diff --git a/application/__pycache__/database_payloads.cpython-313.pyc b/application/__pycache__/database_payloads.cpython-313.pyc index af1b7f0..278919b 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/administration/sql/CREATE/plan_events.sql b/application/administration/sql/CREATE/plan_events.sql index 53fbe22..ca65296 100644 --- a/application/administration/sql/CREATE/plan_events.sql +++ b/application/administration/sql/CREATE/plan_events.sql @@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_plan_events( event_uuid UUID DEFAULT gen_random_uuid(), plan_uuid UUID, recipe_uuid UUID, + receipt_uuid UUID DEFAULT NULL, event_shortname VARCHAR(32) NOT NULL, event_description TEXT, event_date_start TIMESTAMP NOT NULL, diff --git a/application/database_payloads.py b/application/database_payloads.py index 707322d..b3ed303 100644 --- a/application/database_payloads.py +++ b/application/database_payloads.py @@ -576,6 +576,7 @@ class PlanEventPayload: event_date_end: datetime.datetime created_by: int recipe_uuid: str + receipt_uuid: str event_type: str def payload(self): @@ -587,6 +588,7 @@ class PlanEventPayload: self.event_date_end, self.created_by, self.recipe_uuid, + self.receipt_uuid, self.event_type ) 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 0edf40e..ea0ba63 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 e1e4104..81b57f1 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 new file mode 100644 index 0000000..8bfe966 Binary files /dev/null and b/application/meal_planner/__pycache__/meal_planner_processes.cpython-313.pyc differ diff --git a/application/meal_planner/meal_planner_api.py b/application/meal_planner/meal_planner_api.py index e311b31..c5d6163 100644 --- a/application/meal_planner/meal_planner_api.py +++ b/application/meal_planner/meal_planner_api.py @@ -10,7 +10,7 @@ import datetime from config import config from application.access_module import access_api from application import postsqldb, database_payloads -from application.meal_planner import meal_planner_database +from application.meal_planner import meal_planner_database, meal_planner_processes meal_planner_api = Blueprint('meal_planner_api', __name__, template_folder="templates", static_folder="static") @@ -62,6 +62,24 @@ def getRecipes(): return jsonify(status=201, message="Recipes fetched Successfully!", recipes=recipes, end=math.ceil(count/limit)) return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint!", recipes=recipes, end=math.ceil(count/limit)) + +@meal_planner_api.route('/api/getVendors', methods=["GET"]) +@access_api.login_required +def getVendors(): + if request.method == "GET": + site_name = session['selected_site'] + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 50)) + search_string = request.args.get('search_string', "") + + offset = (page - 1) * limit + vendors, count = [], 0 + vendors, count = meal_planner_database.paginateVendorsTuples(site_name, (limit, offset)) + + return jsonify(status=201, message="Recipes fetched Successfully!", vendors=vendors, end=math.ceil(count/limit)) + return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint!", vendors=vendors, end=math.ceil(count/limit)) + + @meal_planner_api.route('/api/addEvent', methods=["POST"]) @access_api.login_required def addEvent(): @@ -78,6 +96,7 @@ def addEvent(): event_date_end=event_date_end, created_by=session['user_id'], recipe_uuid=request.get_json()['recipe_uuid'], + receipt_uuid=None, event_type=request.get_json()['event_type'] ) @@ -86,6 +105,20 @@ def addEvent(): return jsonify(status=201, message="Event added Successfully!") return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint!") +@meal_planner_api.route('/api/addTOEvent', methods=["POST"]) +@access_api.login_required +def addTOEvent(): + if request.method == "POST": + site_name = session['selected_site'] + data= request.get_json() + user_id = session['user_id'] + + meal_planner_processes.addTakeOutEvent(site_name, data, user_id) + + return jsonify(status=201, message="Event added Successfully!") + return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint!") + + @meal_planner_api.route('/api/saveEvent', methods=["POST"]) @access_api.login_required def saveEvent(): diff --git a/application/meal_planner/meal_planner_database.py b/application/meal_planner/meal_planner_database.py index a7037a0..6b2a9b1 100644 --- a/application/meal_planner/meal_planner_database.py +++ b/application/meal_planner/meal_planner_database.py @@ -3,6 +3,46 @@ import psycopg2 from application import postsqldb import config +def requestNextReceiptID(site_name, conn=None): + """gets the next id for receipts_id, currently returns a 8 digit number + + Args: + site (str): site to get the next id for + + Returns: + json: receipt_id, message, error keys + """ + next_receipt_id = None + self_conn = False + sql = f"SELECT receipt_id FROM {site_name}_receipts ORDER BY id DESC LIMIT 1;" + 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) + next_receipt_id = cur.fetchone() + if next_receipt_id == None: + next_receipt_id = "00000001" + else: + next_receipt_id = next_receipt_id[0] + next_receipt_id = int(next_receipt_id.split("-")[1]) + 1 + y = str(next_receipt_id) + len_str = len(y) + x = "".join(["0" for _ in range(8 - len_str)]) + next_receipt_id = x + y + + if self_conn: + conn.commit() + conn.close() + + return next_receipt_id + except (Exception, psycopg2.DatabaseError) as error: + raise postsqldb.DatabaseError(error, payload=(), sql=sql) + def paginateRecipesTuples(site: str, payload: tuple, convert=True, conn=None): self_conn = False recipes = () @@ -34,6 +74,37 @@ def paginateRecipesTuples(site: str, payload: tuple, convert=True, conn=None): except Exception as error: raise postsqldb.DatabaseError(error, payload, sql) +def paginateVendorsTuples(site: str, payload: tuple, convert=True, conn=None): + self_conn = False + recipes = () + count = 0 + sql = f"SELECT * FROM {site}_vendors ORDER BY vendor_name ASC LIMIT %s OFFSET %s;" + sql_count = f"SELECT COUNT(*) FROM {site}_vendors;" + 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: + recipes = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows] + if rows and not convert: + recipes = rows + + cur.execute(sql_count) + count = cur.fetchone()[0] + + if self_conn: + conn.close() + + return recipes, count + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + def selectPlanEventsByMonth(site: str, payload: tuple, convert=True, conn=None): """payload=(year, month)""" self_conn = False @@ -126,7 +197,63 @@ def insertPlanEventTuple(site: str, payload: tuple, convert=True, conn=None): return event_tuple except Exception as error: raise postsqldb.DatabaseError(error, payload, sql) - + +def insertReceiptItemsTuple(site, payload, convert=True, conn=None): + receipt_item = () + self_conn = False + with open(f"application/meal_planner/sql/insertReceiptItemsTuple.sql", "r+") as file: + sql = file.read().replace("%%site_name%%", site) + 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: + receipt_item = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + receipt_item = rows + + if self_conn: + conn.commit() + conn.close() + + return receipt_item + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + +def insertReceiptsTuple(site, payload, convert=True, conn=None): + receipt = () + self_conn = False + with open(f"application/meal_planner/sql/insertReceiptsTuple.sql", "r+") as file: + sql = file.read().replace("%%site_name%%", site) + 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: + receipt = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + receipt = rows + + if self_conn: + conn.commit() + conn.close() + + return receipt + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + def updatePlanEventTuple(site:str, payload: dict, convert=True, conn=None): """ payload (dict): {'barcode': row_id, 'update': {... column_to_update: value_to_update_to...}} """ updated = () diff --git a/application/meal_planner/meal_planner_processes.py b/application/meal_planner/meal_planner_processes.py index e69de29..460614c 100644 --- a/application/meal_planner/meal_planner_processes.py +++ b/application/meal_planner/meal_planner_processes.py @@ -0,0 +1,66 @@ +import datetime +import psycopg2 + +from application.meal_planner import meal_planner_database +from application import postsqldb, database_payloads +import config + +def addTakeOutEvent(site, data, user_id, conn=None): + event_date_start = datetime.datetime.strptime(data['event_date_start'], "%Y-%m-%d") + event_date_end = datetime.datetime.strptime(data['event_date_end'], "%Y-%m-%d") + + vendor_id = data['vendor_id'] + + self_conn = False + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = False + self_conn = True + + receipt_id = meal_planner_database.requestNextReceiptID(site, conn=conn) + + receipt_payload = database_payloads.ReceiptPayload( + receipt_id=f"TOR-{receipt_id}", + receipt_status="Unresolved", + submitted_by=user_id, + vendor_id=vendor_id + ) + + receipt = meal_planner_database.insertReceiptsTuple(site, receipt_payload.payload(), conn=conn) + + print(receipt) + + receipt_item = database_payloads.ReceiptItemPayload( + type = 'custom', + receipt_id=receipt['id'], + barcode="", + item_uuid=None, + name=data['event_shortname'], + qty=data['attendees'], + uom=1, + data={'cost': data['cost'], 'expires': False} + ) + + receipt_item = meal_planner_database.insertReceiptItemsTuple(site, receipt_item.payload(), conn=conn) + print(receipt_item) + event_payload = database_payloads.PlanEventPayload( + plan_uuid=None, + event_shortname=data['event_shortname'], + event_description=data['event_description'], + event_date_start=event_date_start, + event_date_end=event_date_end, + created_by=user_id, + recipe_uuid=data['recipe_uuid'], + receipt_uuid=receipt['receipt_uuid'], + event_type=data['event_type'] + ) + + event = meal_planner_database.insertPlanEventTuple(site, event_payload.payload(), conn=conn) + print(event) + if self_conn: + conn.commit() + conn.close() + return False + + return True \ No newline at end of file diff --git a/application/meal_planner/sql/insertPlanEvent.sql b/application/meal_planner/sql/insertPlanEvent.sql index 6902fa8..96dcd92 100644 --- a/application/meal_planner/sql/insertPlanEvent.sql +++ b/application/meal_planner/sql/insertPlanEvent.sql @@ -1,4 +1,4 @@ INSERT INTO %%site_name%%_plan_events -(plan_uuid, event_shortname, event_description, event_date_start, event_date_end, created_by, recipe_uuid, event_type) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s) +(plan_uuid, event_shortname, event_description, event_date_start, event_date_end, created_by, recipe_uuid, receipt_uuid, event_type) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING *; \ No newline at end of file diff --git a/application/meal_planner/sql/insertReceiptItemsTuple.sql b/application/meal_planner/sql/insertReceiptItemsTuple.sql new file mode 100644 index 0000000..8f06d55 --- /dev/null +++ b/application/meal_planner/sql/insertReceiptItemsTuple.sql @@ -0,0 +1,4 @@ +INSERT INTO %%site_name%%_receipt_items +(type, receipt_id, barcode, item_uuid, name, qty, uom, data, status) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) +RETURNING *; \ No newline at end of file diff --git a/application/meal_planner/sql/insertReceiptsTuple.sql b/application/meal_planner/sql/insertReceiptsTuple.sql new file mode 100644 index 0000000..8ddaf60 --- /dev/null +++ b/application/meal_planner/sql/insertReceiptsTuple.sql @@ -0,0 +1,4 @@ +INSERT INTO %%site_name%%_receipts +(receipt_id, receipt_status, date_submitted, submitted_by, vendor_id, files) +VALUES (%s, %s, %s, %s, %s, %s) +RETURNING *; \ No newline at end of file diff --git a/application/meal_planner/static/css/planner.css b/application/meal_planner/static/css/planner.css index 576ac2b..258199a 100644 --- a/application/meal_planner/static/css/planner.css +++ b/application/meal_planner/static/css/planner.css @@ -133,6 +133,24 @@ background-color: rgb(255, 255, 255); } +.take-out-label { + background:rgb(250, 162, 238); + margin-bottom: 3px; + padding: 2px 5px; + border-radius: 1px; + font-size: 12px; + font-weight: bold; +} + +.take-out-label:hover{ + background-color: rgb(225, 255, 255); + cursor: pointer; +} + +.take-out-label:hover, .custom-label-selected { + background-color: rgb(255, 255, 255); +} + .my-list-item:hover { background-color: whitesmoke; } diff --git a/application/meal_planner/static/js/mealPlannerHandler.js b/application/meal_planner/static/js/mealPlannerHandler.js index dccf52f..25f1b2a 100644 --- a/application/meal_planner/static/js/mealPlannerHandler.js +++ b/application/meal_planner/static/js/mealPlannerHandler.js @@ -105,6 +105,7 @@ async function setupCalendarAndEvents(){ let recipeLabel = e.target.closest('.recipe-label'); let calendarCell = e.target.closest('.calendar-cell'); let customLabel = e.target.closest('.custom-label'); + let takeOutLabel = e.target.closest('.take-out-label') if (recipeLabel) { recipeLabel.classList.add('recipe-label-selected') let rect = recipeLabel.getBoundingClientRect(); @@ -121,6 +122,14 @@ async function setupCalendarAndEvents(){ let menuX = rect.left + scrollLeft; let menuY = rect.bottom + scrollTop; showContextMenuForEvent(customLabel, menuX, menuY); + } else if (takeOutLabel) { + takeOutLabel.classList.add('take-out-label-selected') + let rect = takeOutLabel.getBoundingClientRect(); + let scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; + let scrollTop = window.pageYOffset || document.documentElement.scrollTop; + let menuX = rect.left + scrollLeft; + let menuY = rect.bottom + scrollTop; + showContextMenuForTOEvent(takeOutLabel, menuX, menuY); } else if (calendarCell) { calendarCell.classList.add('calendar-cell-selected') let rect = calendarCell.getBoundingClientRect(); @@ -167,6 +176,8 @@ async function createCalender() { eventsHTML += `
${event.event_shortname}
`; } else if (event.event_type==="recipe" && !event.has_missing_ingredients){ eventsHTML += `
${event.event_shortname}
`; + } else if (event.event_type==="take out"){ + eventsHTML += `
${event.event_shortname}
`; } else { eventsHTML += `
${event.event_shortname}
`; } @@ -218,6 +229,21 @@ function showContextMenuForEvent(eventLabel, x, y) { menu.style.top = y + 'px'; } +function showContextMenuForTOEvent(eventLabel, x, y) { + const menu = document.getElementById('calendarContextMenu'); + // Set only "Edit" and "Remove" (and optionally "Add Another") + menu.className = "uk-dropdown uk-open"; + menu.innerHTML = ` + + `; + menu.style.display = 'block'; + menu.style.left = x + 'px'; + menu.style.top = y + 'px'; +} + + function showContextMenuForCell(calendarCell, x, y) { const menu = document.getElementById('calendarContextMenu'); // Only "Add Event" @@ -225,6 +251,7 @@ function showContextMenuForCell(calendarCell, x, y) { menu.innerHTML = ` `; menu.style.display = 'block'; @@ -237,6 +264,7 @@ window.addEventListener('click', function() { document.querySelectorAll('.calendar-cell-selected').forEach(el => el.classList.remove('calendar-cell-selected')); document.querySelectorAll('.custom-label-selected').forEach(el => el.classList.remove('custom-label-selected')); document.querySelectorAll('.recipe-label-selected').forEach(el => el.classList.remove('recipe-label-selected')); + document.querySelectorAll('.take-out-label-selected').forEach(el => el.classList.remove('recipe-label-selected')); }); async function addEvent(day) { @@ -593,4 +621,207 @@ async function updateEventsPaginationElement() { console.log(nextElement.innerHTML) } paginationElement.append(nextElement) +} + +// Take Out Event Functions +var TO_current_page = 1; +var TO_end_page = 1; +var TO_Limit = 10; +var TO_search_string = ""; + +async function addTakeOut(day) { + TO_current_page = 1; + TO_end_page = 1; + TO_Limit = 10; + TO_search_string = ""; + let menu = document.getElementById('calendarContextMenu'); + //let day = menu.getAttribute('data-day') + console.log(year, month, day) + let customDate = new Date(year, month-1, day); + document.getElementById('TO_date_start').value = customDate.toISOString().split('T')[0]; + document.getElementById('TO_date_end').value = customDate.toISOString().split('T')[0]; + UIkit.modal(document.getElementById('takeOutOrderModal')).show(); +} + +async function selectTOEvent() { + document.getElementById('TOModalBody').hidden = true + document.getElementById('paginationTOModalBody').hidden = false + document.getElementById('TOModalFooter').hidden = true + let vendors = await fetchVendors() + await updateTOPaginationElement() + await updateTOTableWithVendors(vendors) +} + +async function fetchVendors() { + const url = new URL('/planner/api/getVendors', window.location.origin); + url.searchParams.append('page', TO_current_page); + url.searchParams.append('limit', TO_Limit); + url.searchParams.append('search_string', TO_search_string); + const response = await fetch(url); + data = await response.json(); + TO_end_page = data.end + return data.vendors; +} + +async function updateTOTableWithVendors(vendors) { + let vendorsTableBody = document.getElementById('vendorsTableBody') + vendorsTableBody.innerHTML = "" + + + for (let i = 0; i < vendors.length; i++){ + let tableRow = document.createElement('tr') + + let nameCell = document.createElement('td') + nameCell.innerHTML = `${vendors[i].vendor_name}` + + + let opCell = document.createElement('td') + + let selectButton = document.createElement('button') + selectButton.setAttribute('class', 'uk-button uk-button-primary uk-button-small') + selectButton.innerHTML = "Select" + selectButton.onclick = async function() { + document.getElementById('vendor_id').value = vendors[i].id + document.getElementById('selected_vendor_name').value = vendors[i].vendor_name + document.getElementById('TOModalBody').hidden = false + document.getElementById('paginationTOModalBody').hidden = true + document.getElementById('TOModalFooter').hidden = false + } + + opCell.append(selectButton) + + tableRow.append(nameCell, opCell) + vendorsTableBody.append(tableRow) + } +} + +async function setTOModalPage(pageNumber){ + TO_current_page = pageNumber; + let vendors = await fetchVendors() + await updateTOTableWithVendors(vendors) + await updateTOPaginationElement() +} + +async function updateTOPaginationElement() { + let paginationElement = document.getElementById('takeOutOrderPage'); + paginationElement.innerHTML = ""; + // previous + let previousElement = document.createElement('li') + if(TO_current_page<=1){ + previousElement.innerHTML = ``; + previousElement.classList.add('uk-disabled'); + }else { + previousElement.innerHTML = ``; + } + paginationElement.append(previousElement) + + //first + let firstElement = document.createElement('li') + if(TO_current_page<=1){ + firstElement.innerHTML = `1`; + firstElement.classList.add('uk-disabled'); + }else { + firstElement.innerHTML = `1`; + } + paginationElement.append(firstElement) + + // ... + if(TO_current_page-2>1){ + let firstDotElement = document.createElement('li') + firstDotElement.classList.add('uk-disabled') + firstDotElement.innerHTML = ``; + paginationElement.append(firstDotElement) + } + // last + if(TO_current_page-2>0){ + let lastElement = document.createElement('li') + lastElement.innerHTML = `${TO_current_page-1}` + paginationElement.append(lastElement) + } + // current + if(TO_current_page!=1 && TO_current_page != TO_end_page){ + let currentElement = document.createElement('li') + currentElement.innerHTML = `
  • ${TO_current_page}
  • ` + paginationElement.append(currentElement) + } + // next + if(TO_current_page+2${TO_current_page+1}` + paginationElement.append(nextElement) + } + // ... + if(TO_current_page+2<=TO_end_page){ + let secondDotElement = document.createElement('li') + secondDotElement.classList.add('uk-disabled') + secondDotElement.innerHTML = ``; + paginationElement.append(secondDotElement) + } + //end + let endElement = document.createElement('li') + if(TO_current_page>=TO_end_page){ + endElement.innerHTML = `${TO_end_page}`; + endElement.classList.add('uk-disabled'); + }else { + endElement.innerHTML = `${TO_end_page}`; + } + paginationElement.append(endElement) + //next button + let nextElement = document.createElement('li') + if(TO_current_page>=TO_end_page){ + nextElement.innerHTML = ``; + nextElement.classList.add('uk-disabled'); + }else { + nextElement.innerHTML = ``; + console.log(nextElement.innerHTML) + } + paginationElement.append(nextElement) +} + +async function postTOEvent(){ + let event_shortname = `Take Out: ${document.getElementById('selected_vendor_name').value}` + let event_description = `Take out dining at ${event_shortname}` + let event_date_start = document.getElementById('TO_date_start').value + let event_date_end = document.getElementById('TO_date_end').value + let event_type = 'take out' + let recipe_uuid = null + + let vendor_id = parseInt(document.getElementById('vendor_id').value) + let attendees = parseInt(document.getElementById('TO_attendees').value) + let cost = parseFloat(document.getElementById('TO_cost').value) + + const response = await fetch('/planner/api/addTOEvent', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + event_shortname: event_shortname, + event_description: event_description, + event_date_start: event_date_start, + event_date_end: event_date_end, + recipe_uuid: recipe_uuid, + event_type: event_type, + vendor_id: vendor_id, + attendees: attendees, + cost: cost + }) + }); + + data = await response.json(); + response_status = 'primary' + if (!data.status === 201){ + response_status = 'danger' + } + + UIkit.notification({ + message: data.message, + status: response_status, + pos: 'top-right', + timeout: 5000 + }); + + await setupCalendarAndEvents() + UIkit.modal(document.getElementById('takeOutOrderModal')).hide(); + } \ No newline at end of file diff --git a/application/meal_planner/templates/meal_planner.html b/application/meal_planner/templates/meal_planner.html index d6a5f8f..9ef1a1b 100644 --- a/application/meal_planner/templates/meal_planner.html +++ b/application/meal_planner/templates/meal_planner.html @@ -203,6 +203,103 @@ + +
    +
    + +
    +

    Take Out Form

    +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + +
    +
    diff --git a/logs/database.log b/logs/database.log index deb0266..1271c8a 100644 --- a/logs/database.log +++ b/logs/database.log @@ -652,4 +652,7 @@ sql='SELECT items.item_uuid as item_uuid, items.item_name as item_name, units.fullname AS fullname, units.id AS unit_id, items.links AS linksFROM main_items itemsLEFT JOIN main_item_info item_info ON item_info.id = items.item_info_idLEFT JOIN units ON item_info.uom = units.idWHERE items.search_string LIKE '%%' || %s || '%%' AND items.inactive IS false AND item_info.safety_stock > 0ORDER BY items.item_name LIMIT %s OFFSET %s;') 2025-08-21 17:32:19.598403 --- ERROR --- DatabaseError(message='syntax error at or near "%"LINE 1: ...CT COUNT(items.*) FROM main_items items LEFT JOIN %site_name... ^', payload=('', 25, 0), - sql='SELECT items.item_uuid as item_uuid, items.item_name as item_name, units.fullname AS fullname, units.id AS unit_id, items.links AS linksFROM main_items itemsLEFT JOIN main_item_info item_info ON item_info.id = items.item_info_idLEFT JOIN units ON item_info.uom = units.idWHERE items.search_string LIKE '%%' || %s || '%%' AND items.inactive IS false AND item_info.safety_stock > 0ORDER BY items.item_name LIMIT %s OFFSET %s;') \ No newline at end of file + sql='SELECT items.item_uuid as item_uuid, items.item_name as item_name, units.fullname AS fullname, units.id AS unit_id, items.links AS linksFROM main_items itemsLEFT JOIN main_item_info item_info ON item_info.id = items.item_info_idLEFT JOIN units ON item_info.uom = units.idWHERE items.search_string LIKE '%%' || %s || '%%' AND items.inactive IS false AND item_info.safety_stock > 0ORDER BY items.item_name LIMIT %s OFFSET %s;') +2025-08-21 18:44:41.870684 --- ERROR --- DatabaseError(message='column "name" does not existLINE 1: SELECT * FROM test_vendors ORDER BY name ASC LIMIT 10 OFFSET... ^', + payload=(10, 0), + sql='SELECT * FROM test_vendors ORDER BY name ASC LIMIT %s OFFSET %s;') \ No newline at end of file