Added Take Out Events and REceipts

This commit is contained in:
Jadowyne Ulve 2025-08-22 07:08:17 -05:00
parent 0fc1b6dc1d
commit 1156ab5aca
16 changed files with 591 additions and 5 deletions

View File

@ -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,

View File

@ -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
)

View File

@ -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():

View File

@ -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 = ()

View File

@ -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

View File

@ -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 *;

View File

@ -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 *;

View File

@ -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 *;

View File

@ -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;
}

View File

@ -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 += `<div class="recipe-label recipe-error" data-event_uuid="${event.event_uuid}" data-day="${day}">${event.event_shortname}</div>`;
} else if (event.event_type==="recipe" && !event.has_missing_ingredients){
eventsHTML += `<div class="recipe-label recipe-success" data-event_uuid="${event.event_uuid}" data-day="${day}">${event.event_shortname}</div>`;
} else if (event.event_type==="take out"){
eventsHTML += `<div class="take-out-label" data-event_uuid="${event.event_uuid}" data-day="${day}">${event.event_shortname}</div>`;
} else {
eventsHTML += `<div class="custom-label" data-event_uuid="${event.event_uuid}" data-day="${day}">${event.event_shortname}</div>`;
}
@ -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 = `
<ul class="uk-nav uk-dropdown-nav">
<li><a href="#" onclick="postRemoveEvent('${eventLabel.dataset.event_uuid}')">Remove Event</a></li>
</ul>
`;
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 = `
<ul class="uk-nav uk-dropdown-nav">
<li><a href="#" onclick="addEvent('${calendarCell.dataset.day}')">Add Event</a></li>
<li><a href="#" onclick="addTakeOut('${calendarCell.dataset.day}')">Add Take Out</a></li>
</ul>
`;
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 = `<a><span uk-pagination-previous></span></a>`;
previousElement.classList.add('uk-disabled');
}else {
previousElement.innerHTML = `<a onclick="setTOModalPage(${TO_current_page-1})"><span uk-pagination-previous></span></a>`;
}
paginationElement.append(previousElement)
//first
let firstElement = document.createElement('li')
if(TO_current_page<=1){
firstElement.innerHTML = `<a><strong>1</strong></a>`;
firstElement.classList.add('uk-disabled');
}else {
firstElement.innerHTML = `<a onclick="setTOModalPage(1)">1</a>`;
}
paginationElement.append(firstElement)
// ...
if(TO_current_page-2>1){
let firstDotElement = document.createElement('li')
firstDotElement.classList.add('uk-disabled')
firstDotElement.innerHTML = `<span>…</span>`;
paginationElement.append(firstDotElement)
}
// last
if(TO_current_page-2>0){
let lastElement = document.createElement('li')
lastElement.innerHTML = `<a onclick="setTOModalPage(${TO_current_page-1})">${TO_current_page-1}</a>`
paginationElement.append(lastElement)
}
// current
if(TO_current_page!=1 && TO_current_page != TO_end_page){
let currentElement = document.createElement('li')
currentElement.innerHTML = `<li class="uk-active"><span aria-current="page"><strong>${TO_current_page}</strong></span></li>`
paginationElement.append(currentElement)
}
// next
if(TO_current_page+2<TO_end_page+1){
let nextElement = document.createElement('li')
nextElement.innerHTML = `<a onclick="setTOModalPage(${TO_current_page+1})">${TO_current_page+1}</a>`
paginationElement.append(nextElement)
}
// ...
if(TO_current_page+2<=TO_end_page){
let secondDotElement = document.createElement('li')
secondDotElement.classList.add('uk-disabled')
secondDotElement.innerHTML = `<span>…</span>`;
paginationElement.append(secondDotElement)
}
//end
let endElement = document.createElement('li')
if(TO_current_page>=TO_end_page){
endElement.innerHTML = `<a><strong>${TO_end_page}</strong></a>`;
endElement.classList.add('uk-disabled');
}else {
endElement.innerHTML = `<a onclick="setTOModalPage(${TO_end_page})">${TO_end_page}</a>`;
}
paginationElement.append(endElement)
//next button
let nextElement = document.createElement('li')
if(TO_current_page>=TO_end_page){
nextElement.innerHTML = `<a><span uk-pagination-next></span></a>`;
nextElement.classList.add('uk-disabled');
}else {
nextElement.innerHTML = `<a onclick="setTOModalPage(${TO_current_page+1})"><span uk-pagination-next></span></a>`;
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();
}

