diff --git a/application/recipes/__pycache__/recipe_processes.cpython-313.pyc b/application/recipes/__pycache__/recipe_processes.cpython-313.pyc index 2bc4861..5789dd4 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/shoppinglists/__pycache__/shoplist_api.cpython-313.pyc b/application/shoppinglists/__pycache__/shoplist_api.cpython-313.pyc index 3a6bdad..4851f76 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 a6bb1b3..62740f5 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 99f14f3..31bd45a 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/shoppinglists/shoplist_api.py b/application/shoppinglists/shoplist_api.py index f3cb556..9fdbdac 100644 --- a/application/shoppinglists/shoplist_api.py +++ b/application/shoppinglists/shoplist_api.py @@ -314,6 +314,7 @@ def postGeneratedList(): payload: dict = request.get_json() site_name: str = session['selected_site'] user_id: int = session['user_id'] + print(payload) shoplist_processess.postNewGeneratedList(site_name, payload, user_id) return jsonify(status=201, message=f"List Generated successfully!") return jsonify(status=405, message=f"{request.method} is not an accepted method on this endpoint!") \ No newline at end of file diff --git a/application/shoppinglists/shoplist_database.py b/application/shoppinglists/shoplist_database.py index 6665739..ed226ce 100644 --- a/application/shoppinglists/shoplist_database.py +++ b/application/shoppinglists/shoplist_database.py @@ -324,6 +324,36 @@ def getItemByUUID(site, payload:dict, convert=True, conn=None): except Exception as error: raise postsqldb.DatabaseError(error, payload, sql) +def getEventRecipes(site, payload, convert=True, conn=None): + """ payload: dict = {'plan_uuid', 'start_date', 'end_date'}""" + records = () + self_conn = False + with open('application/shoppinglists/sql/getEventsRecipes.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.fetchall() + if rows and convert: + records = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows] + elif rows and not convert: + records = rows + + if self_conn: + conn.close() + + return records + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + def deleteShoppingListsTuple(site_name, payload, convert=True, conn=None): deleted = () self_conn = False diff --git a/application/shoppinglists/shoplist_processess.py b/application/shoppinglists/shoplist_processess.py index dd8cc4d..e0ff281 100644 --- a/application/shoppinglists/shoplist_processess.py +++ b/application/shoppinglists/shoplist_processess.py @@ -48,6 +48,7 @@ def postNewGeneratedList(site: str, data: dict, user_id: int, conn=None): recipes: list = data['recipes'] full_system_calculated: list = data['full_system_calculated'] shopping_lists: list = data['shopping_lists'] + site_plans: list = data['site_plans'] self_conn=False @@ -157,6 +158,26 @@ def postNewGeneratedList(site: str, data: dict, user_id: int, conn=None): ) items_to_add_to_system.append(temp_item) + + if site_plans: + for site_plan in site_plans: + if site_plan['plan_uuid'] == 'site': site_plan['plan_uuid'] = None + plan_recipes = [event['recipe_uuid'] for event in shoplist_database.getEventRecipes(site, site_plan, conn=conn)] + if plan_recipes: + for recipe_uuid in plan_recipes: + recipe_items = shoplist_database.getRecipeItemsByUUID(site, (recipe_uuid,), conn=conn) + for item in recipe_items: + temp_item = database_payloads.ShoppingListItemPayload( + list_uuid=shopping_list['list_uuid'], + item_type='recipe', + item_name=item['item_name'], + uom=item['uom'], + qty=float(item['qty']), + item_uuid=item['item_uuid'], + links=item['links'] + ) + items_to_add_to_system.append(temp_item) + if items_to_add_to_system: for item in items_to_add_to_system: @@ -180,7 +201,9 @@ def deleteShoppingList(site: str, data: dict, user_id: int, conn=None): shopping_list_items = [item['list_item_uuid'] for item in shopping_list_items] shoplist_database.deleteShoppingListsTuple(site, (shopping_list_uuid,), conn=conn) - shoplist_database.deleteShoppingListItemsTuple(site, shopping_list_items, conn=conn) + if shopping_list_items: + shoplist_database.deleteShoppingListItemsTuple(site, shopping_list_items, conn=conn) + if self_conn: conn.commit() diff --git a/application/shoppinglists/sql/getEventsRecipes.sql b/application/shoppinglists/sql/getEventsRecipes.sql new file mode 100644 index 0000000..d318bc0 --- /dev/null +++ b/application/shoppinglists/sql/getEventsRecipes.sql @@ -0,0 +1,7 @@ +SELECT events.recipe_uuid +FROM %%site_name%%_plan_events events +WHERE events.plan_uuid IS NULL + AND events.event_type = 'recipe' + AND events.recipe_uuid IS NOT NULL + AND events.event_date_start <= %(end_date)s + AND events.event_date_end >= %(start_date)s; \ No newline at end of file diff --git a/application/shoppinglists/static/js/shoppingListGeneratorHandler.js b/application/shoppinglists/static/js/shoppingListGeneratorHandler.js index e1258e7..a68e8ad 100644 --- a/application/shoppinglists/static/js/shoppingListGeneratorHandler.js +++ b/application/shoppinglists/static/js/shoppingListGeneratorHandler.js @@ -1076,6 +1076,130 @@ async function generateListsTable() { } +// Site Planner Functions +var site_planners = {} +var site_planner_card_active = false; +async function addPlannerCard(){ + if(!site_planner_card_active){ + document.getElementById('plannerCard').hidden = false + site_planner_card_active = true; + } +} + +async function removePlannerCard(){ + document.getElementById('plannerCard').hidden = true + site_planner_card_active = false; + site_planners = [] +} + +var PlannerZoneState = true +async function changePlannerZoneState() { + PlannerZoneState = !PlannerZoneState + document.getElementById('plannerZone').hidden = !PlannerZoneState +} + +async function openPlannerModal(){ + document.getElementById('planUUID').setAttribute('class', 'uk-input uk-disabled') + document.getElementById('planUUID').value = 'site' + document.getElementById('planStartDate').value = '' + document.getElementById('planEndDate').value = '' + document.getElementById('plannerModalButton').innerHTML = "Save" + document.getElementById('plannerModalButton').onclick = async function () { await addPlanner()} + UIkit.modal(document.getElementById('plannerModal')).show() +} + +async function addPlanner() { + var planner_select = document.getElementById('planUUID') + planner_uuid = planner_select.value + plan_name = planner_select.options[planner_select.selectedIndex].text + startDate = document.getElementById('planStartDate').value + endDate = document.getElementById('planEndDate').value + site_planners[planner_uuid] = { + start_date: startDate, + end_date: endDate, + plan_uuid: planner_uuid, + plan_name: plan_name + } + UIkit.modal(document.getElementById('plannerModal')).hide() + console.log(site_planners) + await generatePlannerTable() +} + +async function editPlanner(planUUID) { + let data = site_planners[planUUID] + document.getElementById('planUUID').setAttribute('class', 'uk-input uk-disabled') + document.getElementById('planUUID').value = data['plan_uuid'] + document.getElementById('planStartDate').value = data['start_date'] + document.getElementById('planEndDate').value = data['end_date'] + document.getElementById('plannerModalButton').innerHTML = "Save" + document.getElementById('plannerModalButton').onclick = async function () { + var planner_select = document.getElementById('planUUID') + planner_uuid = planner_select.value + plan_name = planner_select.options[planner_select.selectedIndex].text + startDate = document.getElementById('planStartDate').value + endDate = document.getElementById('planEndDate').value + site_planners[planner_uuid] = { + start_date: startDate, + end_date: endDate, + plan_uuid: planner_uuid, + plan_name: plan_name + } + + await generatePlannerTable() + UIkit.modal(document.getElementById('plannerModal')).hide() + } + + UIkit.modal(document.getElementById('plannerModal')).show() +} + + +async function deletePlan(plannerUUID) { + delete site_planners[plannerUUID] + await generatePlannerTable() +} + +async function generatePlannerTable() { + let plannerTableBody = document.getElementById('plannerTableBody') + plannerTableBody.innerHTML = "" + + for(const key in site_planners){ + if(site_planners.hasOwnProperty(key)){ + let tableRow = document.createElement('tr') + + + let nameCell = document.createElement('td') + nameCell.innerHTML = `${site_planners[key].plan_name}` + + let startCell = document.createElement('td') + startCell.innerHTML = `${site_planners[key].start_date}` + + let endCell = document.createElement('td') + endCell.innerHTML = `${site_planners[key].end_date}` + + let opCell = document.createElement('td') + + let editButton = document.createElement('button') + editButton.setAttribute('class', 'uk-button uk-button-default uk-button-small') + editButton.setAttribute('uk-tooltip', 'Edits this rows plan dates.') + editButton.innerHTML = "Edit" + editButton.onclick = async function() {await editPlanner(site_planners[key].plan_uuid)} + + + let removeButton = document.createElement('button') + removeButton.setAttribute('class', 'uk-button uk-button-default uk-button-small') + removeButton.setAttribute('uk-tooltip', 'Removes Shopping List from the saved shopping lists') + removeButton.innerHTML = "Remove" + removeButton.onclick = async function() {await deletePlan(site_planners[key].plan_uuid)} + + opCell.append(editButton, removeButton) + + tableRow.append(nameCell, startCell, endCell, opCell) + plannerTableBody.append(tableRow) + + } + } + +} // Generate Functions async function postGenerateList() { @@ -1088,7 +1212,8 @@ async function postGenerateList() { calculated_items: Object.keys(calculated_items), recipes: Object.keys(recipes), full_system_calculated: full_sku_enabled, - shopping_lists: Object.keys(shopping_lists) + shopping_lists: Object.keys(shopping_lists), + site_plans: Object.values(site_planners) } const response = await fetch(`/shopping-lists/api/postGeneratedList`, { @@ -1098,4 +1223,5 @@ async function postGenerateList() { }, body: JSON.stringify(data), }); + location.href = "/shopping-lists" } \ No newline at end of file diff --git a/application/shoppinglists/static/js/shoppingListViewHandler.js b/application/shoppinglists/static/js/shoppingListViewHandler.js index bfae935..5351385 100644 --- a/application/shoppinglists/static/js/shoppingListViewHandler.js +++ b/application/shoppinglists/static/js/shoppingListViewHandler.js @@ -23,29 +23,54 @@ async function replenishLineTable(sl_items){ listItemsTableBody.innerHTML = "" console.log(sl_items) - for(let i = 0; i < sl_items.length; i++){ - let tableRow = document.createElement('tr') + let grouped = sl_items.reduce((accumen, item) => { + if (!accumen[item.item_type]) { + accumen[item.item_type] = []; + } + accumen[item.item_type].push(item); + return accumen; + }, {}); + + console.log(grouped) + for(let key in grouped){ + console.log(key) + let items = grouped[key] + let headerRow = document.createElement('tr') + let headerCell = document.createElement('td') + headerCell.colSpan = 3; + headerCell.textContent = key.toUpperCase(); + headerCell.className = 'type-header'; + headerCell.style = `font-weight: bold;background: #eee; text-align: left;` + headerRow.appendChild(headerCell); + listItemsTableBody.appendChild(headerRow); - let checkboxCell = document.createElement('td') - checkboxCell.innerHTML = `` - checkboxCell.onclick = async function (event) { - await updateListItemState(sl_items[i].list_item_uuid, event.target.checked) + for(let i = 0; i < items.length; i++){ + console.log(items) + let tableRow = document.createElement('tr') + let item = items[i] + let checkboxCell = document.createElement('td') + checkboxCell.innerHTML = `` + checkboxCell.onclick = async function (event) { + console.log(item) + await updateListItemState(item.list_item_uuid, event.target.checked) + } + + namefield = items[i].item_name + if(items[i].links.hasOwnProperty('main')){ + namefield = `${item.item_name}` + } + + let nameCell = document.createElement('td') + nameCell.innerHTML = namefield + + let qtyuomCell = document.createElement('td') + qtyuomCell.innerHTML = `${item.qty} ${item.uom.fullname}` + + checkboxCell.checked = item.list_item_state + tableRow.append(checkboxCell, nameCell, qtyuomCell) + listItemsTableBody.append(tableRow) } - namefield = sl_items[i].item_name - if(sl_items[i].links.hasOwnProperty('main')){ - namefield = `${sl_items[i].item_name}` - } - - let nameCell = document.createElement('td') - nameCell.innerHTML = namefield - - let qtyuomCell = document.createElement('td') - qtyuomCell.innerHTML = `${sl_items[i].qty} ${sl_items[i].uom.fullname}` - - checkboxCell.checked = sl_items[i].list_item_state - tableRow.append(checkboxCell, nameCell, qtyuomCell) - listItemsTableBody.append(tableRow) } } diff --git a/application/shoppinglists/templates/generate.html b/application/shoppinglists/templates/generate.html index 3d8e855..a73cada 100644 --- a/application/shoppinglists/templates/generate.html +++ b/application/shoppinglists/templates/generate.html @@ -176,11 +176,11 @@
| Plan Name | +Start Date | +End Date | +Operations | +
|---|
Site Planner Operators allow you to select specific plans and a date range on that planner to insert any planned recipes into the list. This is best + utilized without the Recipe Operator, but it has been allowed to have both. +
+| + | + |
|---|---|
| Plan | ++ + | +
| Start Date | ++ |
| End Date | ++ |
+ + +
+