diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc index fa2a852..b17bb25 100644 Binary files a/__pycache__/config.cpython-313.pyc and b/__pycache__/config.cpython-313.pyc differ diff --git a/__pycache__/database.cpython-313.pyc b/__pycache__/database.cpython-313.pyc index 8aa832e..59bbb4d 100644 Binary files a/__pycache__/database.cpython-313.pyc and b/__pycache__/database.cpython-313.pyc differ diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index db96cfc..d6128f4 100644 Binary files a/__pycache__/main.cpython-313.pyc and b/__pycache__/main.cpython-313.pyc differ diff --git a/__pycache__/outh.cpython-313.pyc b/__pycache__/outh.cpython-313.pyc index ffc3f81..e251d68 100644 Binary files a/__pycache__/outh.cpython-313.pyc and b/__pycache__/outh.cpython-313.pyc differ diff --git a/__pycache__/postsqldb.cpython-313.pyc b/__pycache__/postsqldb.cpython-313.pyc index 7a7b51c..ab4c94f 100644 Binary files a/__pycache__/postsqldb.cpython-313.pyc and b/__pycache__/postsqldb.cpython-313.pyc differ diff --git a/__pycache__/webpush.cpython-313.pyc b/__pycache__/webpush.cpython-313.pyc index 90a348e..ea72ac0 100644 Binary files a/__pycache__/webpush.cpython-313.pyc and b/__pycache__/webpush.cpython-313.pyc differ diff --git a/application/__pycache__/__init__.cpython-313.pyc b/application/__pycache__/__init__.cpython-313.pyc index ad9bd77..c24605b 100644 Binary files a/application/__pycache__/__init__.cpython-313.pyc and b/application/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/__pycache__/database_payloads.cpython-313.pyc b/application/__pycache__/database_payloads.cpython-313.pyc index 6d92e3d..b3fc156 100644 Binary files a/application/__pycache__/database_payloads.cpython-313.pyc and b/application/__pycache__/database_payloads.cpython-313.pyc differ diff --git a/application/__pycache__/postsqldb.cpython-313.pyc b/application/__pycache__/postsqldb.cpython-313.pyc index 6664be4..e8e03e3 100644 Binary files a/application/__pycache__/postsqldb.cpython-313.pyc and b/application/__pycache__/postsqldb.cpython-313.pyc differ diff --git a/application/access_module/__pycache__/__init__.cpython-313.pyc b/application/access_module/__pycache__/__init__.cpython-313.pyc index f88ef8c..0a227d6 100644 Binary files a/application/access_module/__pycache__/__init__.cpython-313.pyc and b/application/access_module/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/access_module/__pycache__/access_api.cpython-313.pyc b/application/access_module/__pycache__/access_api.cpython-313.pyc index 0fea0f3..2b363bf 100644 Binary files a/application/access_module/__pycache__/access_api.cpython-313.pyc and b/application/access_module/__pycache__/access_api.cpython-313.pyc differ diff --git a/application/access_module/__pycache__/access_database.cpython-313.pyc b/application/access_module/__pycache__/access_database.cpython-313.pyc index bf35a23..01221a2 100644 Binary files a/application/access_module/__pycache__/access_database.cpython-313.pyc and b/application/access_module/__pycache__/access_database.cpython-313.pyc differ diff --git a/application/access_module/access_api.py b/application/access_module/access_api.py index 7fa07db..caee1d0 100644 --- a/application/access_module/access_api.py +++ b/application/access_module/access_api.py @@ -1,27 +1,20 @@ from flask import Blueprint, request, render_template, redirect, session, url_for, jsonify -from authlib.integrations.flask_client import OAuth -import hashlib, psycopg2 +import hashlib from config import config, sites_config from functools import wraps -import postsqldb import requests from application.access_module import access_database +from application.database_postgres.UsersModel import UsersModel from outh import oauth access_api = Blueprint('access_api', __name__, template_folder="templates", static_folder="static") - - - def update_session_user(): - user = access_database.selectLoginsTupleByID((session['user_id'],)) - user = access_database.washUserDictionary(user) + user = UsersModel.select_tuple(session['selected_site'], {'key': session['user_uuid']}) + user = UsersModel.washUserDictionary(user) session['user'] = user - print(user) - - def login_required(func): @wraps(func) def wrapper(*args, **kwargs): @@ -89,20 +82,13 @@ def login(): password = hashlib.sha256(password.encode()).hexdigest() database_config = config() - with psycopg2.connect(**database_config) as conn: - try: - with conn.cursor() as cur: - sql = f"SELECT * FROM logins WHERE username=%s;" - cur.execute(sql, (username,)) - user = cur.fetchone() - except (Exception, psycopg2.DatabaseError) as error: - conn.rollback() - return jsonify({'error': True, 'message': str(error)}) - if user and user[2] == password: - session['user_id'] = user[0] - session['user'] = {'id': user[0], 'username': user[1], 'sites': user[13], 'site_roles': user[14], 'system_admin': user[15], 'flags': user[16]} - session['login_type'] = 'Internal' + user = UsersModel.select_tuple_by_username({'key': username}) + + if user and user['user_password'] == password: + session['user_uuid'] = user['user_uuid'] + session['user'] = UsersModel.washUserDictionary(user) + session['user_login_type'] = 'Internal' return jsonify({'error': False, 'message': 'Logged In Sucessfully!'}) else: return jsonify({'error': True, 'message': 'Username or Password was incorrect!'}) @@ -111,16 +97,8 @@ def login(): if 'user' not in session.keys(): session['user'] = None - print(instance_config) - return render_template("login.html", instance_settings=instance_config) -@access_api.route('/dashboard') -def dashboard(): - if 'user' not in session: - return redirect('/') - return f"Hello, {session['user']['name']}! Logout" - @access_api.route('/signup', methods=['POST', 'GET']) def signup(): instance_config = sites_config() @@ -132,14 +110,14 @@ def signup(): password = request.get_json()['password'] email = request.get_json()['email'] password = hashlib.sha256(password.encode()).hexdigest() - database_config = config() - with psycopg2.connect(**database_config) as conn: - try: - with conn.cursor() as cur: - sql = f"INSERT INTO logins(username, password, email, row_type) VALUES(%s, %s, %s, %s);" - cur.execute(sql, (username, password, email, 'user')) - except (Exception, psycopg2.DatabaseError) as error: - conn.rollback() - return jsonify({'error': True, 'message': str(error)}) + + new_user = UsersModel.Payload( + user_name=username, + user_password=password, + user_email=email + ) + + new_user = UsersModel.insert_tuple('', new_user.payload_dictionary()) + return jsonify({'error': False, 'message': 'You have been signed up successfully, you will have to wait until the server admin finishes your onboarding!'}) return jsonify({'error': True, 'message': 'There was a problem with this POST request!'}) diff --git a/application/administration/__pycache__/__init__.cpython-313.pyc b/application/administration/__pycache__/__init__.cpython-313.pyc index 0d169b4..562367c 100644 Binary files a/application/administration/__pycache__/__init__.cpython-313.pyc and b/application/administration/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/administration/__pycache__/administration_api.cpython-313.pyc b/application/administration/__pycache__/administration_api.cpython-313.pyc index ea0a324..2b9880f 100644 Binary files a/application/administration/__pycache__/administration_api.cpython-313.pyc and b/application/administration/__pycache__/administration_api.cpython-313.pyc differ diff --git a/application/administration/__pycache__/administration_database.cpython-313.pyc b/application/administration/__pycache__/administration_database.cpython-313.pyc index 9f4a8bc..f012cad 100644 Binary files a/application/administration/__pycache__/administration_database.cpython-313.pyc and b/application/administration/__pycache__/administration_database.cpython-313.pyc differ diff --git a/application/administration/__pycache__/administration_models.cpython-313.pyc b/application/administration/__pycache__/administration_models.cpython-313.pyc new file mode 100644 index 0000000..a9a779e Binary files /dev/null and b/application/administration/__pycache__/administration_models.cpython-313.pyc differ diff --git a/application/administration/__pycache__/administration_processes.cpython-313.pyc b/application/administration/__pycache__/administration_processes.cpython-313.pyc index e570609..e10e726 100644 Binary files a/application/administration/__pycache__/administration_processes.cpython-313.pyc and b/application/administration/__pycache__/administration_processes.cpython-313.pyc differ diff --git a/application/administration/__pycache__/administration_services.cpython-313.pyc b/application/administration/__pycache__/administration_services.cpython-313.pyc new file mode 100644 index 0000000..ff6bdcd Binary files /dev/null and b/application/administration/__pycache__/administration_services.cpython-313.pyc differ diff --git a/application/administration/administration_api.py b/application/administration/administration_api.py index e9432d5..3eac558 100644 --- a/application/administration/administration_api.py +++ b/application/administration/administration_api.py @@ -7,7 +7,7 @@ import hashlib # APPLICATION IMPORTS from application.access_module import access_api -from application.administration import administration_database, administration_processes +from application.administration import administration_database, administration_services from application import database_payloads, postsqldb @@ -69,7 +69,7 @@ def first_time_setup(): "site_description": request.form['site_description'] } - administration_processes.addSite(payload) + administration_services.addSite(payload) return redirect("/login") @@ -127,7 +127,7 @@ def postDeleteSite(): return jsonify({'error': True, 'message': f"You must be the owner of this site to delete."}) try: - administration_processes.deleteSite(site, user) + administration_services.deleteSite(site, user) except Exception as err: print(err) @@ -144,7 +144,7 @@ def postAddSite(): user = administration_database.selectLoginsTuple((user_id,)) payload['admin_user'] = (user['username'], user['password'], user['email'], user['row_type']) - administration_processes.addSite(payload) + administration_services.addSite(payload) return jsonify({'error': False, 'message': f"Zone added to {site_name}."}) diff --git a/application/administration/administration_models.py b/application/administration/administration_models.py new file mode 100644 index 0000000..7bfee26 --- /dev/null +++ b/application/administration/administration_models.py @@ -0,0 +1,148 @@ +from application.database_postgres.UsersModel import UsersModel +from application.database_postgres.RolesModel import RolesModel +from application.database_postgres.BaseModel import DatabaseError, tupleDictionaryFactory +import config +import psycopg2 + +class ExtendedRolesModel(RolesModel): + @classmethod + def select_by_site_uuid(self, payload, convert=True, conn=None): + roles = () + self_conn = False + select_roles_sql = f"SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s::uuid;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(select_roles_sql, payload) + rows = cur.fetchall() + if rows and convert: + roles = [tupleDictionaryFactory(cur.description, role) for role in rows] + elif rows and not convert: + roles = rows + + if self_conn: + conn.close() + + return roles + except Exception as error: + raise DatabaseError(error, payload, select_roles_sql) + +class ExtendedUsersModel(UsersModel): + @classmethod + def add_admin_user(self, payload:dict, convert=True, conn=None): + admin_user = () + self_conn = False + sql = f"""INSERT INTO users (user_name, user_password, user_email, user_row_type) + VALUES (%(user_name)s, %(user_password)s, %(user_email)s, %(user_row_type)s) ON CONFLICT (user_name) DO UPDATE SET user_name = excluded.user_name RETURNING *;""" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + admin_user = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + admin_user = rows + + if self_conn: + conn.commit() + conn.close() + + return admin_user + except Exception as e: + DatabaseError(str(e), payload, sql) + + @classmethod + def update_roles(self, payload, convert=True, conn=None): + """ payload: {'role_uuid': x,} """ + self_conn = False + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + select_sql = f"SELECT users.user_uuid FROM users WHERE user_roles @> ARRAY[%(role_uuid)s::uuid];" + with conn.cursor() as cur: + cur.execute(select_sql, payload) + users = tuple([row[0] for row in cur.fetchall()]) + + update_sql = f"UPDATE users SET user_roles = array_remove(user_roles, %(role_uuid)s::uuid) WHERE user_uuid = %(user_uuid)s::uuid;" + with conn.cursor() as cur: + for user_uuid in users: + cur.execute(update_sql, {'role_uuid': payload['role_uuid'], 'user_uuid': user_uuid}) + + if self_conn: + conn.commit() + conn.close() + + except Exception as error: + raise error + + @classmethod + def update_sites(self, payload, convert=True, conn=None): + """ payload: {'site_uuid',} """ + self_conn = False + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + select_sql = f"SELECT users.user_uuid FROM users WHERE user_sites @> ARRAY[%(site_uuid)s::uuid];" + with conn.cursor() as cur: + cur.execute(select_sql, payload) + user = tuple([row[0] for row in cur.fetchall()]) + + update_sql = f"UPDATE users SET user_sites = array_remove(user_sites, %(site_uuid)s::uuid) WHERE user_uuid = %(user_uuid)s::uuid;" + with conn.cursor() as cur: + for user_uuid in user: + cur.execute(update_sql, {'site_uuid': payload['site_uuid'], 'user_uuid': user_uuid}) + + if self_conn: + conn.commit() + conn.close() + + except Exception as error: + raise error + + @classmethod + def update_user_site_roles(self, payload, convert=True, conn=None): + """ payload (tuple): (site_uuid, role_uuid, user_uuid) """ + sql = f"UPDATE users SET user_sites = user_sites || %(site_uuid)s::uuid, user_roles = user_roles || %(role_uuid)s::uuid WHERE user_uuid=%(user_uuid)s RETURNING *;" + user = () + self_conn = False + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + user = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + user = rows + + if self_conn: + conn.commit() + conn.close() + + return user + except Exception as error: + raise DatabaseError(error, payload, sql) \ No newline at end of file diff --git a/application/administration/administration_processes.py b/application/administration/administration_processes.py deleted file mode 100644 index 11e6376..0000000 --- a/application/administration/administration_processes.py +++ /dev/null @@ -1,167 +0,0 @@ -# 3RD PARTY IMPORTS -import psycopg2 -import datetime - -# APPLICATION IMPORTS -import config -from application import postsqldb, database_payloads -from application.administration import administration_database - -def dropSiteTables(conn, site_manager): - try: - for table in site_manager.drop_order: - administration_database.dropTable(site_manager.site_name, table, conn=conn) - with open("logs/process.log", "a+") as file: - file.write(f"{datetime.datetime.now()} --- INFO --- {table} DROPPED!\n") - except Exception as error: - raise error - -def deleteSite(site, user, conn=None): - """Uses a Site Manager to delete a site from the system. - - Args: - site_manager (MyDataclasses.SiteManager): - - Raises: - Exception: - """ - self_conn = False - if not conn: - database_config = config.config() - conn = psycopg2.connect(**database_config) - conn.autocommit = False - self_conn = True - - try: - admin_user = (user['username'], user['password'], user['email'], user['row_type']) - site_manager = database_payloads.SiteManager( - site['site_name'], - admin_user, - site['default_zone'], - site['default_primary_location'], - site['site_description'] - ) - - roles = administration_database.selectRolesTupleBySite((site['id'],), conn=conn) - administration_database.deleteRolesTuple([role['id'] for role in roles], conn=conn) - - dropSiteTables(conn, site_manager) - - for role in roles: - administration_database.updateUsersRoles({'role_id': role['id']}, conn=conn) - - administration_database.updateUsersSites({'site_id': site['id']}, conn=conn) - - site = administration_database.deleteSitesTuple((site['id'], ), conn=conn) - - if self_conn: - conn.commit() - conn.close() - - except Exception as error: - with open("logs/process.log", "a+") as file: - file.write(f"{datetime.datetime.now()} --- ERROR --- {error}\n") - conn.rollback() - conn.close() - -def addAdminUser(conn, site_manager, convert=True): - admin_user = () - try: - sql = f"INSERT INTO logins (username, password, email, row_type) VALUES (%s, %s, %s, %s) ON CONFLICT (username) DO UPDATE SET username = excluded.username RETURNING *;" - with conn.cursor() as cur: - cur.execute(sql, site_manager.admin_user) - rows = cur.fetchone() - if rows and convert: - admin_user = postsqldb.tupleDictionaryFactory(cur.description, rows) - elif rows and not convert: - admin_user = rows - with open("logs/process.log", "a+") as file: - file.write(f"{datetime.datetime.now()} --- INFO --- Admin User Created!\n") - except Exception as error: - raise error - return admin_user - -def setupSiteTables(conn, site_manager): - try: - for table in site_manager.create_order: - administration_database.createTable(site_manager.site_name, table, conn=conn) - with open("logs/process.log", "a+") as file: - file.write(f"{datetime.datetime.now()} --- INFO --- {table} Created!\n") - except Exception as error: - raise error - -def addSite(payload, conn=None): - """uses a Site Manager to add a site to the system - - Args: - site_manager (MyDataclasses.SiteManager): - """ - self_conn = False - site_manager = database_payloads.SiteManager( - payload['site_name'], - payload['admin_user'], - payload['default_zone'], - payload['default_primary_location'], - payload['site_description'] - ) - - try: - - if not conn: - database_config = config.config() - conn = psycopg2.connect(**database_config) - conn.autocommit = False - self_conn = True - - setupSiteTables(conn, site_manager) - - admin_user = addAdminUser(conn, site_manager) - - site = database_payloads.SitePayload( - site_name=site_manager.site_name, - site_description=site_manager.description, - site_owner_id=admin_user['id'] - ) - - site = administration_database.insertSitesTuple(site.payload(), conn=conn) - - role = database_payloads.RolePayload("Admin", f"Admin for {site['site_name']}", site['id']) - role = administration_database.insertRolesTuple(role.payload(), conn=conn) - - admin_user = administration_database.updateAddLoginSitesRoles((site["id"], role["id"], admin_user["id"]), conn=conn) - - default_zone = database_payloads.ZonesPayload(site_manager.default_zone) - default_zone = administration_database.insertZonesTuple(site["site_name"], default_zone.payload(), conn=conn) - uuid = f"{site_manager.default_zone}@{site_manager.default_location}" - - default_location = database_payloads.LocationsPayload(uuid, site_manager.default_location, default_zone['id']) - default_location = administration_database.insertLocationsTuple(site['site_name'], default_location.payload(), conn=conn) - - payload = { - 'id': site['id'], - 'update': { - 'default_zone': default_zone['id'], - 'default_auto_issue_location': default_location['id'], - 'default_primary_location': default_location['id'] - } - } - - administration_database.updateSitesTuple(payload, conn=conn) - - - blank_vendor = database_payloads.VendorsPayload("None", admin_user['id']) - blank_brand = database_payloads.BrandsPayload("None") - - blank_vendor = administration_database.insertVendorsTuple(site['site_name'], blank_vendor.payload(), conn=conn) - blank_brand = administration_database.insertBrandsTuple(site['site_name'], blank_brand.payload(), conn=conn) - - - if self_conn: - conn.commit() - conn.close() - - except Exception as error: - with open("logs/process.log", "a+") as file: - file.write(f"{datetime.datetime.now()} --- ERROR --- {error}\n") - conn.rollback() - raise error \ No newline at end of file diff --git a/application/administration/administration_services.py b/application/administration/administration_services.py new file mode 100644 index 0000000..a176a2a --- /dev/null +++ b/application/administration/administration_services.py @@ -0,0 +1,261 @@ +# 3RD PARTY IMPORTS +import psycopg2 +import datetime + +# APPLICATION IMPORTS +import config +from application import postsqldb, database_payloads +from application.administration import administration_database, administration_models +from dataclasses import dataclass, field + +from application.database_postgres import ( + CostLayersModel, + BrandsModel, + FoodInfoModel, + ItemInfoModel, + ZonesModel, + LocationsModel, + LogisticsInfoModel, + TransactionsModel, + ItemsModel, + ItemLocationsModel, + ConversionsModel, + SKUPrefixModel, + BarcodesModel, + VendorsModel, + ReceiptsModel, + ReceiptItemsModel, + RecipesModel, + RecipeItemsModel, + ShoppingListsModel, + ShoppingListItemsModel, + PlansModel, + PlanEventsModel, + SitesModel, + UsersModel, + RolesModel, + UnitsModel +) + +from application.database_postgres import BaseModel + +@dataclass +class SiteManager: + site_name: str + admin_user: tuple + default_zone: int + default_location: int + description: str + create_order: list = field(init=False) + drop_order: list = field(init=False) + + def create_tables(self, conn): + UsersModel.UsersModel.create_table(self.site_name, conn=conn) + SitesModel.SitesModel.create_table(self.site_name, conn=conn) + RolesModel.RolesModel.create_table(self.site_name, conn=conn) + UnitsModel.UnitsModel.create_table(self.site_name, conn=conn) + + # Needed for Items and Logistics + BrandsModel.BrandsModel.create_table(self.site_name, conn=conn) + ZonesModel.ZonesModel.create_table(self.site_name, conn=conn) + LocationsModel.LocationsModel.create_table(self.site_name, conn=conn) + ItemsModel.ItemsModel.create_table(self.site_name, conn=conn) + FoodInfoModel.FoodInfoModel.create_table(self.site_name, conn=conn) + ItemInfoModel.ItemInfoModel.create_table(self.site_name, conn=conn) + LogisticsInfoModel.LogisticsInfoModel.create_table(self.site_name, conn=conn) + ItemLocationsModel.ItemLocationsModel.create_table(self.site_name, conn=conn) + CostLayersModel.CostLayersModel.create_table(self.site_name, conn=conn) + ConversionsModel.ConversionsModel.create_table(self.site_name, conn=conn) + TransactionsModel.TransactionsModel.create_table(self.site_name, conn=conn) + SKUPrefixModel.SKUPrefixModel.create_table(self.site_name, conn=conn) + BarcodesModel.BarcodesModel.create_table(self.site_name, conn=conn) + + + # Vendors is used losely in Planner and in receipts. + VendorsModel.VendorsModel.create_table(self.site_name, conn=conn) + ReceiptsModel.ReceiptsModel.create_table(self.site_name, conn=conn) + ReceiptItemsModel.ReceiptItemsModel.create_table(self.site_name, conn=conn) + + # This is the Recipe Module + RecipesModel.RecipesModel.create_table(self.site_name, conn=conn) + RecipeItemsModel.RecipeItemsModel.create_table(self.site_name, conn=conn) + + # this is the Shopping List Module + ShoppingListsModel.ShoppingListsModel.create_table(self.site_name, conn=conn) + ShoppingListItemsModel.ShoppingListItemsModel.create_table(self.site_name, conn=conn) + + # Planner Module + PlansModel.PlansModel.create_table(self.site_name, conn=conn) + PlanEventsModel.PlanEventsModel.create_table(self.site_name, conn=conn) + + def drop_tables(self, conn): + # Needed for Items and Logistics + BrandsModel.BrandsModel.drop_table(self.site_name,conn=conn) + CostLayersModel.CostLayersModel.drop_table(self.site_name, conn=conn) + FoodInfoModel.FoodInfoModel.drop_table(self.site_name, conn=conn) + ItemInfoModel.ItemInfoModel.drop_table(self.site_name, conn=conn) + ZonesModel.ZonesModel.drop_table(self.site_name, conn=conn) + LocationsModel.LocationsModel.drop_table(self.site_name, conn=conn) + LogisticsInfoModel.LogisticsInfoModel.drop_table(self.site_name, conn=conn) + TransactionsModel.TransactionsModel.drop_table(self.site_name, conn=conn) + ItemsModel.ItemsModel.drop_table(self.site_name, conn=conn) + ItemLocationsModel.ItemLocationsModel.drop_table(self.site_name, conn=conn) + ConversionsModel.ConversionsModel.drop_table(self.site_name, conn=conn) + SKUPrefixModel.SKUPrefixModel.drop_table(self.site_name, conn=conn) + BarcodesModel.BarcodesModel.drop_table(self.site_name, conn=conn) + + + # Vendors is used losely in Planner and in receipts. + VendorsModel.VendorsModel.drop_table(self.site_name, conn=conn) + ReceiptsModel.ReceiptsModel.drop_table(self.site_name, conn=conn) + ReceiptItemsModel.ReceiptItemsModel.drop_table(self.site_name, conn=conn) + + # This is the Recipe Module + RecipesModel.RecipesModel.drop_table(self.site_name, conn=conn) + RecipeItemsModel.RecipeItemsModel.drop_table(self.site_name, conn=conn) + + # this is the Shopping List Module + ShoppingListsModel.ShoppingListsModel.drop_table(self.site_name, conn=conn) + ShoppingListItemsModel.ShoppingListItemsModel.drop_table(self.site_name, conn=conn) + + # Planner Module + PlansModel.PlansModel.drop_table(self.site_name, conn=conn) + PlanEventsModel.PlanEventsModel.drop_table(self.site_name, conn=conn) + +def deleteSite(payload, conn=None): + """Uses a Site Manager to delete a site from the system. + + Args: + site_manager (MyDataclasses.SiteManager): + + Raises: + Exception: + """ + self_conn = False + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = False + self_conn = True + + + site_manager = SiteManager( + payload['site_name'], + payload['admin_user'], + payload['default_zone'], + payload['default_primary_location'], + payload['site_description'] + ) + + roles = administration_models.ExtendedRolesModel.select_by_site_uuid({'site_uuid': payload['site_uuid']}, conn=conn) + roles = RolesModel.RolesModel.delete_tuples([role['role_uuid'] for role in roles], conn=conn) + + site_manager.drop_tables(conn=conn) + + for role in roles: + administration_models.ExtendedUsersModel.update_roles({'role_uuid': role['role_uuid']}, conn=conn) + + + administration_models.ExtendedUsersModel.update_sites({'site_uuid': payload['site_uuid']}, conn=conn) + + SitesModel.SitesModel.delete_tuples((payload['site_uuid'],), conn=conn) + + if self_conn: + conn.commit() + conn.close() + +def addSite(payload, conn=None): + """uses a Site Manager to add a site to the system + + Args: + site_manager (MyDataclasses.SiteManager): + """ + self_conn = False + site_manager = SiteManager( + payload['site_name'], + payload['admin_user'], + payload['default_zone'], + payload['default_primary_location'], + payload['site_description'] + ) + + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = False + self_conn = True + + sql = 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp";' + with conn.cursor() as cur: + cur.execute(sql) + + site_manager.create_tables(conn=conn) + + + admin_user = administration_models.ExtendedUsersModel.add_admin_user(site_manager.admin_user, conn=conn) + + site = SitesModel.SitesModel.Payload( + site_name=site_manager.site_name, + site_description=site_manager.description, + site_created_by=admin_user['user_uuid'] + ) + + # have to build site table + site = SitesModel.SitesModel.insert_tuple(site.site_name, site.payload_dictionary(), conn=conn) + + # have to build roles table + role = RolesModel.RolesModel.Payload( + role_name="Admin", + role_description=f"Admin for {site['site_name']}", + role_site_uuid=site['site_uuid'] + ) + role = RolesModel.RolesModel.insert_tuple(site['site_name'], role.payload_dictionary(), conn=conn) + + # have to build logins table + payload = { + 'user_uuid': admin_user['user_uuid'], + 'site_uuid': site['site_uuid'], + 'role_uuid': role['role_uuid'] + } + admin_user = administration_models.ExtendedUsersModel.update_user_site_roles(payload, conn=conn) + + default_zone = ZonesModel.ZonesModel.Payload(zone_name=site_manager.default_zone) + default_zone = ZonesModel.ZonesModel.insert_tuple(site["site_name"], default_zone.payload_dictionary(), conn=conn) + uuid = f"{site_manager.default_zone}@{site_manager.default_location}" + + default_location = LocationsModel.LocationsModel.Payload( + location_shortname=uuid, + location_name=site_manager.default_location, + zone_uuid=default_zone['zone_uuid'] + ) + + default_location = LocationsModel.LocationsModel.insert_tuple(site['site_name'], default_location.payload_dictionary(), conn=conn) + + payload = { + 'key': site['site_uuid'], + 'update': { + 'site_default_zone_uuid': default_zone['zone_uuid'], + 'site_default_auto_issue_location_uuid': default_location['location_uuid'], + 'site_default_primary_location_uuid': default_location['location_uuid'] + } + } + + SitesModel.SitesModel.update_tuple(payload, conn=conn) + + + blank_vendor = VendorsModel.VendorsModel.Payload("None", admin_user['user_uuid']) + blank_brand = BrandsModel.BrandsModel.Payload("None") + + VendorsModel.VendorsModel.insert_tuple(site['site_name'], blank_vendor.payload_dictionary(), conn=conn) + BrandsModel.BrandsModel.insert_tuple(site['site_name'], blank_brand.payload_dictionary(), conn=conn) + + if self_conn: + conn.commit() + conn.close() + + except Exception as error: + with open("logs/process.log", "a+") as file: + file.write(f"{datetime.datetime.now()} --- ERROR --- {error}\n") + conn.rollback() + raise error \ No newline at end of file diff --git a/application/administration/templates/site.html b/application/administration/templates/site.html index b4dbf59..7d6a365 100644 --- a/application/administration/templates/site.html +++ b/application/administration/templates/site.html @@ -204,7 +204,7 @@ payload: payload }), }); - location.href = '/administration' + //location.href = '/administration' } async function postEditSite(){ diff --git a/application/database_postgres/BaseModel.py b/application/database_postgres/BaseModel.py index d73028e..7c446c3 100644 --- a/application/database_postgres/BaseModel.py +++ b/application/database_postgres/BaseModel.py @@ -1,5 +1,6 @@ from abc import ABC import psycopg2 +import psycopg2.extras import datetime import uuid import json @@ -9,7 +10,6 @@ from copy import deepcopy import config - def validateUUID(uuid_string, version): try: u = uuid.UUID(uuid_string, version=version) @@ -58,6 +58,10 @@ def getUUID(n): class BasePayload(ABC): """BasePayloads holds the bare minimum methods required of a Payload. """ + + def __repr__(self): + return self.__dict__ + def payload_dictionary(self): return deepcopy(self.__dict__) @@ -76,6 +80,8 @@ class BaseModel(ABC): """ table_name: str = None # All extended class must assign a table name that CRUD uses to call upon primary_key: str = 'id' # All extended class can assign a different primary key/cloumn which is used to call delete and update queries on. + primary_key_type: str = 'int' + site_agnostic: bool = False #all extended class can set this to true to avoid site injection def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) @@ -225,3 +231,69 @@ class BaseModel(ABC): except Exception as error: raise DatabaseError(error, payload, sql) + + @classmethod + def select_tuple(self, site: str, payload: dict, convert: bool = True, conn=None): + record = () + self_conn = False + + if self.site_agnostic: + sql = f"SELECT * FROM {self.table_name} WHERE {self.primary_key} = %(key)s::{self.primary_key_type};" + else: + sql = f"SELECT * FROM {site}_{self.table_name} WHERE {self.primary_key} = %(key)s::{self.primary_key_type};" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + record = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + record = rows + + if self_conn: + conn.commit() + conn.close() + + return record + + except Exception as error: + raise DatabaseError(error, payload, sql) + + @classmethod + def select_tuples(self, site: str, convert: bool = True, conn=None): + records = () + self_conn = False + + if self.site_agnostic: + sql = f"SELECT * FROM {self.table_name};" + else: + sql = f"SELECT * FROM {site}_{self.table_name};" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql) + rows = cur.fetchall() + if rows and convert: + records = [tupleDictionaryFactory(cur.description, row) for row in rows] + elif rows and not convert: + records = rows + + if self_conn: + conn.commit() + conn.close() + + return records + + except Exception as error: + raise DatabaseError(error, {}, sql) diff --git a/application/database_postgres/BrandsModel.py b/application/database_postgres/BrandsModel.py index 4f9b329..d763516 100644 --- a/application/database_postgres/BrandsModel.py +++ b/application/database_postgres/BrandsModel.py @@ -7,5 +7,5 @@ class BrandsModel(BaseModel): @dataclass class Payload(BasePayload): - name: str + brand_name: str \ No newline at end of file diff --git a/application/database_postgres/FoodInfoModel.py b/application/database_postgres/FoodInfoModel.py index f3f6151..2f829c9 100644 --- a/application/database_postgres/FoodInfoModel.py +++ b/application/database_postgres/FoodInfoModel.py @@ -5,20 +5,23 @@ from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2 class FoodInfoModel(BaseModel): table_name = "food_info" + primary_key = "item_uuid" + primary_key_type = "uuid" @dataclass class Payload(BasePayload): - food_groups: list = field(default_factory=list) - ingrediants: list = field(default_factory=list) - nutrients: dict = field(default_factory=dict) - expires: bool = False - default_expiration: float = 0.0 + item_uuid: str + item_food_groups: list = field(default_factory=list) + item_ingredients: list = field(default_factory=list) + item_nutrients: dict = field(default_factory=dict) + item_expires: bool = False + item_default_expiration: float = 0.0 def payload_dictionary(self): - return { - 'food_groups': lst2pgarr(self.food_groups), - 'ingrediants': lst2pgarr(self.ingrediants), - 'nutrients': json.dumps(self.nutrients), - 'expires': self.expires, - 'default_expiration': self.default_expiration - } \ No newline at end of file + payload = super().payload_dictionary() + payload['item_food_groups'] = lst2pgarr(self.item_food_groups) + payload['item_ingredients'] = lst2pgarr(self.item_ingredients) + payload['item_nutrients'] = json.dumps(self.item_nutrients) + return payload + + \ No newline at end of file diff --git a/application/database_postgres/ItemInfoModel.py b/application/database_postgres/ItemInfoModel.py index d7fb0f4..2781f8e 100644 --- a/application/database_postgres/ItemInfoModel.py +++ b/application/database_postgres/ItemInfoModel.py @@ -5,20 +5,22 @@ from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2 class ItemInfoModel(BaseModel): table_name = "item_info" + primary_key = "item_uuid" + primary_key_type = "uuid" @dataclass class Payload(BasePayload): - barcode: str - packaging: str = "" - uom_quantity: float = 1.0 - uom: int = 1 - cost: float = 0.0 - safety_stock: float = 0.0 - lead_time_days: float = 0.0 - ai_pick: bool = False - prefixes: list = field(default_factory=list) + item_uuid: str + item_uom: str = None + item_packaging: str = "" + item_uom_quantity: float = 1.0 + item_cost: float = 0.0 + item_safety_stock: float = 0.0 + item_lead_time_days: float = 0.0 + item_ai_pick: bool = False + item_prefixes: list = field(default_factory=list) def payload_dictionary(self): payload = super().payload_dictionary() - payload['prefixes'] = lst2pgarr(self.prefixes) + payload['item_prefixes'] = lst2pgarr(self.item_prefixes) return payload \ No newline at end of file diff --git a/application/database_postgres/ItemLocationsModel.py b/application/database_postgres/ItemLocationsModel.py index 0e26ea5..3d023df 100644 --- a/application/database_postgres/ItemLocationsModel.py +++ b/application/database_postgres/ItemLocationsModel.py @@ -3,16 +3,12 @@ from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2 class ItemLocationsModel(BaseModel): table_name = "item_locations" + primary_key = "item_location_uuid" + primary_key_type = 'uuid' @dataclass class Payload(BasePayload): - part_id: int - location_id: int - quantity_on_hand: float = 0.0 - cost_layers: list = field(default_factory=list) - - def payload_dictionary(self): - payload = super().payload_dictionary() - payload['cost_layers'] = lst2pgarr(self.cost_layers) - return payload + item_uuid: str + location_uuid: str + item_quantity_on_hand: float = 0.0 \ No newline at end of file diff --git a/application/database_postgres/ItemsModel.py b/application/database_postgres/ItemsModel.py index 545c175..337ecbe 100644 --- a/application/database_postgres/ItemsModel.py +++ b/application/database_postgres/ItemsModel.py @@ -1,31 +1,106 @@ from dataclasses import dataclass, field import json +import psycopg2 +import datetime -from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2pgarr - +from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2pgarr, DatabaseError, tupleDictionaryFactory +import config class ItemsModel(BaseModel): table_name = "items" + primary_key = "item_uuid" + primary_key_type = "uuid" @dataclass class Payload(BasePayload): - item_info_id: int - item_info_uuid: str - logistics_info_id: int - logistics_info_uuid: str - food_info_id: int - food_info_uuid: str - barcode: str = "" - item_name: str = "" - brand: int = 0 - description: str = "" - tags: list = field(default_factory=list) - links: dict = field(default_factory=dict) - row_type: str = "" - item_type: str = "" - search_string: str ="" + item_category: str + item_name: str + item_created_at: datetime.datetime = field(init=False) + item_updated_at: datetime.datetime = field(init=False) + item_description: str = "" + item_tags: list = field(default_factory=list) + item_links: dict = field(default_factory=dict) + item_brand_uuid: str = None + item_search_string: str = "" + item_inactive: bool = False + + def __post_init__(self): + self.item_created_at = datetime.datetime.now() + self.item_updated_at = datetime.datetime.now() def payload_dictionary(self): payload = super().payload_dictionary() - payload['tags'] = lst2pgarr(self.tags) - payload['links'] = json.dumps(self.links) - return payload \ No newline at end of file + payload['item_tags'] = lst2pgarr(self.item_tags) + payload['item_links'] = json.dumps(self.item_links) + return payload + + @classmethod + def paginate_items_with_qoh(self, site:str, payload: dict, convert: bool=True, conn = None): + recordset = () + count = 0 + self_conn = False + with open('application/database_postgres/sql/ItemsModel/paginateItemsWithQOH.sql', 'r+') as file: + sql = file.read().replace("%%site_name%%", site).replace("%%sort_order%%", payload['sort_order']) + sql_count = f"SELECT COUNT(*) FROM {site}_{self.table_name} items WHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%';" + recordset = () + count = 0 + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + recordset = [tupleDictionaryFactory(cur.description, row) for row in rows] + if rows and not convert: + recordset = rows + + cur.execute(sql_count, payload) + count = cur.fetchone()[0] + + if self_conn: + conn.close() + + return recordset, count + + except Exception as error: + raise DatabaseError(error, payload, sql) + + @classmethod + def paginate_items_for_modal(self, site:str, payload: dict, convert: bool=True, conn = None): + recordset = () + count = 0 + self_conn = False + with open('application/database_postgres/sql/ItemsModel/paginateItemsForModal.sql', 'r+') as file: + sql = file.read().replace("%%site_name%%", site).replace("%%sort_order%%", payload['sort_order']) + sql_count = f"SELECT COUNT(*) FROM {site}_{self.table_name} items WHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%';" + recordset = () + count = 0 + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + recordset = [tupleDictionaryFactory(cur.description, row) for row in rows] + if rows and not convert: + recordset = rows + + cur.execute(sql_count, payload) + count = cur.fetchone()[0] + + if self_conn: + conn.close() + + return recordset, count + + except Exception as error: + raise DatabaseError(error, payload, sql) \ No newline at end of file diff --git a/application/database_postgres/LocationsModel.py b/application/database_postgres/LocationsModel.py index 54f3efe..fa2db25 100644 --- a/application/database_postgres/LocationsModel.py +++ b/application/database_postgres/LocationsModel.py @@ -7,7 +7,8 @@ class LocationsModel(BaseModel): @dataclass class Payload(BasePayload): - uuid: str - name: str - zone_id: int + location_shortname: str + location_name: str + zone_uuid: str + \ No newline at end of file diff --git a/application/database_postgres/LogisticsInfoModel.py b/application/database_postgres/LogisticsInfoModel.py index f7796b9..9678425 100644 --- a/application/database_postgres/LogisticsInfoModel.py +++ b/application/database_postgres/LogisticsInfoModel.py @@ -4,12 +4,14 @@ from application.database_postgres.BaseModel import BasePayload, BaseModel class LogisticsInfoModel(BaseModel): table_name = "logistics_info" + primary_key_type = "uuid" + primary_key = "item_uuid" @dataclass class Payload(BasePayload): - barcode: str - primary_location: int - primary_zone: int - auto_issue_location: int - auto_issue_zone: int + item_uuid: str + item_primary_location: str = None + item_primary_zone: str = None + item_auto_issue_location: str = None + item_auto_issue_zone: str = None \ No newline at end of file diff --git a/application/database_postgres/RolesModel.py b/application/database_postgres/RolesModel.py new file mode 100644 index 0000000..dcef71b --- /dev/null +++ b/application/database_postgres/RolesModel.py @@ -0,0 +1,50 @@ +from dataclasses import dataclass, field +import json +import config +import psycopg2 + +from application.database_postgres.BaseModel import BasePayload, BaseModel, DatabaseError, tupleDictionaryFactory + +class RolesModel(BaseModel): + table_name = "roles" + primary_key = "role_uuid" + + @dataclass + class Payload(BasePayload): + role_name: str + role_description: str + role_site_uuid: str + role_flags: dict = field(default_factory=dict) + + def payload_dictionary(self): + payload = super().payload_dictionary() + payload['role_flags'] = json.dumps(self.role_flags) + return payload + + @classmethod + def delete_tuples(self, payload: tuple, convert: bool = True, conn=None): + deleted = () + self_conn = False + sql = f"WITH deleted_rows AS (DELETE FROM {self.table_name} WHERE {self.primary_key} IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + deleted = [tupleDictionaryFactory(cur.description, r) for r in rows] + elif rows and not convert: + deleted = rows + + if self_conn: + conn.commit() + conn.close() + + return deleted + except Exception as error: + raise DatabaseError(error, payload, sql) \ No newline at end of file diff --git a/application/database_postgres/SitesModel.py b/application/database_postgres/SitesModel.py new file mode 100644 index 0000000..4a9db09 --- /dev/null +++ b/application/database_postgres/SitesModel.py @@ -0,0 +1,121 @@ +from dataclasses import dataclass, field +import json +import datetime +import psycopg2 + +from application.database_postgres.BaseModel import ( + BasePayload, BaseModel, tupleDictionaryFactory, DatabaseError, updateStringFactory + ) +import config + +class SitesModel(BaseModel): + table_name = "sites" + primary_key = "site_uuid" + primary_key_type = "uuid" + site_agnostic = True + + @dataclass + class Payload(BasePayload): + site_name: str + site_description: str + site_created_by: str + site_default_zone_uuid: str = None + site_default_auto_issue_location_uuid: str = None + site_default_primary_location_uuid: str = None + site_created_on: datetime.datetime = field(init=False) + site_flags: dict = field(default_factory=dict) + + def __post_init__(self): + self.site_created_on = datetime.datetime.now() + + def payload_dictionary(self): + payload = super().payload_dictionary() + payload['site_flags'] = json.dumps(self.site_flags) + return payload + + @classmethod + def delete_tuples(self, payload: tuple, convert: bool = True, conn=None): + deleted = () + self_conn = False + sql = f"WITH deleted_rows AS (DELETE FROM {self.table_name} WHERE {self.primary_key} IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + deleted = [tupleDictionaryFactory(cur.description, r) for r in rows] + elif rows and not convert: + deleted = rows + + if self_conn: + conn.commit() + conn.close() + + return deleted + except Exception as error: + raise DatabaseError(error, payload, sql) + + @classmethod + def update_tuple(self, payload: dict, convert=True, conn=None): + """ payload (dict): {'key': row_id, 'update': {... column_to_update: value_to_update_to...}} """ + updated = () + self_conn = False + set_clause, values = updateStringFactory(payload['update']) + values.append(payload['key']) + sql = f"UPDATE {self.table_name} SET {set_clause} WHERE {self.primary_key}=%s RETURNING *;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = False + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, values) + rows = cur.fetchone() + if rows and convert: + updated = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + updated = rows + + if self_conn: + conn.commit() + conn.close() + + return updated + except Exception as error: + raise DatabaseError(error, payload, sql) + + @classmethod + def select_all(self, payload: dict, convert=True, conn=None): + record = () + self_conn = False + sql = f"SELECT * FROM {self.table_name} WHERE {self.primary_key}=%(key)s;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = False + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + record = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + record = rows + + if self_conn: + conn.commit() + conn.close() + + return record + except Exception as error: + raise DatabaseError(error, payload, sql) \ No newline at end of file diff --git a/application/database_postgres/TransactionsModel.py b/application/database_postgres/TransactionsModel.py index 932ba1e..aa5bd9e 100644 --- a/application/database_postgres/TransactionsModel.py +++ b/application/database_postgres/TransactionsModel.py @@ -6,20 +6,25 @@ from application.database_postgres.BaseModel import BasePayload, BaseModel class TransactionsModel(BaseModel): table_name = "transactions" + primary_key = "item_uuid" + primary_key_type = "uuid" @dataclass class Payload(BasePayload): - timestamp: datetime.datetime - logistics_info_id: int - barcode: str - name: str + item_uuid: str + transaction_created_by: str + transaction_name: str transaction_type: str - quantity: float - description: str - user_id: int - data: dict = field(default_factory=dict) + transaction_created_at: datetime.datetime = field(init=False) + transaction_quantity: float = 0.00 + transaction_description: str = '' + transaction_cost: float = 0.00 + transaction_data: dict = field(default_factory=dict) + + def __post_init__(self): + self.transaction_created_at = datetime.datetime.now() def payload_dictionary(self): payload = super().payload_dictionary() - payload['data'] = json.dumps(self.data) + payload['transaction_data'] = json.dumps(self.transaction_data) return payload \ No newline at end of file diff --git a/application/database_postgres/UnitsModel.py b/application/database_postgres/UnitsModel.py new file mode 100644 index 0000000..ad52c57 --- /dev/null +++ b/application/database_postgres/UnitsModel.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass, field + +from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2pgarr, tupleDictionaryFactory, DatabaseError + +class UnitsModel(BaseModel): + table_name = "units" + primary_key = "units_uuid" + primary_key_type = "uuid" + site_agnostic = True + + @dataclass + class Payload(BasePayload): + unit_plural:str + unit_single:str + unit_fullname: str + unit_description: str + \ No newline at end of file diff --git a/application/database_postgres/UsersModel.py b/application/database_postgres/UsersModel.py new file mode 100644 index 0000000..4aa8e36 --- /dev/null +++ b/application/database_postgres/UsersModel.py @@ -0,0 +1,82 @@ +from dataclasses import dataclass, field +import json +import datetime +import psycopg2 + +from application.database_postgres.BaseModel import BasePayload, BaseModel, lst2pgarr, tupleDictionaryFactory, DatabaseError +import config + +class UsersModel(BaseModel): + table_name = "users" + primary_key = "user_uuid" + primary_key_type = "uuid" + site_agnostic = True + + @dataclass + class Payload(BasePayload): + user_name:str + user_password:str + user_email: str + user_flags: dict = field(default_factory=dict) + user_favorites: dict = field(default_factory=dict) + user_sites: list = field(default_factory=list) + user_roles: list = field(default_factory=list) + user_is_system_admin: bool = False + user_row_type: str = "user" + user_profile_pic_url: str = "" + user_login_type: str = "Internal" + user_joined_on: datetime.datetime = field(init=False) + + def __post_init__(self): + self.creation_date = datetime.datetime.now() + + def payload_dictionary(self): + payload = super().payload_dictionary() + payload['user_flags'] = json.dumps(self.user_flags) + payload['user_favorites'] = json.dumps(self.user_favorites) + payload['user_sites'] = lst2pgarr(self.user_sites) + payload['user_roles'] = lst2pgarr(self.user_roles) + return payload + + @staticmethod + def washUserDictionary(user): + return { + 'user_uuid': user['user_uuid'], + 'user_name': user['user_name'], + 'user_sites': user['user_sites'], + 'user_roles': user['user_roles'], + 'user_is_system_admin': user['user_is_system_admin'], + 'user_flags': user['user_flags'], + 'user_profile_pic_url': user['user_profile_pic_url'], + 'user_login_type': user['user_login_type'] + } + + @classmethod + def select_tuple_by_username(self, payload: dict, convert: bool = True, conn=None): + record = () + self_conn = False + sql = f"SELECT * FROM {self.table_name} WHERE user_name = %(key)s" + + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + record = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + record = rows + + if self_conn: + conn.commit() + conn.close() + + return record + + except Exception as error: + raise DatabaseError(error, payload, sql) \ No newline at end of file diff --git a/application/database_postgres/VendorsModel.py b/application/database_postgres/VendorsModel.py index 415f4f8..e244641 100644 --- a/application/database_postgres/VendorsModel.py +++ b/application/database_postgres/VendorsModel.py @@ -9,10 +9,10 @@ class VendorsModel(BaseModel): @dataclass class Payload(BasePayload): vendor_name: str - created_by: int + vendor_created_by: str vendor_address: str = "" - creation_date: datetime.datetime = field(init=False) - phone_number: str = "" + vendor_creation_date: datetime.datetime = field(init=False) + vendor_phone_number: str = "" def __post_init__(self): - self.creation_date = datetime.datetime.now() \ No newline at end of file + self.vendor_creation_date = datetime.datetime.now() \ No newline at end of file diff --git a/application/database_postgres/ZonesModel.py b/application/database_postgres/ZonesModel.py index a9db6ab..efef9d1 100644 --- a/application/database_postgres/ZonesModel.py +++ b/application/database_postgres/ZonesModel.py @@ -7,6 +7,6 @@ class ZonesModel(BaseModel): @dataclass class Payload(BasePayload): - name: str - description: str = "" + zone_name: str + zone_description: str = "" \ No newline at end of file diff --git a/application/database_postgres/__pycache__/BarcodesModel.cpython-313.pyc b/application/database_postgres/__pycache__/BarcodesModel.cpython-313.pyc index da0f144..b2a385c 100644 Binary files a/application/database_postgres/__pycache__/BarcodesModel.cpython-313.pyc and b/application/database_postgres/__pycache__/BarcodesModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/BaseModel.cpython-313.pyc b/application/database_postgres/__pycache__/BaseModel.cpython-313.pyc index 9378289..611c1b8 100644 Binary files a/application/database_postgres/__pycache__/BaseModel.cpython-313.pyc and b/application/database_postgres/__pycache__/BaseModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/BrandsModel.cpython-313.pyc b/application/database_postgres/__pycache__/BrandsModel.cpython-313.pyc index d9d87cb..64cfc88 100644 Binary files a/application/database_postgres/__pycache__/BrandsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/BrandsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ConversionsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ConversionsModel.cpython-313.pyc index 119103d..088ad86 100644 Binary files a/application/database_postgres/__pycache__/ConversionsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ConversionsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/CostLayersModel.cpython-313.pyc b/application/database_postgres/__pycache__/CostLayersModel.cpython-313.pyc index baaeffd..f2fd2c5 100644 Binary files a/application/database_postgres/__pycache__/CostLayersModel.cpython-313.pyc and b/application/database_postgres/__pycache__/CostLayersModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/FoodInfoModel.cpython-313.pyc b/application/database_postgres/__pycache__/FoodInfoModel.cpython-313.pyc index 314415b..18a72bf 100644 Binary files a/application/database_postgres/__pycache__/FoodInfoModel.cpython-313.pyc and b/application/database_postgres/__pycache__/FoodInfoModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ItemInfoModel.cpython-313.pyc b/application/database_postgres/__pycache__/ItemInfoModel.cpython-313.pyc index caa025c..c29b428 100644 Binary files a/application/database_postgres/__pycache__/ItemInfoModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ItemInfoModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ItemLocationsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ItemLocationsModel.cpython-313.pyc index cf9eda2..ef0d38f 100644 Binary files a/application/database_postgres/__pycache__/ItemLocationsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ItemLocationsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ItemsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ItemsModel.cpython-313.pyc index 94ed310..c3ed4ec 100644 Binary files a/application/database_postgres/__pycache__/ItemsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ItemsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/LocationsModel.cpython-313.pyc b/application/database_postgres/__pycache__/LocationsModel.cpython-313.pyc index 894d7f0..77b5a83 100644 Binary files a/application/database_postgres/__pycache__/LocationsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/LocationsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/LogisticsInfoModel.cpython-313.pyc b/application/database_postgres/__pycache__/LogisticsInfoModel.cpython-313.pyc index 12c0707..b34a507 100644 Binary files a/application/database_postgres/__pycache__/LogisticsInfoModel.cpython-313.pyc and b/application/database_postgres/__pycache__/LogisticsInfoModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/PlanEventsModel.cpython-313.pyc b/application/database_postgres/__pycache__/PlanEventsModel.cpython-313.pyc index 8eaf5a1..725ae20 100644 Binary files a/application/database_postgres/__pycache__/PlanEventsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/PlanEventsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/PlansModel.cpython-313.pyc b/application/database_postgres/__pycache__/PlansModel.cpython-313.pyc index c031a55..3aad5a3 100644 Binary files a/application/database_postgres/__pycache__/PlansModel.cpython-313.pyc and b/application/database_postgres/__pycache__/PlansModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ReceiptItemsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ReceiptItemsModel.cpython-313.pyc index 7d45c21..79b7f8e 100644 Binary files a/application/database_postgres/__pycache__/ReceiptItemsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ReceiptItemsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ReceiptsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ReceiptsModel.cpython-313.pyc index 4226b07..4298a33 100644 Binary files a/application/database_postgres/__pycache__/ReceiptsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ReceiptsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/RecipeItemsModel.cpython-313.pyc b/application/database_postgres/__pycache__/RecipeItemsModel.cpython-313.pyc index 540ace7..b4c5087 100644 Binary files a/application/database_postgres/__pycache__/RecipeItemsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/RecipeItemsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/RecipesModel.cpython-313.pyc b/application/database_postgres/__pycache__/RecipesModel.cpython-313.pyc index 1853814..8301442 100644 Binary files a/application/database_postgres/__pycache__/RecipesModel.cpython-313.pyc and b/application/database_postgres/__pycache__/RecipesModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/RolesModel.cpython-313.pyc b/application/database_postgres/__pycache__/RolesModel.cpython-313.pyc new file mode 100644 index 0000000..1daa3f8 Binary files /dev/null and b/application/database_postgres/__pycache__/RolesModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/SKUPrefixModel.cpython-313.pyc b/application/database_postgres/__pycache__/SKUPrefixModel.cpython-313.pyc index 804d5bb..b57ed05 100644 Binary files a/application/database_postgres/__pycache__/SKUPrefixModel.cpython-313.pyc and b/application/database_postgres/__pycache__/SKUPrefixModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ShoppingListItemsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ShoppingListItemsModel.cpython-313.pyc index 4f5465a..5511503 100644 Binary files a/application/database_postgres/__pycache__/ShoppingListItemsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ShoppingListItemsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ShoppingListsModel.cpython-313.pyc b/application/database_postgres/__pycache__/ShoppingListsModel.cpython-313.pyc index d110b25..bbebb7c 100644 Binary files a/application/database_postgres/__pycache__/ShoppingListsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ShoppingListsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/SitesModel.cpython-313.pyc b/application/database_postgres/__pycache__/SitesModel.cpython-313.pyc new file mode 100644 index 0000000..839751c Binary files /dev/null and b/application/database_postgres/__pycache__/SitesModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/TransactionsModel.cpython-313.pyc b/application/database_postgres/__pycache__/TransactionsModel.cpython-313.pyc index 292dd72..14fc344 100644 Binary files a/application/database_postgres/__pycache__/TransactionsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/TransactionsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/UnitsModel.cpython-313.pyc b/application/database_postgres/__pycache__/UnitsModel.cpython-313.pyc new file mode 100644 index 0000000..83e8cc1 Binary files /dev/null and b/application/database_postgres/__pycache__/UnitsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/UsersModel.cpython-313.pyc b/application/database_postgres/__pycache__/UsersModel.cpython-313.pyc new file mode 100644 index 0000000..93ee206 Binary files /dev/null and b/application/database_postgres/__pycache__/UsersModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/VendorsModel.cpython-313.pyc b/application/database_postgres/__pycache__/VendorsModel.cpython-313.pyc index e450639..be15c05 100644 Binary files a/application/database_postgres/__pycache__/VendorsModel.cpython-313.pyc and b/application/database_postgres/__pycache__/VendorsModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/ZonesModel.cpython-313.pyc b/application/database_postgres/__pycache__/ZonesModel.cpython-313.pyc index 49db8ea..79ff6e8 100644 Binary files a/application/database_postgres/__pycache__/ZonesModel.cpython-313.pyc and b/application/database_postgres/__pycache__/ZonesModel.cpython-313.pyc differ diff --git a/application/database_postgres/__pycache__/__init__.cpython-313.pyc b/application/database_postgres/__pycache__/__init__.cpython-313.pyc index bb8b1a5..007c9f4 100644 Binary files a/application/database_postgres/__pycache__/__init__.cpython-313.pyc and b/application/database_postgres/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/database_postgres/sql/CREATE/cost_layers.sql b/application/database_postgres/sql/CREATE/cost_layers.sql index 8c5549d..566c74a 100644 --- a/application/database_postgres/sql/CREATE/cost_layers.sql +++ b/application/database_postgres/sql/CREATE/cost_layers.sql @@ -5,5 +5,5 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_cost_layers ( layer_cost FLOAT8 DEFAULT 0.00 NOT NULL, layer_currency_type VARCHAR(16) DEFAULT 'USD' NOT NULL, layer_expires TIMESTAMP DEFAULT NULL, - layer_vendor INTEGER DEFAULT 0 NOT NULL + layer_vendor UUID DEFAULT NULL ); \ No newline at end of file diff --git a/application/database_postgres/sql/CREATE/item_info.sql b/application/database_postgres/sql/CREATE/item_info.sql index bb56e38..02f9fe2 100644 --- a/application/database_postgres/sql/CREATE/item_info.sql +++ b/application/database_postgres/sql/CREATE/item_info.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_item_info ( item_uuid UUID PRIMARY KEY REFERENCES %%site_name%%_items(item_uuid) ON DELETE CASCADE, - item_uom INTEGER NOT NULL, + item_uom UUID DEFAULT NULL REFERENCES units(unit_uuid) ON DELETE SET NULL, item_packaging VARCHAR(255) DEFAULT '' NOT NULL, item_uom_quantity FLOAT8 DEFAULT 0.00 NOT NULL, item_cost FLOAT8 DEFAULT 0.00 NOT NULL, diff --git a/application/database_postgres/sql/CREATE/items.sql b/application/database_postgres/sql/CREATE/items.sql index 38efd9a..be5f34f 100644 --- a/application/database_postgres/sql/CREATE/items.sql +++ b/application/database_postgres/sql/CREATE/items.sql @@ -10,8 +10,5 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_items( item_category VARCHAR(255) NOT NULL, item_search_string TEXT DEFAULT '' NOT NULL, item_inactive BOOLEAN DEFAULT false NOT NULL, - CONSTRAINT fk_brand - FOREIGN KEY(item_brand_uuid) - REFERENCES %%site_name%%_brands(brand_uuid) - ON DELETE SET NULL + UNIQUE(item_name) ); diff --git a/application/database_postgres/sql/CREATE/roles.sql b/application/database_postgres/sql/CREATE/roles.sql new file mode 100644 index 0000000..66444a1 --- /dev/null +++ b/application/database_postgres/sql/CREATE/roles.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS roles( + role_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + role_name VARCHAR(255) NOT NULL, + role_description TEXT DEFAULT '' NOT NULL, + role_site_uuid UUID REFERENCES sites(site_uuid) ON DELETE CASCADE NOT NULL, + role_flags JSONB DEFAULT '{}' NOT NULL, + UNIQUE(role_name, role_site_uuid) +); \ No newline at end of file diff --git a/application/database_postgres/sql/CREATE/sites.sql b/application/database_postgres/sql/CREATE/sites.sql new file mode 100644 index 0000000..5014481 --- /dev/null +++ b/application/database_postgres/sql/CREATE/sites.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS sites ( + site_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + site_name VARCHAR(120) NOT NULL, + site_description TEXT DEFAULT '' NOT NULL, + site_created_on TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + site_created_by UUID REFERENCES users(user_uuid) ON DELETE SET NULL, + site_flags JSONB DEFAULT '{}' NOT NULL, + site_default_zone_uuid UUID DEFAULT NULL, + site_default_auto_issue_location_uuid UUID DEFAULT NULL, + site_default_primary_location_uuid UUID DEFAULT NULL, + UNIQUE(site_name) +); \ No newline at end of file diff --git a/application/database_postgres/sql/CREATE/transactions.sql b/application/database_postgres/sql/CREATE/transactions.sql index 95f404f..a351004 100644 --- a/application/database_postgres/sql/CREATE/transactions.sql +++ b/application/database_postgres/sql/CREATE/transactions.sql @@ -6,6 +6,6 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_transactions ( transaction_quantity FLOAT8 DEFAULT 0.00 NOT NULL, transaction_description TEXT DEFAULT '' NOT NULL, transaction_cost FLOAT8 DEFAULT 0.00 NOT NULL, - transaction_created_by INTEGER NOT NULL, + transaction_created_by UUID DEFAULT NULL REFERENCES users(user_uuid) ON DELETE SET NULL, transaction_data JSONB DEFAULT '{}' NOT NULL ); \ No newline at end of file diff --git a/application/database_postgres/sql/CREATE/units.sql b/application/database_postgres/sql/CREATE/units.sql new file mode 100644 index 0000000..27ec669 --- /dev/null +++ b/application/database_postgres/sql/CREATE/units.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS units ( + unit_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + unit_plural VARCHAR(32) NOT NULL, + unit_single VARCHAR(32) NOT NULL, + unit_fullname VARCHAR(255) NOT NULL, + unit_description TEXT DEFAULT '' NOT NULL, + unique(unit_plural), + unique(unit_single), + unique(unit_fullname) +); \ No newline at end of file diff --git a/application/database_postgres/sql/CREATE/users.sql b/application/database_postgres/sql/CREATE/users.sql new file mode 100644 index 0000000..a5780e0 --- /dev/null +++ b/application/database_postgres/sql/CREATE/users.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites UUID [] DEFAULT '{}' NOT NULL, + user_roles UUID [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) DEFAULT 'user' NOT NULL, + user_profile_pic_url VARCHAR(255) DEFAULT '' NOT NULL, + user_login_type VARCHAR(32) DEFAULT 'Internal' NOT NULL, + UNIQUE(user_name), + CHECK (user_email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + diff --git a/application/database_postgres/sql/CREATE/vendors.sql b/application/database_postgres/sql/CREATE/vendors.sql index 3e42781..22831a7 100644 --- a/application/database_postgres/sql/CREATE/vendors.sql +++ b/application/database_postgres/sql/CREATE/vendors.sql @@ -4,6 +4,6 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_vendors ( vendor_address VARCHAR(255) DEFAULT '' NOT NULL, vendor_phone_number VARCHAR(32) DEFAULT '' NOT NULL, vendor_creation_date TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, - vendor_created_by INTEGER NOT NULL, + vendor_created_by UUID NOT NULL, UNIQUE(vendor_name, vendor_address, vendor_phone_number) ); \ No newline at end of file diff --git a/application/database_postgres/sql/DROP/units.sql b/application/database_postgres/sql/DROP/units.sql new file mode 100644 index 0000000..13e5997 --- /dev/null +++ b/application/database_postgres/sql/DROP/units.sql @@ -0,0 +1 @@ +DROP TABLE units CASCADE; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/brands.sql b/application/database_postgres/sql/INSERT/brands.sql index f107386..873bd8c 100644 --- a/application/database_postgres/sql/INSERT/brands.sql +++ b/application/database_postgres/sql/INSERT/brands.sql @@ -1,4 +1,4 @@ INSERT INTO %%site_name%%_brands -(name) -VALUES (%(name)s) +(brand_name) +VALUES (%(brand_name)s) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/food_info.sql b/application/database_postgres/sql/INSERT/food_info.sql index 0258ae4..6fc514c 100644 --- a/application/database_postgres/sql/INSERT/food_info.sql +++ b/application/database_postgres/sql/INSERT/food_info.sql @@ -1,4 +1,18 @@ INSERT INTO %%site_name%%_food_info -(ingrediants, food_groups, nutrients, expires, default_expiration) -VALUES (%(ingrediants)s, %(food_groups)s, %(nutrients)s, %(expires)s, %(default_expiration)s) +( + item_uuid, + item_food_groups, + item_ingredients, + item_nutrients, + item_expires, + item_default_expiration +) +VALUES ( + %(item_uuid)s, + %(item_food_groups)s, + %(item_ingredients)s, + %(item_nutrients)s, + %(item_expires)s, + %(item_default_expiration)s +) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/item_info.sql b/application/database_postgres/sql/INSERT/item_info.sql index 83ecc49..71ed7fa 100644 --- a/application/database_postgres/sql/INSERT/item_info.sql +++ b/application/database_postgres/sql/INSERT/item_info.sql @@ -1,4 +1,24 @@ INSERT INTO %%site_name%%_item_info -(barcode, packaging, uom_quantity, uom, cost, safety_stock, lead_time_days, ai_pick, prefixes) -VALUES (%(barcode)s, %(packaging)s, %(uom_quantity)s, %(uom)s, %(cost)s, %(safety_stock)s, %(lead_time_days)s, %(ai_pick)s, %(prefixes)s) +( + item_uuid, + item_uom, + item_packaging, + item_uom_quantity, + item_cost, + item_safety_stock, + item_lead_time_days, + item_ai_pick, + item_prefixes + ) +VALUES( + %(item_uuid)s, + %(item_uom)s, + %(item_packaging)s, + %(item_uom_quantity)s, + %(item_cost)s, + %(item_safety_stock)s, + %(item_lead_time_days)s, + %(item_ai_pick)s, + %(item_prefixes)s +) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/item_locations.sql b/application/database_postgres/sql/INSERT/item_locations.sql index b020ce2..f5b5fe8 100644 --- a/application/database_postgres/sql/INSERT/item_locations.sql +++ b/application/database_postgres/sql/INSERT/item_locations.sql @@ -1,4 +1,4 @@ INSERT INTO %%site_name%%_item_locations -(part_id, location_id, quantity_on_hand, cost_layers) -VALUES (%(part_id)s, %(location_id)s, %(quantity_on_hand)s, %(cost_layers)s) +(item_uuid, location_uuid, item_quantity_on_hand) +VALUES (%(item_uuid)s, %(location_uuid)s, %(item_quantity_on_hand)s) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/items.sql b/application/database_postgres/sql/INSERT/items.sql index ef69cc3..077b445 100644 --- a/application/database_postgres/sql/INSERT/items.sql +++ b/application/database_postgres/sql/INSERT/items.sql @@ -1,7 +1,27 @@ INSERT INTO %%site_name%%_items -(barcode, item_name, brand, description, tags, links, item_info_id, item_info_uuid, -logistics_info_id, logistics_info_uuid, food_info_id, food_info_uuid, row_type, item_type, search_string) -VALUES(%(barcode)s, %(item_name)s, %(brand)s, %(description)s, %(tags)s, %(links)s, %(item_info_id)s, %(item_info_uuid)s, -%(logistics_info_id)s, %(logistics_info_uuid)s, %(food_info_id)s, %(food_info_uuid)s, -%(row_type)s, %(item_type)s, %(search_string)s) +( + item_category, + item_name, + item_created_at, + item_updated_at, + item_description, + item_tags, + item_links, + item_brand_uuid, + item_search_string, + item_inactive + + ) +VALUES( + %(item_category)s, + %(item_name)s, + %(item_created_at)s, + %(item_updated_at)s, + %(item_description)s, + %(item_tags)s, + %(item_links)s, + %(item_brand_uuid)s, + %(item_search_string)s, + %(item_inactive)s +) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/locations.sql b/application/database_postgres/sql/INSERT/locations.sql index 06f1f58..7cf5950 100644 --- a/application/database_postgres/sql/INSERT/locations.sql +++ b/application/database_postgres/sql/INSERT/locations.sql @@ -1,4 +1,4 @@ INSERT INTO %%site_name%%_locations -(uuid, name, zone_id) -VALUES (%s, %s, %s) +(location_shortname, location_name, zone_uuid) +VALUES (%(location_shortname)s, %(location_name)s, %(zone_uuid)s) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/logistics_info.sql b/application/database_postgres/sql/INSERT/logistics_info.sql index 1d2ca28..cc49c42 100644 --- a/application/database_postgres/sql/INSERT/logistics_info.sql +++ b/application/database_postgres/sql/INSERT/logistics_info.sql @@ -1,4 +1,16 @@ INSERT INTO %%site_name%%_logistics_info -(barcode, primary_location, primary_zone, auto_issue_location, auto_issue_zone) -VALUES (%(barcode)s, %(primary_location)s, %(primary_zone)s, %(auto_issue_location)s, %(auto_issue_zone)s) +( + item_uuid, + item_primary_location, + item_primary_zone, + item_auto_issue_location, + item_auto_issue_zone +) +VALUES ( + %(item_uuid)s, + %(item_primary_location)s, + %(item_primary_zone)s, + %(item_auto_issue_location)s, + %(item_auto_issue_zone)s +) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/roles.sql b/application/database_postgres/sql/INSERT/roles.sql new file mode 100644 index 0000000..af608a9 --- /dev/null +++ b/application/database_postgres/sql/INSERT/roles.sql @@ -0,0 +1,4 @@ +INSERT INTO roles +(role_name, role_description, role_site_uuid, role_flags) +VALUES (%(role_name)s, %(role_description)s, %(role_site_uuid)s, %(role_flags)s) +RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/sites.sql b/application/database_postgres/sql/INSERT/sites.sql new file mode 100644 index 0000000..4f16227 --- /dev/null +++ b/application/database_postgres/sql/INSERT/sites.sql @@ -0,0 +1,7 @@ +INSERT INTO sites +(site_name, site_description, site_created_by, site_default_zone_uuid, site_default_auto_issue_location_uuid, +site_default_primary_location_uuid, site_created_on, site_flags) +VALUES (%(site_name)s, %(site_description)s, %(site_created_by)s, %(site_default_zone_uuid)s, +%(site_default_auto_issue_location_uuid)s, %(site_default_primary_location_uuid)s, +%(site_created_on)s, %(site_flags)s) +RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/transactions.sql b/application/database_postgres/sql/INSERT/transactions.sql index dd0ee91..90135ba 100644 --- a/application/database_postgres/sql/INSERT/transactions.sql +++ b/application/database_postgres/sql/INSERT/transactions.sql @@ -1,6 +1,24 @@ INSERT INTO %%site_name%%_transactions -(timestamp, logistics_info_id, barcode, name, transaction_type, -quantity, description, user_id, data) -VALUES (%(timestamp)s, %(logistics_info_id)s, %(barcode)s, %(name)s, %(transaction_type)s, -%(quantity)s, %(description)s, %(user_id)s, %(data)s) +( + item_uuid, + transaction_created_by, + transaction_name, + transaction_type, + transaction_created_at, + transaction_quantity, + transaction_description, + transaction_cost, + transaction_data +) +VALUES ( + %(item_uuid)s, + %(transaction_created_by)s, + %(transaction_name)s, + %(transaction_type)s, + %(transaction_created_at)s, + %(transaction_quantity)s, + %(transaction_description)s, + %(transaction_cost)s, + %(transaction_data)s +) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/units.sql b/application/database_postgres/sql/INSERT/units.sql new file mode 100644 index 0000000..7f1d99c --- /dev/null +++ b/application/database_postgres/sql/INSERT/units.sql @@ -0,0 +1,3 @@ +INSERT INTO units (unit_plural, unit_single, unit_fullname, unit_description) +VALUES (%(unit_plural)s, %(unit_single)s,%(unit_fullname)s,%(unit_description)s) +RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/users.sql b/application/database_postgres/sql/INSERT/users.sql new file mode 100644 index 0000000..a181289 --- /dev/null +++ b/application/database_postgres/sql/INSERT/users.sql @@ -0,0 +1,3 @@ +INSERT INTO users (user_name, user_password, user_email) +VALUES (%(user_name)s, %(user_password)s, %(user_email)s) +RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/vendors.sql b/application/database_postgres/sql/INSERT/vendors.sql index 6c8376a..a7b328a 100644 --- a/application/database_postgres/sql/INSERT/vendors.sql +++ b/application/database_postgres/sql/INSERT/vendors.sql @@ -1,4 +1,4 @@ INSERT INTO %%site_name%%_vendors -(vendor_name, vendor_address, creation_date, created_by, phone_number) -VALUES (%(vendor_name)s, %(vendor_address)s, %(creation_date)s, %(created_by)s, %(phone_number)s) +(vendor_name, vendor_address, vendor_creation_date, vendor_created_by, vendor_phone_number) +VALUES (%(vendor_name)s, %(vendor_address)s, %(vendor_creation_date)s, %(vendor_created_by)s, %(vendor_phone_number)s) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/INSERT/zones.sql b/application/database_postgres/sql/INSERT/zones.sql index e6a7da2..3d682bc 100644 --- a/application/database_postgres/sql/INSERT/zones.sql +++ b/application/database_postgres/sql/INSERT/zones.sql @@ -1,4 +1,4 @@ INSERT INTO %%site_name%%_zones -(name, description) -VALUES (%(name)s, %(description)s) +(zone_name, zone_description) +VALUES (%(zone_name)s, %(zone_description)s) RETURNING *; \ No newline at end of file diff --git a/application/database_postgres/sql/ItemsModel/paginateItemsWithQOH.sql b/application/database_postgres/sql/ItemsModel/paginateItemsWithQOH.sql new file mode 100644 index 0000000..8a4025d --- /dev/null +++ b/application/database_postgres/sql/ItemsModel/paginateItemsWithQOH.sql @@ -0,0 +1,17 @@ +WITH sum_cte AS ( + SELECT items.item_uuid, SUM(items_locations.item_quantity_on_hand)::FLOAT8 AS total_sum + FROM %%site_name%%_item_locations items_locations + JOIN %%site_name%%_items items ON items_locations.item_uuid = items.item_uuid + GROUP BY items.item_uuid + ) + +SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.unit_fullname +FROM %%site_name%%_items items +LEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuid +LEFT JOIN %%site_name%%_item_info item_info ON items.item_uuid = item_info.item_uuid +LEFT JOIN units ON item_info.item_uom = units.unit_uuid +WHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' + AND items.item_inactive IS false +ORDER BY %%sort_order%% +LIMIT %(limit)s OFFSET %(offset)s; + diff --git a/application/database_postgres/sql/ItemsModel/paginateitemsForModal.sql b/application/database_postgres/sql/ItemsModel/paginateitemsForModal.sql new file mode 100644 index 0000000..9e97c2b --- /dev/null +++ b/application/database_postgres/sql/ItemsModel/paginateitemsForModal.sql @@ -0,0 +1,7 @@ +SELECT items.item_uuid, items.item_name, units.unit_uuid as uom FROM %%site_name%%_items items +LEFT JOIN %%site_name%%_item_info item_info ON item_info.item_uuid = items.item_uuid +LEFT JOIN units ON units.unit_uuid = item_info.item_uom +WHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' + ANd items.item_inactive IS false +ORDER BY %%sort_order%% +LIMIT %(limit)s OFFSET %(offset)s; \ No newline at end of file diff --git a/application/items/__pycache__/__init__.cpython-313.pyc b/application/items/__pycache__/__init__.cpython-313.pyc index e1f329c..45b2a0a 100644 Binary files a/application/items/__pycache__/__init__.cpython-313.pyc and b/application/items/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/items/__pycache__/database_items.cpython-313.pyc b/application/items/__pycache__/database_items.cpython-313.pyc index 2fcfacd..6b7d994 100644 Binary files a/application/items/__pycache__/database_items.cpython-313.pyc and b/application/items/__pycache__/database_items.cpython-313.pyc differ diff --git a/application/items/__pycache__/items_API.cpython-313.pyc b/application/items/__pycache__/items_API.cpython-313.pyc index 18f42d6..a6f45a0 100644 Binary files a/application/items/__pycache__/items_API.cpython-313.pyc and b/application/items/__pycache__/items_API.cpython-313.pyc differ diff --git a/application/items/__pycache__/items_models.cpython-313.pyc b/application/items/__pycache__/items_models.cpython-313.pyc new file mode 100644 index 0000000..2e3c630 Binary files /dev/null and b/application/items/__pycache__/items_models.cpython-313.pyc differ diff --git a/application/items/__pycache__/items_processes.cpython-313.pyc b/application/items/__pycache__/items_processes.cpython-313.pyc index a9c1524..0c45d1b 100644 Binary files a/application/items/__pycache__/items_processes.cpython-313.pyc and b/application/items/__pycache__/items_processes.cpython-313.pyc differ diff --git a/application/items/__pycache__/models.cpython-313.pyc b/application/items/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000..d452d70 Binary files /dev/null and b/application/items/__pycache__/models.cpython-313.pyc differ diff --git a/application/items/__pycache__/services.cpython-313.pyc b/application/items/__pycache__/services.cpython-313.pyc new file mode 100644 index 0000000..6f550a3 Binary files /dev/null and b/application/items/__pycache__/services.cpython-313.pyc differ diff --git a/application/items/items_API.py b/application/items/items_API.py index 1f1b8c6..e1ebbe5 100644 --- a/application/items/items_API.py +++ b/application/items/items_API.py @@ -15,6 +15,11 @@ import application.postsqldb as db from application.items import database_items from application.items import items_processes import application.database_payloads as dbPayloads +from application.database_postgres.UsersModel import UsersModel +from application.database_postgres.SitesModel import SitesModel +from application.database_postgres.UnitsModel import UnitsModel +from application.items import models +from application.database_postgres.ItemsModel import ItemsModel items_api = Blueprint('items_api', __name__, template_folder="templates", static_folder="static") @@ -28,59 +33,51 @@ def update_session_user(): @items_api.route("/") @access_api.login_required def items(): - update_session_user() - sites = [site[1] for site in db.get_sites(session['user']['sites'])] + sites = [SitesModel.select_tuple('', {'key': site})['site_name'] for site in session['user'].get('user_sites', [])] + access_api.update_session_user() return render_template("index.html", current_site=session['selected_site'], sites=sites) -@items_api.route("/") +@items_api.route("/") @access_api.login_required -def item(id): - sites = [site[1] for site in db.get_sites(session['user']['sites'])] - database_config = config() - with psycopg2.connect(**database_config) as conn: - units = db.UnitsTable.getAll(conn) +def item(item_uuid): + sites = [SitesModel.select_tuple('', {'key': site})['site_name'] for site in session['user'].get('user_sites', [])] + units = UnitsModel.select_tuples('') return render_template("item_new.html", id=id, units=units, current_site=session['selected_site'], sites=sites) @items_api.route("/transaction") @access_api.login_required def transaction(): - sites = [site[1] for site in db.get_sites(session['user']['sites'])] - database_config = config() - with psycopg2.connect(**database_config) as conn: - units = db.UnitsTable.getAll(conn) - return render_template("transaction.html", units=units, current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}) + sites = [SitesModel.select_tuple('', {'key': site})['site_name'] for site in session['user'].get('user_sites', [])] + units = UnitsModel.select_tuples('') + return render_template("transaction.html", units=units, current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}) -@items_api.route("/transactions/") +@items_api.route("/transactions/") @access_api.login_required -def transactions(id): - sites = [site[1] for site in db.get_sites(session['user']['sites'])] - return render_template("transactions.html", id=id, current_site=session['selected_site'], sites=sites) - -@items_api.route("//itemLink/") -@access_api.login_required -def itemLink(parent_id, id): - sites = [site[1] for site in db.get_sites(session['user']['sites'])] - return render_template("itemlink.html", current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}, id=id) +def transactions(item_uuid): + sites = [SitesModel.select_tuple('', {'key': site})['site_name'] for site in session['user'].get('user_sites', [])] + return render_template("transactions.html", item_uuid=item_uuid, current_site=session['selected_site'], sites=sites) # API CALLS -@items_api.route("/getTransactions", methods=["GET"]) +@items_api.route("/api/getTransactions", methods=["GET"]) @access_api.login_required def getTransactions(): if request.method == "GET": recordset = [] count = 0 - logistics_info_id = int(request.args.get('logistics_info_id', 1)) + item_uuid = request.args.get('item_uuid', None) page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 50)) site_name = session['selected_site'] offset = (page - 1) * limit - recordset, count = database_items.getTransactions(site_name, (logistics_info_id, limit, offset)) + filter = {'item_uuid': item_uuid, 'limit': limit, 'offset': offset} + recordset, count = models.ExtendedTransactionModel.paginate_transactions_by_item_uuid(site_name, filter) return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": False, "message": ""}) return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": True, "message": f"method {request.method} is not allowed."}) -@items_api.route("/getTransaction", methods=["GET"]) +#potential unused, was used on the transactions.html page +@items_api.route("/api/getTransaction", methods=["GET"]) @access_api.login_required def getTransaction(): transaction = () @@ -91,18 +88,32 @@ def getTransaction(): return jsonify({"transaction": transaction, "error": False, "message": ""}) return jsonify({"transaction": transaction, "error": True, "message": f"method {request.method} is not allowed."}) -@items_api.route("/getItem", methods=["GET"]) +@items_api.route("/api/getItem", methods=["GET"]) @access_api.login_required def get_item(): if request.method == "GET": id = int(request.args.get('id', 1)) site_name = session['selected_site'] item = () + item = database_items.getItemAllByID(site_name, (id, )) return jsonify({'item': item, 'error': False, 'message': ''}) return jsonify({'item': item, 'error': True, 'message': f'method {request.method} not allowed.'}) -@items_api.route("/getItemsWithQOH", methods=['GET']) +@items_api.route("/api/getTransactionItem", methods=["GET"]) +@access_api.login_required +def getTransactionItem(): + if request.method == "GET": + item_uuid = request.args.get('item_uuid', None) + site_name = session['selected_site'] + item = () + if item_uuid: + item = models.ExtendedItemsModel.get_item_for_transactions(site_name, {'item_uuid': item_uuid}) + return jsonify({'item': item, 'error': False, 'message': ''}) + return jsonify({'item': item, 'error': True, 'message': f'method {request.method} not allowed.'}) + + +@items_api.route("/api/getItemsWithQOH", methods=['GET']) @access_api.login_required def pagninate_items(): items = [] @@ -118,13 +129,14 @@ def pagninate_items(): if sort == 'total_qoh': sort_order = f"{sort} {order}" else: - sort_order = f"item.{sort} {order}" + sort_order = f"items.{sort} {order}" - items, count = database_items.getItemsWithQOH(site_name, (search_string, limit, offset, sort_order)) + filter = {'search_string': search_string, 'limit': limit, 'offset': offset, 'sort_order': sort_order} + items, count = ItemsModel.paginate_items_with_qoh(site_name, filter) return jsonify({'items': items, "end": math.ceil(count/limit), 'error':False, 'message': 'Items Loaded Successfully!'}) return jsonify({'items': items, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading the items!'}) -@items_api.route('/getModalItems', methods=["GET"]) +@items_api.route('/api/getModalItems', methods=["GET"]) @access_api.login_required def getModalItems(): recordset, count = tuple(), 0 @@ -134,7 +146,9 @@ def getModalItems(): search_string = request.args.get('search_string', '') site_name = session['selected_site'] offset = (page - 1) * limit - recordset, count = database_items.getModalSKUs(site_name, (search_string, limit, offset)) + sort_order = "item_name ASC" + filter = {'search_string': search_string, 'limit': limit, 'offset': offset, 'sort_order': sort_order} + recordset, count = ItemsModel.paginate_items_for_modal(site_name, filter) return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"}) return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message": f"method {request.method} is not allowed."}) diff --git a/application/items/models.py b/application/items/models.py new file mode 100644 index 0000000..27791fe --- /dev/null +++ b/application/items/models.py @@ -0,0 +1,72 @@ +import psycopg2 + +from application.database_postgres.TransactionsModel import TransactionsModel +from application.database_postgres.ItemsModel import ItemsModel +from application.database_postgres.BaseModel import tupleDictionaryFactory, DatabaseError +import config + +class ExtendedItemsModel(ItemsModel): + @classmethod + def get_item_for_transactions(self, site: str, payload: dict, convert=True, conn=None): + with open('application/items/sql/getItemForTransaction.sql', 'r+') as file: + sql = file.read().replace("%%site_name%%", site) + + record = () + self_conn = False + + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + record = tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + record = rows + + if self_conn: + conn.close() + + return record + except Exception as error: + raise DatabaseError(error, {}, sql) + +class ExtendedTransactionModel(TransactionsModel): + @classmethod + def paginate_transactions_by_item_uuid(self, site:str, payload:dict, convert=True, conn=None): + sql = f"SELECT * FROM {site}_transactions WHERE item_uuid = %(item_uuid)s::uuid LIMIT %(limit)s OFFSET %(offset)s;" + sql_count = f"SELECT COUNT(*) FROM {site}_transactions WHERE item_uuid=%(item_uuid)s::uuid;" + records = () + self_conn = False + + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + records = [tupleDictionaryFactory(cur.description, row) for row in rows] + elif rows and not convert: + records = rows + + cur.execute(sql_count, payload) + count = cur.fetchone()[0] + + if self_conn: + conn.commit() + conn.close() + + return records, count + + except Exception as error: + raise DatabaseError(error, {}, sql) \ No newline at end of file diff --git a/application/items/services.py b/application/items/services.py new file mode 100644 index 0000000..6e4fb09 --- /dev/null +++ b/application/items/services.py @@ -0,0 +1,61 @@ +import psycopg2 + +from application.database_postgres.ItemsModel import ItemsModel +from application.database_postgres.ItemInfoModel import ItemInfoModel +from application.database_postgres.LogisticsInfoModel import LogisticsInfoModel +from application.database_postgres.FoodInfoModel import FoodInfoModel +from application.database_postgres.TransactionsModel import TransactionsModel +from application.database_postgres.ItemLocationsModel import ItemLocationsModel +import config + +def add_new_item(site: str, data: dict, user_uuid: str, conn=None): + item_data = data.get('item_data') + food_info = data.get('food_data', {}) + item_info = data.get('item_info', {}) + logistics_info = data.get('logistics_info', {}) + + self_conn = False + + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = False + self_conn = True + + item_payload = ItemsModel.Payload(**item_data) + + item = ItemsModel.insert_tuple(site, item_payload.payload_dictionary(), conn=conn) + + item_info['item_uuid'] = item['item_uuid'] + item_info_payload = ItemInfoModel.Payload(**item_info) + item_info = ItemInfoModel.insert_tuple(site, item_info_payload.payload_dictionary(), conn=conn) + + logistics_info['item_uuid'] = item['item_uuid'] + logistics_info_payload = LogisticsInfoModel.Payload(**logistics_info) + logistics_info = LogisticsInfoModel.insert_tuple(site, logistics_info_payload.payload_dictionary(), conn=conn) + + + if 'item_primary_location' in logistics_info.keys(): + items_location = ItemLocationsModel.Payload( + item_uuid=item['item_uuid'], + location_uuid=logistics_info['item_primary_location'] + ) + items_location = ItemLocationsModel.insert_tuple(site, items_location.payload_dictionary(), conn=conn) + + if item['item_category'] in ['FOOD', 'FOOD PLU']: + food_info['item_uuid'] = item['item_uuid'] + food_info_payload = FoodInfoModel.Payload(**food_info) + food_info = FoodInfoModel.insert_tuple(site, food_info_payload.payload_dictionary(), conn=conn) + + + transaction = TransactionsModel.Payload( + item_uuid=item['item_uuid'], + transaction_created_by=user_uuid, + transaction_name="Item Created", + transaction_type="SYSTEM" + ) + transaction = TransactionsModel.insert_tuple(site, transaction.payload_dictionary(), conn=conn) + + if self_conn: + conn.commit() + conn.close() \ No newline at end of file diff --git a/application/items/sql/getItemForTransaction.sql b/application/items/sql/getItemForTransaction.sql new file mode 100644 index 0000000..9d16325 --- /dev/null +++ b/application/items/sql/getItemForTransaction.sql @@ -0,0 +1,28 @@ +WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), + cte_item_locations_qty AS ( + SELECT SUM(item_locations.item_quantity_on_hand) AS quantity_on_hand + FROM %%site_name%%_item_locations item_locations + WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) + ), + cte_item_locations AS ( + SELECT item_locations.item_location_uuid, item_locations.item_quantity_on_hand, locations.location_shortname, zones.zone_uuid, locations.location_uuid + FROM %%site_name%%_item_locations item_locations + LEFT JOIN %%site_name%%_locations locations ON locations.location_uuid = item_locations.location_uuid + LEFT JOIN %%site_name%%_zones zones ON zones.zone_uuid = locations.zone_uuid + WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) + ) + +SELECT + items.item_uuid, + items.item_name, + item_info.item_cost, + (SELECT COALESCE(ilsq.quantity_on_hand, 0.00) FROM cte_item_locations_qty ilsq) AS item_quantity_on_hand, + COALESCE(units.unit_fullname, 'ERROR') AS unit_fullname, + (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locations, + (SELECT COALESCE(row_to_json(zones.*), '{}') FROM %%site_name%%_zones zones WHERE zones.zone_uuid = log_info.item_primary_zone) AS primary_zone, + (SELECT COALESCE(row_to_json(locations.*), '{}') FROM %%site_name%%_locations locations WHERE locations.location_uuid = log_info.item_primary_location) AS primary_location +FROM %%site_name%%_items items +LEFT JOIN %%site_name%%_item_info item_info ON item_info.item_uuid = items.item_uuid +LEFT JOIN units ON item_info.item_uom = units.unit_uuid +LEFT JOIN %%site_name%%_logistics_info log_info ON log_info.item_uuid = items.item_uuid +WHERE items.item_uuid = (SELECT item_uuid FROM parameters); \ No newline at end of file diff --git a/application/items/sql/getItemAllByBarcode.sql b/application/items/sql_old/getItemAllByBarcode.sql similarity index 100% rename from application/items/sql/getItemAllByBarcode.sql rename to application/items/sql_old/getItemAllByBarcode.sql diff --git a/application/items/sql/getItemAllByID.sql b/application/items/sql_old/getItemAllByID.sql similarity index 100% rename from application/items/sql/getItemAllByID.sql rename to application/items/sql_old/getItemAllByID.sql diff --git a/application/items/sql/getItemLocations.sql b/application/items/sql_old/getItemLocations.sql similarity index 100% rename from application/items/sql/getItemLocations.sql rename to application/items/sql_old/getItemLocations.sql diff --git a/application/items/sql/getItemsAll.sql b/application/items/sql_old/getItemsAll.sql similarity index 100% rename from application/items/sql/getItemsAll.sql rename to application/items/sql_old/getItemsAll.sql diff --git a/application/items/sql/getItemsWithQOH.sql b/application/items/sql_old/getItemsWithQOH.sql similarity index 100% rename from application/items/sql/getItemsWithQOH.sql rename to application/items/sql_old/getItemsWithQOH.sql diff --git a/application/items/sql/getLocationsWithZone.sql b/application/items/sql_old/getLocationsWithZone.sql similarity index 100% rename from application/items/sql/getLocationsWithZone.sql rename to application/items/sql_old/getLocationsWithZone.sql diff --git a/application/items/sql/getSkuPrefixes.sql b/application/items/sql_old/getSkuPrefixes.sql similarity index 100% rename from application/items/sql/getSkuPrefixes.sql rename to application/items/sql_old/getSkuPrefixes.sql diff --git a/application/items/sql/insertCostLayersTuple.sql b/application/items/sql_old/insertCostLayersTuple.sql similarity index 100% rename from application/items/sql/insertCostLayersTuple.sql rename to application/items/sql_old/insertCostLayersTuple.sql diff --git a/application/items/sql/insertFoodInfoTuple.sql b/application/items/sql_old/insertFoodInfoTuple.sql similarity index 100% rename from application/items/sql/insertFoodInfoTuple.sql rename to application/items/sql_old/insertFoodInfoTuple.sql diff --git a/application/items/sql/insertItemInfoTuple.sql b/application/items/sql_old/insertItemInfoTuple.sql similarity index 100% rename from application/items/sql/insertItemInfoTuple.sql rename to application/items/sql_old/insertItemInfoTuple.sql diff --git a/application/items/sql/insertItemLinksTuple.sql b/application/items/sql_old/insertItemLinksTuple.sql similarity index 100% rename from application/items/sql/insertItemLinksTuple.sql rename to application/items/sql_old/insertItemLinksTuple.sql diff --git a/application/items/sql/insertItemLocationsTuple copy.sql b/application/items/sql_old/insertItemLocationsTuple copy.sql similarity index 100% rename from application/items/sql/insertItemLocationsTuple copy.sql rename to application/items/sql_old/insertItemLocationsTuple copy.sql diff --git a/application/items/sql/insertItemLocationsTuple.sql b/application/items/sql_old/insertItemLocationsTuple.sql similarity index 100% rename from application/items/sql/insertItemLocationsTuple.sql rename to application/items/sql_old/insertItemLocationsTuple.sql diff --git a/application/items/sql/insertItemTuple.sql b/application/items/sql_old/insertItemTuple.sql similarity index 100% rename from application/items/sql/insertItemTuple.sql rename to application/items/sql_old/insertItemTuple.sql diff --git a/application/items/sql/insertLogisticsInfoTuple.sql b/application/items/sql_old/insertLogisticsInfoTuple.sql similarity index 100% rename from application/items/sql/insertLogisticsInfoTuple.sql rename to application/items/sql_old/insertLogisticsInfoTuple.sql diff --git a/application/items/sql/insertSKUPrefixTuple.sql b/application/items/sql_old/insertSKUPrefixTuple.sql similarity index 100% rename from application/items/sql/insertSKUPrefixTuple.sql rename to application/items/sql_old/insertSKUPrefixTuple.sql diff --git a/application/items/sql/insertTransactionsTuple.sql b/application/items/sql_old/insertTransactionsTuple.sql similarity index 100% rename from application/items/sql/insertTransactionsTuple.sql rename to application/items/sql_old/insertTransactionsTuple.sql diff --git a/application/items/sql/itemsModal.sql b/application/items/sql_old/itemsModal.sql similarity index 100% rename from application/items/sql/itemsModal.sql rename to application/items/sql_old/itemsModal.sql diff --git a/application/items/sql/itemsModalCount.sql b/application/items/sql_old/itemsModalCount.sql similarity index 100% rename from application/items/sql/itemsModalCount.sql rename to application/items/sql_old/itemsModalCount.sql diff --git a/application/items/sql/paginateLocationsBySkuZone.sql b/application/items/sql_old/paginateLocationsBySkuZone.sql similarity index 100% rename from application/items/sql/paginateLocationsBySkuZone.sql rename to application/items/sql_old/paginateLocationsBySkuZone.sql diff --git a/application/items/sql/paginateLocationsBySkuZoneCount.sql b/application/items/sql_old/paginateLocationsBySkuZoneCount.sql similarity index 100% rename from application/items/sql/paginateLocationsBySkuZoneCount.sql rename to application/items/sql_old/paginateLocationsBySkuZoneCount.sql diff --git a/application/items/sql/paginateZonesBySku.sql b/application/items/sql_old/paginateZonesBySku.sql similarity index 100% rename from application/items/sql/paginateZonesBySku.sql rename to application/items/sql_old/paginateZonesBySku.sql diff --git a/application/items/sql/paginateZonesBySkuCount.sql b/application/items/sql_old/paginateZonesBySkuCount.sql similarity index 100% rename from application/items/sql/paginateZonesBySkuCount.sql rename to application/items/sql_old/paginateZonesBySkuCount.sql diff --git a/application/items/sql/selectItemByBarcode.sql b/application/items/sql_old/selectItemByBarcode.sql similarity index 100% rename from application/items/sql/selectItemByBarcode.sql rename to application/items/sql_old/selectItemByBarcode.sql diff --git a/application/items/static/ItemListHandler.js b/application/items/static/ItemListHandler.js index 5411c29..9c7949c 100644 --- a/application/items/static/ItemListHandler.js +++ b/application/items/static/ItemListHandler.js @@ -174,6 +174,8 @@ async function updateTableElements(){ let table_body = document.createElement('tbody') + console.log(items) + for (let i = 0; i < items.length; i++){ let table_row = document.createElement('tr') @@ -181,11 +183,11 @@ async function updateTableElements(){ nameCell.innerHTML = items[i].item_name nameCell.setAttribute('class', 'uk-width-1-4') let descriptionCell = document.createElement('td') - descriptionCell.innerHTML = items[i].description + descriptionCell.innerHTML = items[i].item_description descriptionCell.setAttribute('class', 'uk-text-truncate uk-table-expand uk-visible@m') let qtyUOMCell = document.createElement('td') - qtyUOMCell.innerHTML = `${parseFloat(items[i].total_qoh)} ${items[i].fullname}` + qtyUOMCell.innerHTML = `${parseFloat(items[i].quantity_on_hand)} ${items[i].fullname}` let opsCell = document.createElement('td') opsCell.setAttribute('class', 'uk-width-1-4') @@ -196,12 +198,12 @@ async function updateTableElements(){ let viewOp = document.createElement('a') viewOp.innerHTML = `edit ` viewOp.setAttribute('class', 'uk-button uk-button-default uk-button-small') - viewOp.href = `/items/${items[i].id}` + viewOp.href = `/items/${items[i].item_uuid}` let historyOp = document.createElement('a') historyOp.innerHTML = `history ` historyOp.setAttribute('class', 'uk-button uk-button-default uk-button-small') - historyOp.href = `/items/transactions/${items[i].id}` + historyOp.href = `/items/transactions/${items[i].item_uuid}` buttonGroup.append(viewOp, historyOp) opsCell.append(buttonGroup) @@ -237,16 +239,16 @@ async function updateListElements(){ header.classList.add('uk-card-header') header.style = "border-radius: 0px, 10px, 0px, 10px;" - header.innerHTML = `