View File

@ -203,6 +203,103 @@
</div>
</div>
</div>
<!-- Take Out Order -->
<div id="takeOutOrderModal" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog">
<button class="uk-modal-close-default" type="button" uk-close></button>
<div class="uk-modal-header">
<h2 class="uk-modal-title">Take Out Form</h2>
</div>
<div id="paginationTOModalBody" class="uk-modal-body" hidden>
<div class="uk-grid-small" uk-grid>
<div class="uk-width-1-1">
<div id="searchItemsForm" onkeydown="" class="uk-search uk-search-default uk-align-center">
<input id="searchVendorsInput" class="uk-border-pill uk-search-input" type="search" placeholder="" aria-label="">
<span class="uk-search-icon-flip" uk-search-icon></span>
</div>
</div>
<div class="uk-width-1-1">
<nav aria-label="Pagination">
<ul id="takeOutOrderPage" class="uk-pagination uk-flex-center" uk-margin>
<li><a href="#"><span uk-pagination-previous></span></a></li>
<li><a href="#">1</a></li>
<li class="uk-disabled"><span></span></li>
<li><a href="#">5</a></li>
<li><a href="#">6</a></li>
<li class="uk-active"><span aria-current="page">7</span></li>
<li><a href="#">8</a></li>
<li><a href="#"><span uk-pagination-next></span></a></li>
</ul>
</nav>
</div>
<div class="uk-width-1-1">
<caption>Select a Vendor from the system...</caption>
<table class="uk-table uk-table-striped">
<thead>
<tr>
<th>Name</th>
<th>Operations</th>
</tr>
</thead>
<tbody id="vendorsTableBody">
</tbody>
</table>
</div>
</div>
</div>
<div id="TOModalBody" class="uk-modal-body">
<div class="uk-grid-small" uk-grid>
<div class="uk-width-1-1">
<div class="uk-margin">
<label class="uk-form-label" for="TO_date_start">Date</label>
<div class="uk-form-controls">
<input id="TO_date_start" class="uk-input uk-disabled" type="date">
</div>
</div>
</div>
<div class="uk-width-1-1" hidden>
<div class="uk-margin">
<label class="uk-form-label" for="TO_date_end"></label>
<div class="uk-form-controls">
<input id="TO_date_end" class="uk-input uk-disabled" type="date">
</div>
</div>
</div>
<div class="uk-width-1-1" id="vendor_button_modal">
<div class="uk-margin">
<button class="uk-button uk-button-default" type="button" onclick="selectTOEvent()">Choose Vendor...</button>
</div>
</div>
<div class="uk-width-1-1" id="vendor_name">
<div class="uk-margin">
<input id="selected_vendor_name" class="uk-input uk-disabled" type="text" placeholder="No Vendor selected">
</div>
<input id="vendor_id" hidden>
</div>
<div class="uk-width-1-1">
<div class="uk-margin">
<label class="uk-form-label" for="TO_attendees">Attendees</label>
<div class="uk-form-controls">
<input id="TO_attendees" class="uk-input" type="number" step="1" min="1" placeholder="1" value="1">
</div>
</div>
</div>
<div class="uk-width-1-1">
<div class="uk-margin">
<label class="uk-form-label" for="TO_cost">Cost</label>
<div class="uk-form-controls">
<input id="TO_cost" class="uk-input" type="number" step="0.01" min="0" placeholder="0.00">
</div>
</div>
</div>
</div>
</div>
<div class="uk-modal-footer uk-text-right" id="TOModalFooter">
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
<button onclick="postTOEvent()" class="uk-button uk-button-primary" type="button">Save</button>
</div>
</div>
</div>
<!-- Event Modal-->
<div id="eventModal" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog">

View File

@ -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;')
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;')