Added Take Out Events and REceipts
This commit is contained in:
parent
0fc1b6dc1d
commit
1156ab5aca
Binary file not shown.
@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_plan_events(
|
|||||||
event_uuid UUID DEFAULT gen_random_uuid(),
|
event_uuid UUID DEFAULT gen_random_uuid(),
|
||||||
plan_uuid UUID,
|
plan_uuid UUID,
|
||||||
recipe_uuid UUID,
|
recipe_uuid UUID,
|
||||||
|
receipt_uuid UUID DEFAULT NULL,
|
||||||
event_shortname VARCHAR(32) NOT NULL,
|
event_shortname VARCHAR(32) NOT NULL,
|
||||||
event_description TEXT,
|
event_description TEXT,
|
||||||
event_date_start TIMESTAMP NOT NULL,
|
event_date_start TIMESTAMP NOT NULL,
|
||||||
|
|||||||
@ -576,6 +576,7 @@ class PlanEventPayload:
|
|||||||
event_date_end: datetime.datetime
|
event_date_end: datetime.datetime
|
||||||
created_by: int
|
created_by: int
|
||||||
recipe_uuid: str
|
recipe_uuid: str
|
||||||
|
receipt_uuid: str
|
||||||
event_type: str
|
event_type: str
|
||||||
|
|
||||||
def payload(self):
|
def payload(self):
|
||||||
@ -587,6 +588,7 @@ class PlanEventPayload:
|
|||||||
self.event_date_end,
|
self.event_date_end,
|
||||||
self.created_by,
|
self.created_by,
|
||||||
self.recipe_uuid,
|
self.recipe_uuid,
|
||||||
|
self.receipt_uuid,
|
||||||
self.event_type
|
self.event_type
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -10,7 +10,7 @@ import datetime
|
|||||||
from config import config
|
from config import config
|
||||||
from application.access_module import access_api
|
from application.access_module import access_api
|
||||||
from application import postsqldb, database_payloads
|
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")
|
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=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))
|
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"])
|
@meal_planner_api.route('/api/addEvent', methods=["POST"])
|
||||||
@access_api.login_required
|
@access_api.login_required
|
||||||
def addEvent():
|
def addEvent():
|
||||||
@ -78,6 +96,7 @@ def addEvent():
|
|||||||
event_date_end=event_date_end,
|
event_date_end=event_date_end,
|
||||||
created_by=session['user_id'],
|
created_by=session['user_id'],
|
||||||
recipe_uuid=request.get_json()['recipe_uuid'],
|
recipe_uuid=request.get_json()['recipe_uuid'],
|
||||||
|
receipt_uuid=None,
|
||||||
event_type=request.get_json()['event_type']
|
event_type=request.get_json()['event_type']
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -86,6 +105,20 @@ def addEvent():
|
|||||||
return jsonify(status=201, message="Event added Successfully!")
|
return jsonify(status=201, message="Event added Successfully!")
|
||||||
return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint!")
|
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"])
|
@meal_planner_api.route('/api/saveEvent', methods=["POST"])
|
||||||
@access_api.login_required
|
@access_api.login_required
|
||||||
def saveEvent():
|
def saveEvent():
|
||||||
|
|||||||
@ -3,6 +3,46 @@ import psycopg2
|
|||||||
from application import postsqldb
|
from application import postsqldb
|
||||||
import config
|
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):
|
def paginateRecipesTuples(site: str, payload: tuple, convert=True, conn=None):
|
||||||
self_conn = False
|
self_conn = False
|
||||||
recipes = ()
|
recipes = ()
|
||||||
@ -34,6 +74,37 @@ def paginateRecipesTuples(site: str, payload: tuple, convert=True, conn=None):
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
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):
|
def selectPlanEventsByMonth(site: str, payload: tuple, convert=True, conn=None):
|
||||||
"""payload=(year, month)"""
|
"""payload=(year, month)"""
|
||||||
self_conn = False
|
self_conn = False
|
||||||
@ -126,7 +197,63 @@ def insertPlanEventTuple(site: str, payload: tuple, convert=True, conn=None):
|
|||||||
return event_tuple
|
return event_tuple
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
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):
|
def updatePlanEventTuple(site:str, payload: dict, convert=True, conn=None):
|
||||||
""" payload (dict): {'barcode': row_id, 'update': {... column_to_update: value_to_update_to...}} """
|
""" payload (dict): {'barcode': row_id, 'update': {... column_to_update: value_to_update_to...}} """
|
||||||
updated = ()
|
updated = ()
|
||||||
|
|||||||
@ -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
|
||||||
@ -1,4 +1,4 @@
|
|||||||
INSERT INTO %%site_name%%_plan_events
|
INSERT INTO %%site_name%%_plan_events
|
||||||
(plan_uuid, event_shortname, event_description, event_date_start, event_date_end, created_by, recipe_uuid, event_type)
|
(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)
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
4
application/meal_planner/sql/insertReceiptItemsTuple.sql
Normal file
4
application/meal_planner/sql/insertReceiptItemsTuple.sql
Normal 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 *;
|
||||||
4
application/meal_planner/sql/insertReceiptsTuple.sql
Normal file
4
application/meal_planner/sql/insertReceiptsTuple.sql
Normal 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 *;
|
||||||
@ -133,6 +133,24 @@
|
|||||||
background-color: rgb(255, 255, 255);
|
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 {
|
.my-list-item:hover {
|
||||||
background-color: whitesmoke;
|
background-color: whitesmoke;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,6 +105,7 @@ async function setupCalendarAndEvents(){
|
|||||||
let recipeLabel = e.target.closest('.recipe-label');
|
let recipeLabel = e.target.closest('.recipe-label');
|
||||||
let calendarCell = e.target.closest('.calendar-cell');
|
let calendarCell = e.target.closest('.calendar-cell');
|
||||||
let customLabel = e.target.closest('.custom-label');
|
let customLabel = e.target.closest('.custom-label');
|
||||||
|
let takeOutLabel = e.target.closest('.take-out-label')
|
||||||
if (recipeLabel) {
|
if (recipeLabel) {
|
||||||
recipeLabel.classList.add('recipe-label-selected')
|
recipeLabel.classList.add('recipe-label-selected')
|
||||||
let rect = recipeLabel.getBoundingClientRect();
|
let rect = recipeLabel.getBoundingClientRect();
|
||||||
@ -121,6 +122,14 @@ async function setupCalendarAndEvents(){
|
|||||||
let menuX = rect.left + scrollLeft;
|
let menuX = rect.left + scrollLeft;
|
||||||
let menuY = rect.bottom + scrollTop;
|
let menuY = rect.bottom + scrollTop;
|
||||||
showContextMenuForEvent(customLabel, menuX, menuY);
|
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) {
|
} else if (calendarCell) {
|
||||||
calendarCell.classList.add('calendar-cell-selected')
|
calendarCell.classList.add('calendar-cell-selected')
|
||||||
let rect = calendarCell.getBoundingClientRect();
|
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>`;
|
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){
|
} 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>`;
|
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 {
|
} else {
|
||||||
eventsHTML += `<div class="custom-label" data-event_uuid="${event.event_uuid}" data-day="${day}">${event.event_shortname}</div>`;
|
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';
|
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) {
|
function showContextMenuForCell(calendarCell, x, y) {
|
||||||
const menu = document.getElementById('calendarContextMenu');
|
const menu = document.getElementById('calendarContextMenu');
|
||||||
// Only "Add Event"
|
// Only "Add Event"
|
||||||
@ -225,6 +251,7 @@ function showContextMenuForCell(calendarCell, x, y) {
|
|||||||
menu.innerHTML = `
|
menu.innerHTML = `
|
||||||
<ul class="uk-nav uk-dropdown-nav">
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
<li><a href="#" onclick="addEvent('${calendarCell.dataset.day}')">Add Event</a></li>
|
<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>
|
</ul>
|
||||||
`;
|
`;
|
||||||
menu.style.display = 'block';
|
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('.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('.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('.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) {
|
async function addEvent(day) {
|
||||||
@ -593,4 +621,207 @@ async function updateEventsPaginationElement() {
|
|||||||
console.log(nextElement.innerHTML)
|
console.log(nextElement.innerHTML)
|
||||||
}
|
}
|
||||||
paginationElement.append(nextElement)
|
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();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -203,6 +203,103 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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-->
|
<!-- Event Modal-->
|
||||||
<div id="eventModal" class="uk-flex-top" uk-modal>
|
<div id="eventModal" class="uk-flex-top" uk-modal>
|
||||||
<div class="uk-modal-dialog">
|
<div class="uk-modal-dialog">
|
||||||
|
|||||||
@ -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;')
|
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... ^',
|
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),
|
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;')
|
||||||
Loading…
x
Reference in New Issue
Block a user