${items[i].item_name}

Quantity on Hand: ${parseFloat(items[i].total_qoh)} ${items[i].fullname}
` + header.innerHTML = `

${items[i].item_name}

Quantity on Hand: ${parseFloat(items[i].quantity_on_hand)} ${items[i].fullname}
` let content = document.createElement('div') content.classList.add('uk-card-body') - content.innerHTML = `

${items[i].description}

` + content.innerHTML = `

${items[i].item_description}

` let footer = document.createElement('div') footer.classList.add('uk-card-footer') - footer.innerHTML = `edit - History` + footer.innerHTML = `edit + History` listItem.append(header) if(!items[i].description == ""){ @@ -263,7 +265,7 @@ async function updateListElements(){ items_list.append(main_list) } -let sort = "id" +let sort = "item_uuid" async function setSort(sort_string) { sort = sort_string await getItems() @@ -278,7 +280,7 @@ async function setOrder(order_string) { } async function getItems(){ - const url = new URL('/items/getItemsWithQOH', window.location.origin); + const url = new URL('/items/api/getItemsWithQOH', window.location.origin); url.searchParams.append('page', current_page); url.searchParams.append('limit', limit); url.searchParams.append('search_text', searchText); diff --git a/application/items/static/transactionHandler.js b/application/items/static/transactionHandler.js index 8210cfc..5100dc8 100644 --- a/application/items/static/transactionHandler.js +++ b/application/items/static/transactionHandler.js @@ -37,68 +37,62 @@ async function replenishItemsTable(items) { for(let i = 0; i < items.length; i++){ let tableRow = document.createElement('tr') - - let idCell = document.createElement('td') - idCell.innerHTML = items[i].id - let barcodeCell = document.createElement('td') - barcodeCell.innerHTML = items[i].barcode let nameCell = document.createElement('td') nameCell.innerHTML = items[i].item_name - tableRow.append(idCell) - tableRow.append(barcodeCell) - tableRow.append(nameCell) + let opCell = document.createElement('td') - tableRow.onclick = function(){ - selectItem(items[i].id) + let selectButton = document.createElement('button') + selectButton.setAttribute('class', 'uk-button uk-button-small uk-button-primary') + selectButton.innerHTML = "Select" + selectButton.onclick = async function(){ + console.log('clicked') + await selectItem(items[i].item_uuid) } + opCell.append(selectButton) + tableRow.append(nameCell, opCell) itemsTableBody.append(tableRow) } } async function populateForm() { if (item){ - console.log(item) - document.getElementById('database_id').value = item.id - document.getElementById('barcode').value = item.barcode + document.getElementById('database_uuid').value = item.item_uuid document.getElementById('name').value = item.item_name - document.getElementById('transaction_cost').value = parseFloat(item.item_info.cost) + document.getElementById('transaction_cost').value = parseFloat(item.item_cost) await selectLocation( - item.logistics_info.primary_zone.id, - item.logistics_info.primary_location.id, - item.logistics_info.primary_zone.name, - item.logistics_info.primary_location.name + item.primary_zone.zone_uuid, + item.primary_location.location_uuid, + item.primary_zone.zone_name, + item.primary_location.location_name ) - let quantity_on_hand = 0 - let locations = await getItemLocations() - for(let i = 0; i < locations.length; i++){ - quantity_on_hand = quantity_on_hand + locations[i].quantity_on_hand - } - document.getElementById('QOH').value = quantity_on_hand - document.getElementById('UOM').value = item.item_info.uom.fullname + document.getElementById('QOH').value = item.item_quantity_on_hand + document.getElementById('UOM').value = item.unit_fullname - await replenishItemLocationsTable(locations) + await replenishItemLocationsTable(item.item_locations) } } -async function selectItem(id) { +async function selectItem(item_uuid) { + console.log(item_uuid) UIkit.modal(document.getElementById("itemsModal")).hide(); - item = await getItem(id) + item = await getItem(item_uuid) + console.log(item) await populateForm() } -var transaction_zone_id = 0 -var transaction_item_location_id = 0 -async function selectLocation(zone_id, location_id, zone_name, location_name) { +var transaction_zone_uuid = "" +var transaction_location_uuid = "" +async function selectLocation(zone_uuid, location_uuid, zone_name, location_name) { document.getElementById('zone').value = zone_name document.getElementById('location').value = location_name - transaction_zone_id = zone_id - transaction_item_location_id = location_id + transaction_zone_uuid = zone_uuid + transaction_item_location_uuid = location_uuid } async function openItemsModal(elementID){ @@ -139,7 +133,7 @@ async function replenishItemLocationsTable(locations) { for(let i = 0; i < locations.length; i++){ let tableRow = document.createElement('tr') - let loca = locations[i].uuid.split('@') + let loca = locations[i].location_shortname.split('@') let zoneCell = document.createElement('td') zoneCell.innerHTML = loca[0] @@ -148,13 +142,13 @@ async function replenishItemLocationsTable(locations) { locationCell.innerHTML = loca[1] let qohCell = document.createElement('td') - qohCell.innerHTML = parseFloat(locations[i].quantity_on_hand) + qohCell.innerHTML = parseFloat(locations[i].item_quantity_on_hand) tableRow.append(zoneCell, locationCell, qohCell) tableRow.onclick = async function(){ await selectLocation( - locations[i].zone_id, - locations[i].id, + locations[i].zone_uuid, + locations[i].location_uuid, loca[0], loca[1] ) @@ -182,7 +176,7 @@ async function getItemLocations() { let items_limit = 50; async function getItems() { console.log("getting items") - const url = new URL('/items/getModalItems', window.location.origin); + const url = new URL('/items/api/getModalItems', window.location.origin); url.searchParams.append('page', pagination_current); url.searchParams.append('limit', items_limit); url.searchParams.append('search_string', search_string) @@ -193,10 +187,10 @@ async function getItems() { return items; } -async function getItem(id) { - console.log(`selected item: ${id}`) - const url = new URL('/items/getItem', window.location.origin); - url.searchParams.append('id', id); +async function getItem(item_uuid) { + console.log(`selected item: ${item_uuid}`) + const url = new URL('/items/api/getTransactionItem', window.location.origin); + url.searchParams.append('item_uuid', item_uuid); const response = await fetch(url); data = await response.json(); item = data.item; @@ -273,9 +267,7 @@ async function submitTransaction() { 'Content-Type': 'application/json', }, body: JSON.stringify({ - item_id: item.id, - logistics_info_id: item.logistics_info_id, - barcode: item.barcode, + item_uuid: item.item_uuid, item_name: item.item_name, transaction_type: document.getElementById('trans_type').value, quantity: parseFloat(document.getElementById('transaction_quantity').value), @@ -283,7 +275,7 @@ async function submitTransaction() { cost: cost, vendor: 0, expires: null, - location_id: transaction_item_location_id + location_id: transaction_location_uuid }), }); data = await response.json(); diff --git a/application/items/static/transactionsHandler.js b/application/items/static/transactionsHandler.js index 7047302..a2d76c8 100644 --- a/application/items/static/transactionsHandler.js +++ b/application/items/static/transactionsHandler.js @@ -4,21 +4,22 @@ var pagination_end = 10 var item; document.addEventListener('DOMContentLoaded', async function() { - item = await getItem(item_id); + //item = await getItem(item_uuid); let transactions = await getTransactions() + console.log(transactions) replenishTransactionsTable(transactions) updatePaginationElement() }) async function populateTransactionReceipt(transaction) { - document.getElementById('trans_barcode').innerHTML = transaction.barcode - document.getElementById('trans_database_id').innerHTML = transaction.id - document.getElementById('trans_timestamp').innerHTML = transaction.timestamp - document.getElementById('trans_name').innerHTML = transaction.name + document.getElementById('trans_barcode').innerHTML = '' + document.getElementById('trans_database_id').innerHTML = '' + document.getElementById('trans_timestamp').innerHTML = transaction.transaction_created_at + document.getElementById('trans_name').innerHTML = transaction.transaction_name document.getElementById('trans_type').innerHTML = transaction.transaction_type - document.getElementById('trans_qty').innerHTML = transaction.quantity - document.getElementById('trans_description').innerHTML = transaction.description - document.getElementById('trans_user').innerHTML = transaction.user_id + document.getElementById('trans_qty').innerHTML = transaction.transaction_quantity + document.getElementById('trans_description').innerHTML = transaction.transaction_description + document.getElementById('trans_user').innerHTML = transaction.transaction_created_by let receiptTableBody = document.getElementById('receiptTableBody') receiptTableBody.innerHTML = "" @@ -38,8 +39,7 @@ async function populateTransactionReceipt(transaction) { } } -async function inspectTransactions(id) { - let transaction = await getTransaction(id) +async function inspectTransactions(transaction) { await populateTransactionReceipt(transaction) UIkit.modal(document.getElementById("transactionModal")).show(); @@ -56,25 +56,25 @@ async function replenishTransactionsTable(transactions) { let timestampCell = document.createElement('td') - timestampCell.innerHTML = transactions[i].timestamp + timestampCell.innerHTML = transactions[i].transaction_created_at let barcodeCell = document.createElement('td') - barcodeCell.innerHTML = transactions[i].barcode + barcodeCell.innerHTML = '' let nameCell = document.createElement('td') - nameCell.innerHTML = transactions[i].name + nameCell.innerHTML = transactions[i].transaction_name let typeCell = document.createElement('td') typeCell.innerHTML = transactions[i].transaction_type let qtyCell = document.createElement('td') - qtyCell.innerHTML = transactions[i].quantity + qtyCell.innerHTML = transactions[i].transaction_quantity let descriptionCell = document.createElement('td') - descriptionCell.innerHTML = transactions[i].description + descriptionCell.innerHTML = transactions[i].transaction_description let userCell = document.createElement('td') - userCell.innerHTML = transactions[i].user_id + userCell.innerHTML = transactions[i].transaction_created_by tableRow.append( @@ -88,7 +88,7 @@ async function replenishTransactionsTable(transactions) { ) tableRow.onclick = async function() { - await inspectTransactions(transactions[i].id) + await inspectTransactions(transactions[i]) } transactionsTableBody.append(tableRow) @@ -96,7 +96,7 @@ async function replenishTransactionsTable(transactions) { } async function getItem(id) { - const url = new URL('/items/getItem', window.location.origin); + const url = new URL('/items/api/getItem', window.location.origin); url.searchParams.append('id', id); const response = await fetch(url); data = await response.json(); @@ -114,10 +114,10 @@ async function getTransaction(id) { } async function getTransactions(){ - const url = new URL('/items/getTransactions', window.location.origin); + const url = new URL('/items/api/getTransactions', window.location.origin); url.searchParams.append('page', pagination_current); url.searchParams.append('limit', limit); - url.searchParams.append('logistics_info_id', item.logistics_info_id) + url.searchParams.append('item_uuid', item_uuid) const response = await fetch(url); data = await response.json(); pagination_end = data.end diff --git a/application/items/templates/index.html b/application/items/templates/index.html index 6ab2d81..d5dcb24 100644 --- a/application/items/templates/index.html +++ b/application/items/templates/index.html @@ -15,7 +15,7 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% endif %} @@ -25,10 +25,10 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% else %} @@ -86,7 +86,7 @@
  • - Profile Picture + Profile Picture {{username}}
    @@ -197,45 +197,9 @@
    - -
    -
    - -
    -

    Add Prefix...

    -
    -
    -

    Add a Prefix to the system by providing a uuid, a name for the item, and a general description.

    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    -
    + - \ No newline at end of file diff --git a/application/items/templates/item_new.html b/application/items/templates/item_new.html index cb90d2d..d0a1beb 100644 --- a/application/items/templates/item_new.html +++ b/application/items/templates/item_new.html @@ -16,7 +16,7 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% endif %} @@ -42,10 +42,10 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% else %} @@ -104,7 +104,7 @@
    • - Profile Picture + Profile Picture {{username}}
      @@ -791,5 +791,5 @@ - + \ No newline at end of file diff --git a/application/items/templates/transaction.html b/application/items/templates/transaction.html index 6db72d4..7a8345a 100644 --- a/application/items/templates/transaction.html +++ b/application/items/templates/transaction.html @@ -14,7 +14,7 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% endif %} @@ -29,10 +29,10 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% else %} @@ -90,7 +90,7 @@
      • - Profile Picture + Profile Picture {{username}}
        @@ -129,7 +129,7 @@
        - +
        @@ -137,22 +137,18 @@
        -
        - - -
        - +
        - +
        - +
        @@ -187,16 +183,16 @@
        - +
        - +
        - +

        @@ -229,12 +225,11 @@
      - +
      - - + diff --git a/application/items/templates/transactions.html b/application/items/templates/transactions.html index 12c4e39..a7bde19 100644 --- a/application/items/templates/transactions.html +++ b/application/items/templates/transactions.html @@ -17,7 +17,7 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% endif %} @@ -27,10 +27,10 @@ - {% if session['user']['flags']['darkmode'] %} + {% if session['user']['user_flags']['darkmode'] %} {% else %} @@ -89,7 +89,7 @@
      • - Profile Picture + Profile Picture {{username}}
        @@ -153,5 +153,5 @@
        - + \ No newline at end of file diff --git a/application/meal_planner/__pycache__/__init__.cpython-313.pyc b/application/meal_planner/__pycache__/__init__.cpython-313.pyc index 36f3107..b164ad5 100644 Binary files a/application/meal_planner/__pycache__/__init__.cpython-313.pyc and b/application/meal_planner/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/meal_planner/__pycache__/meal_planner_api.cpython-313.pyc b/application/meal_planner/__pycache__/meal_planner_api.cpython-313.pyc index e6ecc7a..fd32074 100644 Binary files a/application/meal_planner/__pycache__/meal_planner_api.cpython-313.pyc and b/application/meal_planner/__pycache__/meal_planner_api.cpython-313.pyc differ diff --git a/application/meal_planner/__pycache__/meal_planner_database.cpython-313.pyc b/application/meal_planner/__pycache__/meal_planner_database.cpython-313.pyc index 4c8bad4..42bae17 100644 Binary files a/application/meal_planner/__pycache__/meal_planner_database.cpython-313.pyc and b/application/meal_planner/__pycache__/meal_planner_database.cpython-313.pyc differ diff --git a/application/meal_planner/__pycache__/meal_planner_processes.cpython-313.pyc b/application/meal_planner/__pycache__/meal_planner_processes.cpython-313.pyc index e7d07d1..a953b1a 100644 Binary files a/application/meal_planner/__pycache__/meal_planner_processes.cpython-313.pyc and b/application/meal_planner/__pycache__/meal_planner_processes.cpython-313.pyc differ diff --git a/application/poe/__pycache__/__init__.cpython-313.pyc b/application/poe/__pycache__/__init__.cpython-313.pyc index 3a14559..fb7ad87 100644 Binary files a/application/poe/__pycache__/__init__.cpython-313.pyc and b/application/poe/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/poe/__pycache__/poe_api.cpython-313.pyc b/application/poe/__pycache__/poe_api.cpython-313.pyc index 51c9bc9..0211aee 100644 Binary files a/application/poe/__pycache__/poe_api.cpython-313.pyc and b/application/poe/__pycache__/poe_api.cpython-313.pyc differ diff --git a/application/poe/__pycache__/poe_database.cpython-313.pyc b/application/poe/__pycache__/poe_database.cpython-313.pyc index 042b6f8..6ad89a5 100644 Binary files a/application/poe/__pycache__/poe_database.cpython-313.pyc and b/application/poe/__pycache__/poe_database.cpython-313.pyc differ diff --git a/application/poe/__pycache__/poe_processes.cpython-313.pyc b/application/poe/__pycache__/poe_processes.cpython-313.pyc index a0870be..7f936a8 100644 Binary files a/application/poe/__pycache__/poe_processes.cpython-313.pyc and b/application/poe/__pycache__/poe_processes.cpython-313.pyc differ diff --git a/application/receipts/__pycache__/__init__.cpython-313.pyc b/application/receipts/__pycache__/__init__.cpython-313.pyc index d53fa54..11cfe92 100644 Binary files a/application/receipts/__pycache__/__init__.cpython-313.pyc and b/application/receipts/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/receipts/__pycache__/receipts_api.cpython-313.pyc b/application/receipts/__pycache__/receipts_api.cpython-313.pyc index ce4980b..0e87153 100644 Binary files a/application/receipts/__pycache__/receipts_api.cpython-313.pyc and b/application/receipts/__pycache__/receipts_api.cpython-313.pyc differ diff --git a/application/receipts/__pycache__/receipts_database.cpython-313.pyc b/application/receipts/__pycache__/receipts_database.cpython-313.pyc index f6c88be..f21044d 100644 Binary files a/application/receipts/__pycache__/receipts_database.cpython-313.pyc and b/application/receipts/__pycache__/receipts_database.cpython-313.pyc differ diff --git a/application/receipts/__pycache__/receipts_processes.cpython-313.pyc b/application/receipts/__pycache__/receipts_processes.cpython-313.pyc index cbe998f..5d848cf 100644 Binary files a/application/receipts/__pycache__/receipts_processes.cpython-313.pyc and b/application/receipts/__pycache__/receipts_processes.cpython-313.pyc differ diff --git a/application/recipes/__pycache__/__init__.cpython-313.pyc b/application/recipes/__pycache__/__init__.cpython-313.pyc index 0525848..7aa48e8 100644 Binary files a/application/recipes/__pycache__/__init__.cpython-313.pyc and b/application/recipes/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/recipes/__pycache__/database_recipes.cpython-313.pyc b/application/recipes/__pycache__/database_recipes.cpython-313.pyc index 53ef95c..cddf8e4 100644 Binary files a/application/recipes/__pycache__/database_recipes.cpython-313.pyc and b/application/recipes/__pycache__/database_recipes.cpython-313.pyc differ diff --git a/application/recipes/__pycache__/recipe_processes.cpython-313.pyc b/application/recipes/__pycache__/recipe_processes.cpython-313.pyc index 9a36f64..2fa7a08 100644 Binary files a/application/recipes/__pycache__/recipe_processes.cpython-313.pyc and b/application/recipes/__pycache__/recipe_processes.cpython-313.pyc differ diff --git a/application/recipes/__pycache__/recipes_api.cpython-313.pyc b/application/recipes/__pycache__/recipes_api.cpython-313.pyc index cf7a6fc..b22def1 100644 Binary files a/application/recipes/__pycache__/recipes_api.cpython-313.pyc and b/application/recipes/__pycache__/recipes_api.cpython-313.pyc differ diff --git a/application/shoppinglists/__pycache__/__init__.cpython-313.pyc b/application/shoppinglists/__pycache__/__init__.cpython-313.pyc index c460c89..a36956d 100644 Binary files a/application/shoppinglists/__pycache__/__init__.cpython-313.pyc and b/application/shoppinglists/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/shoppinglists/__pycache__/shoplist_api.cpython-313.pyc b/application/shoppinglists/__pycache__/shoplist_api.cpython-313.pyc index 851b514..8618f48 100644 Binary files a/application/shoppinglists/__pycache__/shoplist_api.cpython-313.pyc and b/application/shoppinglists/__pycache__/shoplist_api.cpython-313.pyc differ diff --git a/application/shoppinglists/__pycache__/shoplist_database.cpython-313.pyc b/application/shoppinglists/__pycache__/shoplist_database.cpython-313.pyc index 16eb51c..adbd39b 100644 Binary files a/application/shoppinglists/__pycache__/shoplist_database.cpython-313.pyc and b/application/shoppinglists/__pycache__/shoplist_database.cpython-313.pyc differ diff --git a/application/shoppinglists/__pycache__/shoplist_processess.cpython-313.pyc b/application/shoppinglists/__pycache__/shoplist_processess.cpython-313.pyc index 31bd45a..32f5693 100644 Binary files a/application/shoppinglists/__pycache__/shoplist_processess.cpython-313.pyc and b/application/shoppinglists/__pycache__/shoplist_processess.cpython-313.pyc differ diff --git a/application/site_management/__pycache__/__init__.cpython-313.pyc b/application/site_management/__pycache__/__init__.cpython-313.pyc index a4d9273..d4299e1 100644 Binary files a/application/site_management/__pycache__/__init__.cpython-313.pyc and b/application/site_management/__pycache__/__init__.cpython-313.pyc differ diff --git a/application/site_management/__pycache__/site_management_api.cpython-313.pyc b/application/site_management/__pycache__/site_management_api.cpython-313.pyc index 1fd7eb2..6ca82db 100644 Binary files a/application/site_management/__pycache__/site_management_api.cpython-313.pyc and b/application/site_management/__pycache__/site_management_api.cpython-313.pyc differ diff --git a/application/site_management/__pycache__/site_management_database.cpython-313.pyc b/application/site_management/__pycache__/site_management_database.cpython-313.pyc index 6e86c96..937a990 100644 Binary files a/application/site_management/__pycache__/site_management_database.cpython-313.pyc and b/application/site_management/__pycache__/site_management_database.cpython-313.pyc differ diff --git a/config.py b/config.py index ad32bfb..346b115 100644 --- a/config.py +++ b/config.py @@ -3,7 +3,7 @@ from configparser import ConfigParser import json -def config(filename='database.ini', section='postgresql'): +def config(filename='database.ini', section='pantrydev'): # create a parser parser = ConfigParser() # read config file diff --git a/database.ini b/database.ini index 58090a8..b139bb8 100644 --- a/database.ini +++ b/database.ini @@ -5,9 +5,16 @@ user = test password = test port = 5432 +[pantrydev] +host = 192.168.1.67 +database = pantrydev +user = test +password = test +port = 5432 + [manage] sites = first_setup = False -signup_enabled = False +signup_enabled = True internal_login_enabled = True diff --git a/logs/database.log b/logs/database.log index 32b5f60..713487f 100644 --- a/logs/database.log +++ b/logs/database.log @@ -913,4 +913,316 @@ FOREIGN KEY(vendor_id) REFERENCES test2_vendors(id) );, - sql='receipts') \ No newline at end of file + sql='receipts') +2025-08-23 15:47:56.216683 --- ERROR --- DatabaseError(message='column "name" of relation "test2_zones" does not existLINE 2: (name, description) ^', + payload=('all', ''), + sql='INSERT INTO test2_zones(name, description) VALUES (%s, %s) RETURNING *;') +2025-08-23 15:49:56.096165 --- ERROR --- DatabaseError(message='column "name" of relation "test2_zones" does not existLINE 2: (name, description) ^', + payload=('kitchen', ''), + sql='INSERT INTO test2_zones(name, description) VALUES (%s, %s) RETURNING *;') +2025-08-23 15:50:05.772975 --- ERROR --- DatabaseError(message=''name'', + payload={'zone_name': 'kitchen', 'zone_description': ''}, + sql='INSERT INTO test2_zones(name, description) VALUES (%(name)s, %(description)s) RETURNING *;') +2025-08-23 15:50:43.717729 --- ERROR --- DatabaseError(message=''name'', + payload={'zone_name': 'kitchen', 'zone_description': ''}, + sql='INSERT INTO test2_zones(zone_name, zone_description) VALUES (%(name)s, %(description)s) RETURNING *;') +2025-08-23 16:09:42.637708 --- ERROR --- DatabaseError(message='relation "logins" does not existLINE 3: SELECT logins.* FROM logins ^', + payload=(1,), + sql='WITH passed_id AS (SELECT %s AS passed_id), cte_login AS ( SELECT logins.* FROM logins WHERE logins.id = (SELECT passed_id FROM passed_id) ), cte_roles AS ( SELECT roles.*, row_to_json(sites.*) AS site FROM roles LEFT JOIN sites ON sites.id = roles.site_id WHERE roles.id = ANY(SELECT unnest(site_roles) FROM cte_login) )SELECT login.*, (SELECT COALESCE(array_agg(row_to_json(r)), '{}') FROM cte_roles r) AS site_rolesFROM cte_login login;') +2025-08-23 16:24:14.236281 --- ERROR --- DatabaseError(message='column "username" named in key does not existLINE 14: UNIQUE(username), ^', + payload=CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites INTEGER [] DEFAULT '{}' NOT NULL, + user_roles INTEGER [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) NOT NULL, + user_profile_pic_url VARCHAR(255) NOT NULL, + user_login_type VARCHAR(32) NOT NULL, + UNIQUE(username), + CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + +, + sql='users') +2025-08-23 16:24:27.945561 --- ERROR --- DatabaseError(message='function uuid_generate_v4() does not existLINE 2: user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NO... ^HINT: No function matches the given name and argument types. You might need to add explicit type casts.', + payload=CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites INTEGER [] DEFAULT '{}' NOT NULL, + user_roles INTEGER [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) NOT NULL, + user_profile_pic_url VARCHAR(255) NOT NULL, + user_login_type VARCHAR(32) NOT NULL, + UNIQUE(user_name), + CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + +, + sql='users') +2025-08-23 16:29:01.746393 --- ERROR --- DatabaseError(message='column "email" does not existLINE 15: CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z... ^', + payload=CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites INTEGER [] DEFAULT '{}' NOT NULL, + user_roles INTEGER [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) NOT NULL, + user_profile_pic_url VARCHAR(255) NOT NULL, + user_login_type VARCHAR(32) NOT NULL, + UNIQUE(user_name), + CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + +, + sql='users') +2025-08-23 16:37:08.379478 --- ERROR --- DatabaseError(message=''site_created_on'', + payload={'site_name': 'test', 'site_description': 'Test Site', 'site_created_by': '1a5f4974-45a4-41c2-951b-d391eb19dbd5', 'site_default_zone_uuid': None, 'site_default_auto_issue_location_uuid': None, 'site_default_primary_location_uuid': None, 'site_flags': '{}', 'creation_date': datetime.datetime(2025, 8, 23, 16, 37, 8, 370249)}, + sql='INSERT INTO sites(site_name, site_description, site_created_by, site_default_zone_uuid, site_default_auto_issue_location_uuid,site_default_primary_location_uuid, site_created_on, site_flags) VALUES (%(site_name)s, %(site_description)s, %(site_created_by)s, %(site_default_zone_uuid)s, %(site_default_auto_issue_location_uuid)s, %(site_default_primary_location_uuid)s, %(site_created_on)s, %(site_flags)s) RETURNING *;') +2025-08-23 16:38:50.635772 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: "00210dd7-44c1-41e0-8272-3583eb85e6fc"LINE 4: VALUES ('test', 'Test Site', '00210dd7-44c1-41e0-8272-3583eb... ^', + payload={'site_name': 'test', 'site_description': 'Test Site', 'site_created_by': '00210dd7-44c1-41e0-8272-3583eb85e6fc', 'site_default_zone_uuid': None, 'site_default_auto_issue_location_uuid': None, 'site_default_primary_location_uuid': None, 'site_flags': '{}', 'site_created_on': datetime.datetime(2025, 8, 23, 16, 38, 50, 617486)}, + sql='INSERT INTO sites(site_name, site_description, site_created_by, site_default_zone_uuid, site_default_auto_issue_location_uuid,site_default_primary_location_uuid, site_created_on, site_flags) VALUES (%(site_name)s, %(site_description)s, %(site_created_by)s, %(site_default_zone_uuid)s, %(site_default_auto_issue_location_uuid)s, %(site_default_primary_location_uuid)s, %(site_created_on)s, %(site_flags)s) RETURNING *;') +2025-08-23 17:04:20.162561 --- ERROR --- DatabaseError(message='relation "logins" does not existLINE 1: UPDATE logins SET sites = sites || '5c850d2d-ee69-4ac4-bfd5-... ^', + payload={'user_uuid': '50743800-3079-455e-a670-5f7877788dec', 'site_uuid': 'b25fffee-4cef-4709-8715-db530e3c9384', 'role_uuid': '5c850d2d-ee69-4ac4-bfd5-06e7c0975ca9'}, + sql='UPDATE logins SET sites = sites || %(role_uuid)s, site_roles = site_roles || %(site_uuid)s WHERE user_uuid=%(user_uuid)s RETURNING *;') +2025-08-23 17:04:40.004205 --- ERROR --- DatabaseError(message='column "sites" does not existLINE 1: UPDATE users SET sites = sites || '08c400af-3ddc-470f-9c9b-6... ^', + payload={'user_uuid': 'c5bd7fa0-1fcb-4191-8a55-bf41fd758526', 'site_uuid': 'bb8e2c49-6ad4-4f94-9046-cf5115719cb5', 'role_uuid': '08c400af-3ddc-470f-9c9b-6bb6d1d1b9f4'}, + sql='UPDATE users SET sites = sites || %(role_uuid)s, site_roles = site_roles || %(site_uuid)s WHERE user_uuid=%(user_uuid)s RETURNING *;') +2025-08-23 17:05:24.720835 --- ERROR --- DatabaseError(message='malformed array literal: "ed4d4296-5b95-4845-80d4-1b9332c948a2"LINE 1: UPDATE users SET user_sites = user_sites || 'ed4d4296-5b95-4... ^DETAIL: Array value must start with "{" or dimension information.', + payload={'user_uuid': '9fbc9988-2e58-4495-b7a6-a90bb9163cce', 'site_uuid': 'ed4d4296-5b95-4845-80d4-1b9332c948a2', 'role_uuid': '75d88aad-3631-4f20-b4af-af84fc966a0e'}, + sql='UPDATE users SET user_sites = user_sites || %(site_uuid)s, user_roles = user_roles || %(role_uuid)s WHERE user_uuid=%(user_uuid)s RETURNING *;') +2025-08-23 17:08:46.646044 --- ERROR --- DatabaseError(message='relation "test_sites" does not existLINE 1: UPDATE test_sites SET default_zone = '121010a4-80f2-4e77-954... ^', + payload={'key': '9eea0818-071c-47d0-8545-d3fde380a84c', 'update': {'default_zone': '121010a4-80f2-4e77-9542-b5f2e08bca6e', 'default_auto_issue_location': '4c50a4b6-066c-4be2-9cd9-a642d6c022c5', 'default_primary_location': '4c50a4b6-066c-4be2-9cd9-a642d6c022c5'}}, + sql='UPDATE test_sites SET default_zone = %s, default_auto_issue_location = %s, default_primary_location = %s WHERE site_uuid=%s RETURNING *;') +2025-08-23 17:13:28.573831 --- ERROR --- DatabaseError(message='column "default_zone" of relation "sites" does not existLINE 1: UPDATE sites SET default_zone = 'b8c335a8-3813-43ff-857c-548... ^', + payload={'key': 'ff81d4f6-a21b-48cf-b2d9-510ca9f0185b', 'update': {'default_zone': 'b8c335a8-3813-43ff-857c-54870f9c8471', 'default_auto_issue_location': '8af88ddc-7c7a-4daa-a797-708aa47080fc', 'default_primary_location': '8af88ddc-7c7a-4daa-a797-708aa47080fc'}}, + sql='UPDATE sites SET default_zone = %s, default_auto_issue_location = %s, default_primary_location = %s WHERE site_uuid=%s RETURNING *;') +2025-08-23 17:17:18.404900 --- ERROR --- DatabaseError(message=''created_by'', + payload={'vendor_name': 'None', 'vendor_created_by': '3ba91bc5-277e-4601-abb2-a5b27245a1fd', 'vendor_address': '', 'vendor_phone_number': '', 'creation_date': datetime.datetime(2025, 8, 23, 17, 17, 18, 396948)}, + sql='INSERT INTO test_vendors(vendor_name, vendor_address, creation_date, created_by, phone_number) VALUES (%(vendor_name)s, %(vendor_address)s, %(creation_date)s, %(created_by)s, %(phone_number)s) RETURNING *;') +2025-08-23 17:18:26.766922 --- ERROR --- DatabaseError(message=''vendor_creation_date'', + payload={'vendor_name': 'None', 'vendor_created_by': 'd7f2db01-ccef-4318-8b0b-68e53da39c91', 'vendor_address': '', 'vendor_phone_number': '', 'creation_date': datetime.datetime(2025, 8, 23, 17, 18, 26, 756696)}, + sql='INSERT INTO test_vendors(vendor_name, vendor_address, vendor_creation_date, vendor_created_by, vendor_phone_number) VALUES (%(vendor_name)s, %(vendor_address)s, %(vendor_creation_date)s, %(vendor_created_by)s, %(vendor_phone_number)s) RETURNING *;') +2025-08-23 17:18:56.502889 --- ERROR --- DatabaseError(message='column "name" of relation "test_brands" does not existLINE 2: (name) ^', + payload={'name': 'None'}, + sql='INSERT INTO test_brands(name) VALUES (%(name)s) RETURNING *;') +2025-08-23 17:32:23.722383 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: ""LINE 1: SELECT * FROM roles WHERE role_site_uuid = ''; ^', + payload={'site_uuid': ''}, + sql='SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s;') +2025-08-23 17:33:03.401456 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: ""LINE 1: SELECT * FROM roles WHERE role_site_uuid = ''; ^', + payload={'site_uuid': ''}, + sql='SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s;') +2025-08-23 17:33:20.694744 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: ""LINE 1: SELECT * FROM roles WHERE role_site_uuid = ''::uuid; ^', + payload={'site_uuid': ''}, + sql='SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s::uuid;') +2025-08-23 17:35:11.954356 --- ERROR --- DatabaseError(message='relation "_roles" does not existLINE 1: WITH deleted_rows AS (DELETE FROM _roles WHERE id IN ('606f8... ^', + payload=['606f8ca9-dcbe-4ad2-8af6-931e23a6e191'], + sql='WITH deleted_rows AS (DELETE FROM _roles WHERE id IN (%s) RETURNING *) SELECT * FROM deleted_rows;') +2025-08-23 17:36:53.465231 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 1: ...ed_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNIN... ^', + payload=, + sql='WITH deleted_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNING *) SELECT * FROM deleted_rows;') +2025-08-23 17:37:59.387507 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 1: ...ed_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNIN... ^', + payload=, + sql='WITH deleted_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNING *) SELECT * FROM deleted_rows;') +2025-08-23 17:58:01.143255 --- ERROR --- DatabaseError(message='relation "logins" does not existLINE 1: SELECT * FROM logins WHERE id=1; ^', + payload=(1,), + sql='SELECT * FROM logins WHERE id=%s;') +2025-08-23 18:16:06.471922 --- ERROR --- DatabaseError(message='relation "logins" does not existLINE 1: SELECT * FROM logins WHERE id=1; ^', + payload=(1,), + sql='SELECT * FROM logins WHERE id=%s;') +2025-08-23 18:32:51.008106 --- ERROR --- DatabaseError(message=''key'', + payload={'user_name': 'jadowyne'}, + sql='SELECT * FROM users WHERE user_name = %(key)s') +2025-08-23 18:51:35.107602 --- ERROR --- DatabaseError(message='relation "_sites" does not existLINE 1: SELECT * FROM _sites WHERE site_uuid = '{'; ^', + payload={'key': '{'}, + sql='SELECT * FROM _sites WHERE site_uuid = %(key)s;') +2025-08-23 18:51:53.817104 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{' ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s') +2025-08-23 18:54:48.994574 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid') +2025-08-23 18:55:26.991072 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid') +2025-08-23 18:58:22.144591 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid') +2025-08-23 19:00:01.337678 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid') +2025-08-23 19:06:14.672815 --- ERROR --- DatabaseError(message='relation "_users" does not existLINE 1: SELECT * FROM _users WHERE user_uuid = '17488a77-e26f-4f1d-b... ^', + payload={'key': '17488a77-e26f-4f1d-bb7d-63beb1e83f6c'}, + sql='SELECT * FROM _users WHERE user_uuid = %(key)s::int;') +2025-08-23 19:14:45.195849 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid; ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid;') +2025-08-23 19:15:35.522433 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid; ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid;') +2025-08-23 19:16:40.684616 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid; ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid;') +2025-08-23 19:17:29.407048 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid; ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid;') +2025-08-23 19:17:53.262303 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "{"LINE 1: SELECT * FROM sites WHERE site_uuid = '{'::uuid; ^', + payload={'key': '{'}, + sql='SELECT * FROM sites WHERE site_uuid = %(key)s::uuid;') +2025-08-23 19:26:29.889584 --- ERROR --- DatabaseError(message='column mil.part_id does not existLINE 4: JOIN test_items mi ON mil.part_id = mi.id ^', + payload=['', 50, 0], + sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations mil JOIN test_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT item.id, item.description, item.item_name, sum_cte.total_sum as total_qoh, u.fullnameFROM test_items itemLEFT JOIN sum_cte ON item.id = sum_cte.idLEFT JOIN test_item_info item_info ON item.item_info_id = item_info.idLEFT JOIN units u ON item_info.uom = u.idWHERE item.search_string LIKE '%%' || %s || '%%' AND item.inactive IS falseORDER BY item.id ASCLIMIT %s OFFSET %s;') +2025-08-23 21:07:20.957692 --- ERROR --- DatabaseError(message='function genuuid_generate_v4() does not existLINE 2: unit_uuid UUID PRIMARY KEY DEFAULT genuuid_generate_v4()... ^HINT: No function matches the given name and argument types. You might need to add explicit type casts.', + payload=CREATE TABLE IF NOT EXISTS units ( + unit_uuid UUID PRIMARY KEY DEFAULT genuuid_generate_v4(), + unit_plural VARCHAR(32) NOT NULL, + unit_single VARCHAR(32) NOT NULL, + unit_fullname VARCHAR(255) NOT NULL, + unit_description TEXT DEFAULT '' NOT NULL, + unique(unit_plural), + unique(unit_single), + unique(unit_fullname) +);, + sql='units') +2025-08-23 22:02:01.201411 --- ERROR --- DatabaseError(message='column items_locations.quantity_on_hand does not existLINE 2: SELECT items.item_uuid, SUM(items_locations.quantity_o... ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item.id ASC'}, + sql='WITH sum_cte AS ( SELECT items.item_uuid, SUM(items_locations.quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations items_locations JOIN test_items items ON items_locations.item_uuid = items.item_uuid GROUP BY items.item_uuid )SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.fullnameFROM test_items itemsLEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuidLEFT JOIN test_item_info item_info ON items.item_uuid = item_info.item_uuidLEFT JOIN units ON item_info.uom = units.unit_uuidWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' AND items.item_is_inactive IS falseORDER BY item.id ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-23 22:03:01.184936 --- ERROR --- DatabaseError(message='column item_info.uom does not existLINE 12: LEFT JOIN units ON item_info.uom = units.unit_uuid ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item.id ASC'}, + sql='WITH sum_cte AS ( SELECT items.item_uuid, SUM(items_locations.item_quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations items_locations JOIN test_items items ON items_locations.item_uuid = items.item_uuid GROUP BY items.item_uuid )SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.fullnameFROM test_items itemsLEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuidLEFT JOIN test_item_info item_info ON items.item_uuid = item_info.item_uuidLEFT JOIN units ON item_info.uom = units.unit_uuidWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' AND items.item_is_inactive IS falseORDER BY item.id ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-23 22:03:15.916404 --- ERROR --- DatabaseError(message='column units.fullname does not existLINE 8: ...item_name, sum_cte.total_sum as quantity_on_hand, units.full... ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item.id ASC'}, + sql='WITH sum_cte AS ( SELECT items.item_uuid, SUM(items_locations.item_quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations items_locations JOIN test_items items ON items_locations.item_uuid = items.item_uuid GROUP BY items.item_uuid )SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.fullnameFROM test_items itemsLEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuidLEFT JOIN test_item_info item_info ON items.item_uuid = item_info.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' AND items.item_is_inactive IS falseORDER BY item.id ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-23 22:03:41.930847 --- ERROR --- DatabaseError(message='column items.item_is_inactive does not existLINE 14: AND items.item_is_inactive IS false ^HINT: Perhaps you meant to reference the column "items.item_inactive".', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item.id ASC'}, + sql='WITH sum_cte AS ( SELECT items.item_uuid, SUM(items_locations.item_quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations items_locations JOIN test_items items ON items_locations.item_uuid = items.item_uuid GROUP BY items.item_uuid )SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.unit_fullnameFROM test_items itemsLEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuidLEFT JOIN test_item_info item_info ON items.item_uuid = item_info.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' AND items.item_is_inactive IS falseORDER BY item.id ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-23 22:04:26.438958 --- ERROR --- DatabaseError(message='missing FROM-clause entry for table "item"LINE 15: ORDER BY item.id ASC ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item.id ASC'}, + sql='WITH sum_cte AS ( SELECT items.item_uuid, SUM(items_locations.item_quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations items_locations JOIN test_items items ON items_locations.item_uuid = items.item_uuid GROUP BY items.item_uuid )SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.unit_fullnameFROM test_items itemsLEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuidLEFT JOIN test_item_info item_info ON items.item_uuid = item_info.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' AND items.item_inactive IS falseORDER BY item.id ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-23 22:05:31.048675 --- ERROR --- DatabaseError(message='missing FROM-clause entry for table "items"LINE 1: SELECT COUNT(*) FROM test_items WHERE items.item_search_stri... ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'items.item_uuid ASC'}, + sql='WITH sum_cte AS ( SELECT items.item_uuid, SUM(items_locations.item_quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations items_locations JOIN test_items items ON items_locations.item_uuid = items.item_uuid GROUP BY items.item_uuid )SELECT items.item_uuid, items.item_description, items.item_name, sum_cte.total_sum as quantity_on_hand, units.unit_fullnameFROM test_items itemsLEFT JOIN sum_cte ON items.item_uuid = sum_cte.item_uuidLEFT JOIN test_item_info item_info ON items.item_uuid = item_info.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' AND items.item_inactive IS falseORDER BY items.item_uuid ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-24 09:32:31.177059 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 1: ...ed_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNIN... ^', + payload=[], + sql='WITH deleted_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNING *) SELECT * FROM deleted_rows;') +2025-08-24 09:35:52.897988 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 13: ); ^', + payload=CREATE TABLE IF NOT EXISTS test_items( + item_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + item_created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + item_updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + item_name VARCHAR(255) NOT NULL, + item_description TEXT DEFAULT '' NOT NULL, + item_tags TEXT [] DEFAULT '{}' NOT NULL, + item_links JSONB DEFAULT '{}' NOT NULL, + item_brand_uuid UUID DEFAULT NULL, + item_category VARCHAR(255) NOT NULL, + item_search_string TEXT DEFAULT '' NOT NULL, + item_inactive BOOLEAN DEFAULT false NOT NULL, +); +, + sql='items') +2025-08-24 10:54:46.217196 --- ERROR --- DatabaseError(message=''barcode'', + payload={'item_uuid': 'b97e8d84-b302-4739-8921-58802ffcaafb', 'item_uom': None, 'item_packaging': '', 'item_uom_quantity': 1.0, 'item_cost': 0.0, 'item_safety_stock': 0.0, 'item_lead_time_days': 0.0, 'item_ai_pick': False, 'item_prefixes': '{}'}, + sql='INSERT INTO test_item_info(barcode, packaging, uom_quantity, uom, cost, safety_stock, lead_time_days, ai_pick, prefixes) VALUES (%(barcode)s, %(packaging)s, %(uom_quantity)s, %(uom)s, %(cost)s, %(safety_stock)s, %(lead_time_days)s, %(ai_pick)s, %(prefixes)s) RETURNING *;') +2025-08-24 11:04:44.303675 --- ERROR --- DatabaseError(message=''barcode'', + payload={'item_uuid': 'eb84e3cc-9055-4ff0-9a7c-d078e6e8816d', 'item_primary_location': None, 'item_primary_zone': None, 'item_auto_issue_location': None, 'item_auto_issue_zone': None}, + sql='INSERT INTO test_logistics_info(barcode, primary_location, primary_zone, auto_issue_location, auto_issue_zone) VALUES (%(barcode)s, %(primary_location)s, %(primary_zone)s, %(auto_issue_location)s, %(auto_issue_zone)s) RETURNING *;') +2025-08-24 11:15:31.150260 --- ERROR --- DatabaseError(message=''item_uuid'', + payload={'item_food_groups': '{}', 'item_ingredients': '{}', 'item_nutrients': '{}', 'item_expires': False, 'item_default_expiration': 0.0}, + sql='INSERT INTO test_food_info( item_uuid, item_food_groups, item_ingredients, item_nutrients, item_expires, item_default_expiration) VALUES ( %(item_uuid)s, %(item_food_groups)s, %(item_ingredients)s, %(item_nutrients)s, %(item_expires)s, %(item_default_expiration)s) RETURNING *;') +2025-08-24 11:17:54.928279 --- ERROR --- DatabaseError(message=''item_uuid'', + payload={'item_food_groups': '{}', 'item_ingredients': '{}', 'item_nutrients': '{}', 'item_expires': False, 'item_default_expiration': 0.0}, + sql='INSERT INTO test_food_info( item_uuid, item_food_groups, item_ingredients, item_nutrients, item_expires, item_default_expiration) VALUES ( %(item_uuid)s, %(item_food_groups)s, %(item_ingredients)s, %(item_nutrients)s, %(item_expires)s, %(item_default_expiration)s) RETURNING *;') +2025-08-24 11:30:52.050270 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: "17488a77-e26f-4f1d-bb7d-63beb1e83f6c"LINE 15: '17488a77-e26f-4f1d-bb7d-63beb1e83f6c', ^', + payload={'item_uuid': '48c0027e-aafd-44cb-b153-76a2f8516313', 'transaction_created_by': '17488a77-e26f-4f1d-bb7d-63beb1e83f6c', 'transaction_name': 'Item Created', 'transaction_type': 'SYSTEM', 'transaction_quantity': 0.0, 'transaction_description': '', 'transaction_cost': 0.0, 'transaction_data': '{}', 'transaction_created_at': datetime.datetime(2025, 8, 24, 11, 30, 52, 38752)}, + sql='INSERT INTO test_transactions( item_uuid, transaction_created_by, transaction_name, transaction_type, transaction_created_at, transaction_quantity, transaction_description, transaction_cost, transaction_data) VALUES ( %(item_uuid)s, %(transaction_created_by)s, %(transaction_name)s, %(transaction_type)s, %(transaction_created_at)s, %(transaction_quantity)s, %(transaction_description)s, %(transaction_cost)s, %(transaction_data)s)RETURNING *;') +2025-08-24 11:32:18.969254 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 1: ...ed_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNIN... ^', + payload=[], + sql='WITH deleted_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNING *) SELECT * FROM deleted_rows;') +2025-08-24 11:33:06.844922 --- ERROR --- DatabaseError(message='insert or update on table "test_logistics_info" violates foreign key constraint "test_logistics_info_item_primary_location_fkey"DETAIL: Key (item_primary_location)=(a45d40cf-bcb4-46a5-a4e6-a835c63a3dc5) is not present in table "test_locations".', + payload={'item_uuid': 'f9b9ebdb-9f24-48c9-ac47-380cae1ecf33', 'item_primary_location': 'a45d40cf-bcb4-46a5-a4e6-a835c63a3dc5', 'item_primary_zone': '6ac2b642-27b1-4622-a6a2-031389b25329', 'item_auto_issue_location': 'a45d40cf-bcb4-46a5-a4e6-a835c63a3dc5', 'item_auto_issue_zone': '6ac2b642-27b1-4622-a6a2-031389b25329'}, + sql='INSERT INTO test_logistics_info( item_uuid, item_primary_location, item_primary_zone, item_auto_issue_location, item_auto_issue_zone) VALUES ( %(item_uuid)s, %(item_primary_location)s, %(item_primary_zone)s, %(item_auto_issue_location)s, %(item_auto_issue_zone)s) RETURNING *;') +2025-08-24 11:37:18.459039 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "item_name_unique"DETAIL: Key (item_name)=(test) already exists.', + payload={'item_category': 'FOOD', 'item_name': 'test', 'item_description': '', 'item_tags': '{}', 'item_links': '{}', 'item_brand_uuid': None, 'item_search_string': '', 'item_inactive': False, 'item_created_at': datetime.datetime(2025, 8, 24, 11, 37, 18, 449055), 'item_updated_at': datetime.datetime(2025, 8, 24, 11, 37, 18, 449064)}, + sql='INSERT INTO test_items( item_category, item_name, item_created_at, item_updated_at, item_description, item_tags, item_links, item_brand_uuid, item_search_string, item_inactive ) VALUES( %(item_category)s, %(item_name)s, %(item_created_at)s, %(item_updated_at)s, %(item_description)s, %(item_tags)s, %(item_links)s, %(item_brand_uuid)s, %(item_search_string)s, %(item_inactive)s) RETURNING *;') +2025-08-24 17:17:35.777642 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "item_name_unique"DETAIL: Key (item_name)=(test) already exists.', + payload={'item_category': 'FOOD', 'item_name': 'test', 'item_description': '', 'item_tags': '{}', 'item_links': '{}', 'item_brand_uuid': None, 'item_search_string': '', 'item_inactive': False, 'item_created_at': datetime.datetime(2025, 8, 24, 17, 17, 35, 766067), 'item_updated_at': datetime.datetime(2025, 8, 24, 17, 17, 35, 766077)}, + sql='INSERT INTO test_items( item_category, item_name, item_created_at, item_updated_at, item_description, item_tags, item_links, item_brand_uuid, item_search_string, item_inactive ) VALUES( %(item_category)s, %(item_name)s, %(item_created_at)s, %(item_updated_at)s, %(item_description)s, %(item_tags)s, %(item_links)s, %(item_brand_uuid)s, %(item_search_string)s, %(item_inactive)s) RETURNING *;') +2025-08-24 17:22:46.024851 --- ERROR --- DatabaseError(message='relation "test_vendors" does not exist', + payload=CREATE TABLE IF NOT EXISTS test_cost_layers ( + item_location_uuid UUID REFERENCES test_item_locations(item_location_uuid) ON DELETE SET NULL, + layer_aquisition_date TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + layer_quantity FLOAT8 DEFAULT 0.00 NOT NULL, + layer_cost FLOAT8 DEFAULT 0.00 NOT NULL, + layer_currency_type VARCHAR(16) DEFAULT 'USD' NOT NULL, + layer_expires TIMESTAMP DEFAULT NULL, + layer_vendor UUID DEFAULT NULL REFERENCES test_vendors(vendor_uuid) ON DELETE SET NULL +);, + sql='cost_layers') +2025-08-24 17:55:34.739333 --- ERROR --- DatabaseError(message='insert or update on table "test_logistics_info" violates foreign key constraint "test_logistics_info_item_primary_location_fkey"DETAIL: Key (item_primary_location)=(708da889-5b4d-46ae-adc5-36e4fe512bc0) is not present in table "test_locations".', + payload={'item_uuid': 'b047a727-a710-48bb-a51d-d82b552437ba', 'item_primary_location': '708da889-5b4d-46ae-adc5-36e4fe512bc0', 'item_primary_zone': 'ffba7d9b-de2c-4ec2-8fbf-89169d89e27f', 'item_auto_issue_location': '708da889-5b4d-46ae-adc5-36e4fe512bc0', 'item_auto_issue_zone': 'ffba7d9b-de2c-4ec2-8fbf-89169d89e27f'}, + sql='INSERT INTO test_logistics_info( item_uuid, item_primary_location, item_primary_zone, item_auto_issue_location, item_auto_issue_zone) VALUES ( %(item_uuid)s, %(item_primary_location)s, %(item_primary_zone)s, %(item_auto_issue_location)s, %(item_auto_issue_zone)s) RETURNING *;') +2025-08-24 17:58:42.943646 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "test_items_item_name_key"DETAIL: Key (item_name)=(test2) already exists.', + payload={'item_category': 'FOOD', 'item_name': 'test2', 'item_description': '', 'item_tags': '{}', 'item_links': '{}', 'item_brand_uuid': None, 'item_search_string': '', 'item_inactive': False, 'item_created_at': datetime.datetime(2025, 8, 24, 17, 58, 42, 933561), 'item_updated_at': datetime.datetime(2025, 8, 24, 17, 58, 42, 933573)}, + sql='INSERT INTO test_items( item_category, item_name, item_created_at, item_updated_at, item_description, item_tags, item_links, item_brand_uuid, item_search_string, item_inactive ) VALUES( %(item_category)s, %(item_name)s, %(item_created_at)s, %(item_updated_at)s, %(item_description)s, %(item_tags)s, %(item_links)s, %(item_brand_uuid)s, %(item_search_string)s, %(item_inactive)s) RETURNING *;') +2025-08-24 19:02:40.720756 --- ERROR --- DatabaseError(message='column item_info.id does not existLINE 2: LEFT JOIN test_item_info item_info ON item_info.id = item.it... ^', + payload=('', 50, 0), + sql='SELECT item.id, item.barcode, item.item_name, u.id as uom FROM test_items itemLEFT JOIN test_item_info item_info ON item_info.id = item.item_info_idLEFT JOIN units u ON u.id = item_info.uomWHERE item.search_string LIKE '%%' || %s || '%%' ANd item.inactive IS falseLIMIT %s OFFSET %s;') +2025-08-24 19:36:38.102904 --- ERROR --- DatabaseError(message='column items.item_info_id does not existLINE 2: ...test_item_info item_info ON item_info.item_uuid = items.item... ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item_name ASC'}, + sql='SELECT items.item_uuid, items.item_name, u.id as uom FROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_info_idLEFT JOIN units ON units.unit_uuid = item_info.uomWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' ANd items.item_inactive IS falseORDER BY item_name ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-24 19:37:30.354814 --- ERROR --- DatabaseError(message='column items.item_info_id does not existLINE 2: ...test_item_info item_info ON item_info.item_uuid = items.item... ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item_name ASC'}, + sql='SELECT items.item_uuid, items.item_name, u.id as uom FROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_info_idLEFT JOIN units ON units.unit_uuid = item_info.uomWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' ANd items.item_inactive IS falseORDER BY item_name ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-24 19:38:02.462645 --- ERROR --- DatabaseError(message='column item_info.uom does not existLINE 3: LEFT JOIN units ON units.unit_uuid = item_info.uom ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item_name ASC'}, + sql='SELECT items.item_uuid, items.item_name, u.id as uom FROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON units.unit_uuid = item_info.uomWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' ANd items.item_inactive IS falseORDER BY item_name ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-24 19:39:04.082053 --- ERROR --- DatabaseError(message='missing FROM-clause entry for table "u"LINE 1: SELECT items.item_uuid, items.item_name, u.id as uom FROM te... ^', + payload={'search_string': '', 'limit': 50, 'offset': 0, 'sort_order': 'item_name ASC'}, + sql='SELECT items.item_uuid, items.item_name, u.id as uom FROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON units.unit_uuid = item_info.item_uomWHERE items.item_search_string LIKE '%%' || %(search_string)s || '%%' ANd items.item_inactive IS falseORDER BY item_name ASCLIMIT %(limit)s OFFSET %(offset)s;') +2025-08-24 20:03:07.903773 --- ERROR --- DatabaseError(message='column item_locations.quantity_on_hand does not existLINE 3: SELECT item_locations.quantity_on_hand FROM test... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations AS ( SELECT item_locations.quantity_on_hand FROM test_item_locations item_locations WHERE item_locations = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, (SELECT COALESCE(SUM(ils.quantity_on_hand), 0.00) FROM cte_item_locations ils) AS item_locationsFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);/*I needitem_uuiditem_namequantity on handunit_of_measure_fullnamezoneslocationscost*/') +2025-08-24 20:04:09.838089 --- ERROR --- DatabaseError(message='syntax error at or near "SELECT"LINE 9: (SELECT COALESCE(SUM(ils.quantity_on_hand), 0.00) FROM c... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations AS ( SELECT item_locations.quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost (SELECT COALESCE(SUM(ils.quantity_on_hand), 0.00) FROM cte_item_locations ils) AS item_locationsFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-24 20:04:39.166177 --- ERROR --- DatabaseError(message='syntax error at or near "SELECT"LINE 9: (SELECT COALESCE(SUM(ils.qoh), 0.00) FROM cte_item_locat... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations AS ( SELECT item_locations.quantity_on_hand AS qoh FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost (SELECT COALESCE(SUM(ils.qoh), 0.00) FROM cte_item_locations ils) AS item_locationsFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-24 20:04:50.353200 --- ERROR --- DatabaseError(message='syntax error at or near "SELECT"LINE 9: (SELECT COALESCE(SUM(ils.qoh), '0.00') FROM cte_item_loc... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations AS ( SELECT item_locations.quantity_on_hand AS qoh FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost (SELECT COALESCE(SUM(ils.qoh), '0.00') FROM cte_item_locations ils) AS item_locationsFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-24 20:08:55.657999 --- ERROR --- DatabaseError(message='syntax error at or near "SELECT"LINE 10: (SELECT COALESCE(ils.quantity_on_hand, 0.00) FROM cte_it... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations AS ( SELECT SUM(item_locations.quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost (SELECT COALESCE(ils.quantity_on_hand, 0.00) FROM cte_item_locations ils) AS item_quantity_on_handFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-24 20:09:07.438061 --- ERROR --- DatabaseError(message='column item_locations.quantity_on_hand does not existLINE 3: SELECT SUM(item_locations.quantity_on_hand) AS q... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations AS ( SELECT SUM(item_locations.quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost, (SELECT COALESCE(ils.quantity_on_hand, 0.00) FROM cte_item_locations ils) AS item_quantity_on_handFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-24 20:18:14.570378 --- ERROR --- DatabaseError(message='syntax error at or near "SELECT"LINE 8: SELECT ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations_qty AS ( SELECT SUM(item_locations.item_quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) ),SELECT items.item_uuid, items.item_name, item_info.item_cost, (SELECT COALESCE(ilsq.quantity_on_hand, 0.00) FROM cte_item_locations_qty ilsq) AS item_quantity_on_handFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-24 20:46:50.045541 --- ERROR --- DatabaseError(message='syntax error at or near "AS"LINE 8: SELECT * AS quantity_on_hand ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations_qty AS ( SELECT SUM(item_locations.item_quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) ), cte_item_locations AS ( SELECT * AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost, (SELECT COALESCE(ilsq.quantity_on_hand, 0.00) FROM cte_item_locations_qty ilsq) AS item_quantity_on_hand, COALESCE(units.unit_fullname, 'ERROR') AS unit_fullname, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locationsFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-26 17:02:46.543504 --- ERROR --- DatabaseError(message='relation "log_info" does not existLINE 20: ...primary_zone, log_info.item_primary_location FROM log_info) ... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations_qty AS ( SELECT SUM(item_locations.item_quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) ), cte_item_locations AS ( SELECT * FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost, (SELECT COALESCE(ilsq.quantity_on_hand, 0.00) FROM cte_item_locations_qty ilsq) AS item_quantity_on_hand, COALESCE(units.unit_fullname, 'ERROR') AS unit_fullname, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locations, (SELECT log_info.item_primary_zone, log_info.item_primary_location FROM log_info) AS logistics_infoFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidLEFT JOIN test_logistics_info log_info ON log_info.item_uuid = items.item_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-26 17:07:28.743050 --- ERROR --- DatabaseError(message='type "row_to_json" does not existLINE 20: (SELECT COALESCE(row_to_json(zones.*) '{}') FROM test_zo... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations_qty AS ( SELECT SUM(item_locations.item_quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) ), cte_item_locations AS ( SELECT * FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost, (SELECT COALESCE(ilsq.quantity_on_hand, 0.00) FROM cte_item_locations_qty ilsq) AS item_quantity_on_hand, COALESCE(units.unit_fullname, 'ERROR') AS unit_fullname, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locations, (SELECT COALESCE(row_to_json(zones.*) '{}') FROM test_zones zones WHERE zones.zone_uuid = log_info.item_primary_zone), log_info.item_primary_zone AS primary_zone, log_info.item_primary_location AS primary_locationFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidLEFT JOIN test_logistics_info log_info ON log_info.item_uuid = items.item_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') +2025-08-26 17:09:06.017514 --- ERROR --- DatabaseError(message='missing FROM-clause entry for table "zones"LINE 21: ...ons.*), '{}') FROM test_locations locations WHERE zones.loca... ^', + payload={}, + sql='WITH parameters AS (SELECT %(item_uuid)s::uuid AS item_uuid), cte_item_locations_qty AS ( SELECT SUM(item_locations.item_quantity_on_hand) AS quantity_on_hand FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) ), cte_item_locations AS ( SELECT * FROM test_item_locations item_locations WHERE item_locations.item_uuid = (SELECT item_uuid FROM parameters) )SELECT items.item_uuid, items.item_name, item_info.item_cost, (SELECT COALESCE(ilsq.quantity_on_hand, 0.00) FROM cte_item_locations_qty ilsq) AS item_quantity_on_hand, COALESCE(units.unit_fullname, 'ERROR') AS unit_fullname, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locations, (SELECT COALESCE(row_to_json(zones.*), '{}') FROM test_zones zones WHERE zones.zone_uuid = log_info.item_primary_zone) AS primary_zone, (SELECT COALESCE(row_to_json(locations.*), '{}') FROM test_locations locations WHERE zones.location_uuid = log_info.item_primary_location) AS primary_locationFROM test_items itemsLEFT JOIN test_item_info item_info ON item_info.item_uuid = items.item_uuidLEFT JOIN units ON item_info.item_uom = units.unit_uuidLEFT JOIN test_logistics_info log_info ON log_info.item_uuid = items.item_uuidWHERE items.item_uuid = (SELECT item_uuid FROM parameters);') \ No newline at end of file diff --git a/logs/process.log b/logs/process.log index 088af59..4ef7476 100644 --- a/logs/process.log +++ b/logs/process.log @@ -1054,3 +1054,139 @@ 2025-08-21 15:17:18.089331 --- INFO --- sku_prefix DROPPED! 2025-08-21 15:17:18.095712 --- INFO --- barcodes DROPPED! 2025-08-21 15:17:18.102641 --- INFO --- plan_events DROPPED! +2025-08-23 15:47:56.200244 --- INFO --- Admin User Created! +2025-08-23 15:47:56.220267 --- ERROR --- DatabaseError(message='column "name" of relation "test2_zones" does not existLINE 2: (name, description) ^', payload=('all', ''), sql='INSERT INTO test2_zones(name, description) VALUES (%s, %s) RETURNING *;') +2025-08-23 15:49:56.089291 --- INFO --- Admin User Created! +2025-08-23 15:49:56.099841 --- ERROR --- DatabaseError(message='column "name" of relation "test2_zones" does not existLINE 2: (name, description) ^', payload=('kitchen', ''), sql='INSERT INTO test2_zones(name, description) VALUES (%s, %s) RETURNING *;') +2025-08-23 15:50:05.762721 --- INFO --- Admin User Created! +2025-08-23 15:50:05.776618 --- ERROR --- DatabaseError(message=''name'', payload={'zone_name': 'kitchen', 'zone_description': ''}, sql='INSERT INTO test2_zones(name, description) VALUES (%(name)s, %(description)s) RETURNING *;') +2025-08-23 15:50:43.707065 --- INFO --- Admin User Created! +2025-08-23 15:50:43.721469 --- ERROR --- DatabaseError(message=''name'', payload={'zone_name': 'kitchen', 'zone_description': ''}, sql='INSERT INTO test2_zones(zone_name, zone_description) VALUES (%(name)s, %(description)s) RETURNING *;') +2025-08-23 15:51:05.792640 --- INFO --- Admin User Created! +2025-08-23 15:51:05.802795 --- ERROR --- 'id' +2025-08-23 15:52:59.368555 --- INFO --- Admin User Created! +2025-08-23 15:54:32.013319 --- INFO --- Admin User Created! +2025-08-23 16:24:14.240500 --- ERROR --- DatabaseError(message='column "username" named in key does not existLINE 14: UNIQUE(username), ^', payload=CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites INTEGER [] DEFAULT '{}' NOT NULL, + user_roles INTEGER [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) NOT NULL, + user_profile_pic_url VARCHAR(255) NOT NULL, + user_login_type VARCHAR(32) NOT NULL, + UNIQUE(username), + CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + +, sql='users') +2025-08-23 16:24:27.949693 --- ERROR --- DatabaseError(message='function uuid_generate_v4() does not existLINE 2: user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NO... ^HINT: No function matches the given name and argument types. You might need to add explicit type casts.', payload=CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites INTEGER [] DEFAULT '{}' NOT NULL, + user_roles INTEGER [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) NOT NULL, + user_profile_pic_url VARCHAR(255) NOT NULL, + user_login_type VARCHAR(32) NOT NULL, + UNIQUE(user_name), + CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + +, sql='users') +2025-08-23 16:26:32.149906 --- ERROR --- permission denied to create extension "uuid-ossp" +HINT: Must have CREATE privilege on current database to create this extension. + +2025-08-23 16:29:01.750728 --- ERROR --- DatabaseError(message='column "email" does not existLINE 15: CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z... ^', payload=CREATE TABLE IF NOT EXISTS users( + user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_name VARCHAR(255) NOT NULL, + user_password VARCHAR(255) DEFAULT NULL, + user_email VARCHAR(255) UNIQUE NOT NULL, + user_favorites JSONB DEFAULT '{}' NOT NULL, + user_sites INTEGER [] DEFAULT '{}' NOT NULL, + user_roles INTEGER [] DEFAULT '{}' NOT NULL, + user_is_system_admin BOOLEAN DEFAULT FALSE NOT NULL, + user_flags JSONB DEFAULT '{}' NOT NULL, + user_row_type VARCHAR(50) NOT NULL, + user_profile_pic_url VARCHAR(255) NOT NULL, + user_login_type VARCHAR(32) NOT NULL, + UNIQUE(user_name), + CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') +); + +, sql='users') +2025-08-23 16:29:12.520195 --- ERROR --- column "username" does not exist +LINE 1: ...est', 'jadowyne@outlook.com', 'user') ON CONFLICT (username)... + ^ +HINT: Perhaps you meant to reference the column "users.user_name" or the column "excluded.user_name". + +2025-08-23 16:30:01.154597 --- ERROR --- null value in column "user_profile_pic_url" of relation "users" violates not-null constraint +DETAIL: Failing row contains (6527c4a4-d1b0-4c62-a1ae-9dcf5d266bca, jadowyne, test, jadowyne@outlook.com, {}, {}, {}, f, {}, user, null, null). + +2025-08-23 16:31:17.437628 --- INFO --- Admin User Created! +2025-08-23 16:31:17.441506 --- ERROR --- module 'application.database_payloads' has no attribute 'StePayload' +2025-08-23 16:32:59.133569 --- INFO --- Admin User Created! +2025-08-23 16:32:59.137731 --- ERROR --- 'Payload' object has no attribute 'payload' +2025-08-23 16:37:08.370102 --- INFO --- Admin User Created! +2025-08-23 16:37:08.383751 --- ERROR --- DatabaseError(message=''site_created_on'', payload={'site_name': 'test', 'site_description': 'Test Site', 'site_created_by': '1a5f4974-45a4-41c2-951b-d391eb19dbd5', 'site_default_zone_uuid': None, 'site_default_auto_issue_location_uuid': None, 'site_default_primary_location_uuid': None, 'site_flags': '{}', 'creation_date': datetime.datetime(2025, 8, 23, 16, 37, 8, 370249)}, sql='INSERT INTO sites(site_name, site_description, site_created_by, site_default_zone_uuid, site_default_auto_issue_location_uuid,site_default_primary_location_uuid, site_created_on, site_flags) VALUES (%(site_name)s, %(site_description)s, %(site_created_by)s, %(site_default_zone_uuid)s, %(site_default_auto_issue_location_uuid)s, %(site_default_primary_location_uuid)s, %(site_created_on)s, %(site_flags)s) RETURNING *;') +2025-08-23 16:38:50.617369 --- INFO --- Admin User Created! +2025-08-23 16:38:50.639831 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: "00210dd7-44c1-41e0-8272-3583eb85e6fc"LINE 4: VALUES ('test', 'Test Site', '00210dd7-44c1-41e0-8272-3583eb... ^', payload={'site_name': 'test', 'site_description': 'Test Site', 'site_created_by': '00210dd7-44c1-41e0-8272-3583eb85e6fc', 'site_default_zone_uuid': None, 'site_default_auto_issue_location_uuid': None, 'site_default_primary_location_uuid': None, 'site_flags': '{}', 'site_created_on': datetime.datetime(2025, 8, 23, 16, 38, 50, 617486)}, sql='INSERT INTO sites(site_name, site_description, site_created_by, site_default_zone_uuid, site_default_auto_issue_location_uuid,site_default_primary_location_uuid, site_created_on, site_flags) VALUES (%(site_name)s, %(site_description)s, %(site_created_by)s, %(site_default_zone_uuid)s, %(site_default_auto_issue_location_uuid)s, %(site_default_primary_location_uuid)s, %(site_created_on)s, %(site_flags)s) RETURNING *;') +2025-08-23 16:39:36.427017 --- INFO --- Admin User Created! +2025-08-23 16:39:36.433239 --- ERROR --- 'id' +2025-08-23 16:48:55.484197 --- INFO --- Admin User Created! +2025-08-23 16:48:55.492650 --- ERROR --- 'id' +2025-08-23 16:58:58.514948 --- ERROR --- 'id' +2025-08-23 17:04:20.166776 --- ERROR --- DatabaseError(message='relation "logins" does not existLINE 1: UPDATE logins SET sites = sites || '5c850d2d-ee69-4ac4-bfd5-... ^', payload={'user_uuid': '50743800-3079-455e-a670-5f7877788dec', 'site_uuid': 'b25fffee-4cef-4709-8715-db530e3c9384', 'role_uuid': '5c850d2d-ee69-4ac4-bfd5-06e7c0975ca9'}, sql='UPDATE logins SET sites = sites || %(role_uuid)s, site_roles = site_roles || %(site_uuid)s WHERE user_uuid=%(user_uuid)s RETURNING *;') +2025-08-23 17:04:40.007890 --- ERROR --- DatabaseError(message='column "sites" does not existLINE 1: UPDATE users SET sites = sites || '08c400af-3ddc-470f-9c9b-6... ^', payload={'user_uuid': 'c5bd7fa0-1fcb-4191-8a55-bf41fd758526', 'site_uuid': 'bb8e2c49-6ad4-4f94-9046-cf5115719cb5', 'role_uuid': '08c400af-3ddc-470f-9c9b-6bb6d1d1b9f4'}, sql='UPDATE users SET sites = sites || %(role_uuid)s, site_roles = site_roles || %(site_uuid)s WHERE user_uuid=%(user_uuid)s RETURNING *;') +2025-08-23 17:05:24.724897 --- ERROR --- DatabaseError(message='malformed array literal: "ed4d4296-5b95-4845-80d4-1b9332c948a2"LINE 1: UPDATE users SET user_sites = user_sites || 'ed4d4296-5b95-4... ^DETAIL: Array value must start with "{" or dimension information.', payload={'user_uuid': '9fbc9988-2e58-4495-b7a6-a90bb9163cce', 'site_uuid': 'ed4d4296-5b95-4845-80d4-1b9332c948a2', 'role_uuid': '75d88aad-3631-4f20-b4af-af84fc966a0e'}, sql='UPDATE users SET user_sites = user_sites || %(site_uuid)s, user_roles = user_roles || %(role_uuid)s WHERE user_uuid=%(user_uuid)s RETURNING *;') +2025-08-23 17:08:18.737775 --- ERROR --- 'id' +2025-08-23 17:08:37.076305 --- ERROR --- 'site_namm' +2025-08-23 17:08:46.649822 --- ERROR --- DatabaseError(message='relation "test_sites" does not existLINE 1: UPDATE test_sites SET default_zone = '121010a4-80f2-4e77-954... ^', payload={'key': '9eea0818-071c-47d0-8545-d3fde380a84c', 'update': {'default_zone': '121010a4-80f2-4e77-9542-b5f2e08bca6e', 'default_auto_issue_location': '4c50a4b6-066c-4be2-9cd9-a642d6c022c5', 'default_primary_location': '4c50a4b6-066c-4be2-9cd9-a642d6c022c5'}}, sql='UPDATE test_sites SET default_zone = %s, default_auto_issue_location = %s, default_primary_location = %s WHERE site_uuid=%s RETURNING *;') +2025-08-23 17:11:26.904676 --- ERROR --- type object 'BaseModel' has no attribute 'updateStringFactory' +2025-08-23 17:12:17.236998 --- ERROR --- 'str' object has no attribute 'table_name' +2025-08-23 17:12:43.694531 --- ERROR --- string indices must be integers, not 'str' +2025-08-23 17:13:28.577340 --- ERROR --- DatabaseError(message='column "default_zone" of relation "sites" does not existLINE 1: UPDATE sites SET default_zone = 'b8c335a8-3813-43ff-857c-548... ^', payload={'key': 'ff81d4f6-a21b-48cf-b2d9-510ca9f0185b', 'update': {'default_zone': 'b8c335a8-3813-43ff-857c-54870f9c8471', 'default_auto_issue_location': '8af88ddc-7c7a-4daa-a797-708aa47080fc', 'default_primary_location': '8af88ddc-7c7a-4daa-a797-708aa47080fc'}}, sql='UPDATE sites SET default_zone = %s, default_auto_issue_location = %s, default_primary_location = %s WHERE site_uuid=%s RETURNING *;') +2025-08-23 17:17:18.408708 --- ERROR --- DatabaseError(message=''created_by'', payload={'vendor_name': 'None', 'vendor_created_by': '3ba91bc5-277e-4601-abb2-a5b27245a1fd', 'vendor_address': '', 'vendor_phone_number': '', 'creation_date': datetime.datetime(2025, 8, 23, 17, 17, 18, 396948)}, sql='INSERT INTO test_vendors(vendor_name, vendor_address, creation_date, created_by, phone_number) VALUES (%(vendor_name)s, %(vendor_address)s, %(creation_date)s, %(created_by)s, %(phone_number)s) RETURNING *;') +2025-08-23 17:18:26.770976 --- ERROR --- DatabaseError(message=''vendor_creation_date'', payload={'vendor_name': 'None', 'vendor_created_by': 'd7f2db01-ccef-4318-8b0b-68e53da39c91', 'vendor_address': '', 'vendor_phone_number': '', 'creation_date': datetime.datetime(2025, 8, 23, 17, 18, 26, 756696)}, sql='INSERT INTO test_vendors(vendor_name, vendor_address, vendor_creation_date, vendor_created_by, vendor_phone_number) VALUES (%(vendor_name)s, %(vendor_address)s, %(vendor_creation_date)s, %(vendor_created_by)s, %(vendor_phone_number)s) RETURNING *;') +2025-08-23 17:18:56.506382 --- ERROR --- DatabaseError(message='column "name" of relation "test_brands" does not existLINE 2: (name) ^', payload={'name': 'None'}, sql='INSERT INTO test_brands(name) VALUES (%(name)s) RETURNING *;') +2025-08-23 17:32:23.726444 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: ""LINE 1: SELECT * FROM roles WHERE role_site_uuid = ''; ^', payload={'site_uuid': ''}, sql='SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s;') +2025-08-23 17:33:03.405287 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: ""LINE 1: SELECT * FROM roles WHERE role_site_uuid = ''; ^', payload={'site_uuid': ''}, sql='SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s;') +2025-08-23 17:33:20.698805 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: ""LINE 1: SELECT * FROM roles WHERE role_site_uuid = ''::uuid; ^', payload={'site_uuid': ''}, sql='SELECT * FROM roles WHERE role_site_uuid = %(site_uuid)s::uuid;') +2025-08-23 17:34:16.123751 --- ERROR --- 'id' +2025-08-23 17:34:21.617218 --- ERROR --- 'id' +2025-08-23 17:35:11.957920 --- ERROR --- DatabaseError(message='relation "_roles" does not existLINE 1: WITH deleted_rows AS (DELETE FROM _roles WHERE id IN ('606f8... ^', payload=['606f8ca9-dcbe-4ad2-8af6-931e23a6e191'], sql='WITH deleted_rows AS (DELETE FROM _roles WHERE id IN (%s) RETURNING *) SELECT * FROM deleted_rows;') +2025-08-23 17:36:53.469812 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 1: ...ed_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNIN... ^', payload=, sql='WITH deleted_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNING *) SELECT * FROM deleted_rows;') +2025-08-23 17:37:59.391466 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 1: ...ed_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNIN... ^', payload=, sql='WITH deleted_rows AS (DELETE FROM roles WHERE role_uuid IN () RETURNING *) SELECT * FROM deleted_rows;') +2025-08-23 17:39:20.977935 --- ERROR --- 'id' +2025-08-23 17:41:05.777447 --- ERROR --- 'id' +2025-08-24 09:35:52.902187 --- ERROR --- DatabaseError(message='syntax error at or near ")"LINE 13: ); ^', payload=CREATE TABLE IF NOT EXISTS test_items( + item_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + item_created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + item_updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + item_name VARCHAR(255) NOT NULL, + item_description TEXT DEFAULT '' NOT NULL, + item_tags TEXT [] DEFAULT '{}' NOT NULL, + item_links JSONB DEFAULT '{}' NOT NULL, + item_brand_uuid UUID DEFAULT NULL, + item_category VARCHAR(255) NOT NULL, + item_search_string TEXT DEFAULT '' NOT NULL, + item_inactive BOOLEAN DEFAULT false NOT NULL, +); +, sql='items') +2025-08-24 17:22:46.028941 --- ERROR --- DatabaseError(message='relation "test_vendors" does not exist', payload=CREATE TABLE IF NOT EXISTS test_cost_layers ( + item_location_uuid UUID REFERENCES test_item_locations(item_location_uuid) ON DELETE SET NULL, + layer_aquisition_date TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + layer_quantity FLOAT8 DEFAULT 0.00 NOT NULL, + layer_cost FLOAT8 DEFAULT 0.00 NOT NULL, + layer_currency_type VARCHAR(16) DEFAULT 'USD' NOT NULL, + layer_expires TIMESTAMP DEFAULT NULL, + layer_vendor UUID DEFAULT NULL REFERENCES test_vendors(vendor_uuid) ON DELETE SET NULL +);, sql='cost_layers') diff --git a/webserver.py b/webserver.py index bdc22ec..5047147 100644 --- a/webserver.py +++ b/webserver.py @@ -13,9 +13,13 @@ from application.poe import poe_api from application.shoppinglists import shoplist_api from application.receipts import receipts_api from application.meal_planner import meal_planner_api +from application.database_postgres.UsersModel import UsersModel +from application.database_postgres.SitesModel import SitesModel from flasgger import Swagger from outh import oauth +psycopg2.extras.register_uuid() + def create_app(): app = Flask(__name__, instance_relative_config=True) oauth.init_app(app) @@ -58,25 +62,15 @@ app = create_app() @app.context_processor def inject_user(): - if 'user_id' in session.keys() and session['user_id'] is not None: - database_config = config.config() - with psycopg2.connect(**database_config) as conn: - try: - with conn.cursor() as cur: - sql = f"SELECT id, username, sites, site_roles, system_admin, flags, profile_pic_url, login_type FROM logins WHERE id=%s;" - cur.execute(sql, (session['user_id'],)) - user = cur.fetchone() - user = database.tupleDictionaryFactory(cur.description, user) - session['user'] = user - except (Exception, psycopg2.DatabaseError) as error: - print(error) - conn.rollback() - return dict(username="") - return dict( - user_id=session.get('user')['id'], - username=session.get('user')['username'], - system_admin=session.get('user')['system_admin'] - ) + if 'user_uuid' in session.keys() and session['user_uuid'] is not None: + user = UsersModel.select_tuple('', {'key': session['user_uuid']}) + session['user'] = user + if user: + return dict( + user_uuid=session.get('user')['user_uuid'], + username=session.get('user')['user_name'], + system_admin=session.get('user')['user_is_system_admin'] + ) return dict(username="") @@ -114,10 +108,10 @@ def favicon(): @app.route("/") @access_api.login_required def home(): - access_api.update_session_user() - sites = [site[1] for site in main.get_sites(session['user']['sites'])] + sites = [SitesModel.select_tuple('', {'key': site})['site_name'] for site in session['user'].get('user_sites', [])] session['selected_site'] = sites[0] + access_api.update_session_user() return redirect("/items") if __name__ == "__main__": - app.run(host="0.0.0.0", port=5810, debug=True) \ No newline at end of file + app.run(host="0.0.0.0", port=5811, debug=True) \ No newline at end of file
      IDBarcode NameOperations