From 02ebc63050c1632636381581097c9585887d3f82 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 9 Feb 2026 17:24:55 +0000 Subject: [PATCH] Ahmed semifinal version --- core/__pycache__/urls.cpython-311.pyc | Bin 1019 -> 1550 bytes core/__pycache__/views.cpython-311.pyc | Bin 9631 -> 17632 bytes core/templates/base.html | 5 +- core/templates/core/dashboard.html | 48 ++++++- core/templates/core/edit_menu_item.html | 134 +++++++++++++++++++ core/templates/core/menu_management.html | 161 +++++++++++++++++++++++ core/templates/core/user_management.html | 8 +- core/urls.py | 5 + core/views.py | 142 ++++++++++++++++++++ 9 files changed, 499 insertions(+), 4 deletions(-) create mode 100644 core/templates/core/edit_menu_item.html create mode 100644 core/templates/core/menu_management.html diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index a262fa5489bcd8db96258a6979516a70bcd50acd..a1dbf7f8406cbfd569c864ffab8be753ec5c64cd 100644 GIT binary patch delta 569 zcmey(-p8Z9oR^o20SKJLx-$1NGcY^`abQ3Y%J}?kqq;5Q#0{+cqN&VTJTU1Lv55;r z`Ne@kyl^22kdP!$hz~9#1rm}53h~2*WPn1NvOpmLxRBf$wq-#3R|7Ewr1M4zrpN~~ zXevz3W4u^jC6$tzlUkChTUwl2q;Hd%S7HTX#b>72>EGgp$i;(1s)TbB^Agijbu;tQ zi&9fEQ}arS^=}D51mlqet9YOab5rw5fo4EefOu6BK#e83Af|3+NouaXO#sNkf^48& zd?2ZKkW@TKs!FOPKRrDs6J5AC$lj>t-ry-(cp4{Q*}Y<6?KapwHH`?FS7VvVey5E%PnBN zqF`~sKKKGl$VHZrD=Z;ki4M6LqE{rfHppIJvA@V-e}%;!s$O|P=oMAV3l0GnSOPDy l1YThYM7F@?f_KUVmeh+ZsaIH1KS=ZOGjTV7VUaA*NdQ9sr%(U@ delta 108 zcmeC<`OU7roR^o20SJ1zx-w;%7#JRdI55BiWqfwpsBX*1mCg~xlOh_-peZ(4iRmJ< rpC<3*L(Dmo-C2COrGP3Kfw=e`!{qraPX#^*a`H1ZaD!lx1W+6RIGGhv diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index aaa26e7cbf66a866d638d63ec5fc8ef376578f25..fa3e5c8c58d3b90410b8295e81dc48e3228f4ffa 100644 GIT binary patch delta 7112 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%0Ael{q2ep23{{{7=bbhQ-9=TH zekqe$Y(j9Afh5ZW!0Krzrl+D?OOaPmOvM|6+z#+e^|~Oj+ELVp#Oj-HP6MVwYN-Y~ z&lkl6p#mMVP&u0tWK)3-<}e!t03f`XJEgbtOrT?a*Q^2^_^kjsI0oVYEEAQ3Yz8{} zG%chm(1Bca^ESu;_R;@w>~oll3jheT+omeG5V=jR>MoqG$R7~^Y3SuRa%Bkfmsmw; zpha4IuY3j}4Zncor$7+B%v|6AUe%x%HOQ7wi>gvJjedsz6#KWR-~_T-VN69Rh|kpf zH^@dYv}>5FF*?KZfLg}lnNkv)WCI$~P*IDDRm#w%8nrwVQt&r$h%%Ik7f0m(47r;~ z{tAhTW)SFPz8TYKkfvUPG(X3lrA6>R!>Wo|7Lof5Ng1u68!ICf?W)LD{a*N6Fh22L zpqTvkB4%;6|4r8tFFE>TN1x*8n;ZV)h^3hDdf25cvZq(^^v;{F+VX%Yp38Z=v)=Bd z-LiLw;@tsad!7mZ+?r#yXPNEG{(sQP8D>yn2ItL+$)68&-k8h=?5zVa^OWJ z@FKRfR<+21eM(^8ijWPc-CDoY#{WNo7y2rMe>cFFh4_tWmCKL?ovVmoN&v==00u~& z_J2J3<58Io0YH%G5I`k&K%v`lj$CX?#a=+vh*&Q-lNcaBzn(UXVYY?U89tp*e}>DsfD(xWbIOPL-_ViXR~<%bv!mIOZ8zPaZSh4##Ak z0%X1wj@g-a2LQ+9gI!AS`CM>MHn>L)_A9~uf|1$-$OZ@?K14P^pjt#W;l`m5alilN zLFZ75{(;2`wFfQKkk9zQr$;_WBG0INsD2nQO{8);4n>Dc-FCL3p z5YuWRGi0=;*49uUIKePPZxtGdU+eem$?~Y1=Bk-)8)qa7>)R00ANz@Qta*xQCcE!>zQSIuz z7yJCZ-?a^p>*=ENtFq>;Rj1TW~-Yeg+<1P@H`pNsoDy!F2X7EwX+pYDx! zM0A-5p>q{<^<=0(09n^VRUw_4q(yCWlWhe8NVyFqTE89=Han1V8%o|H)sEfR2&CMI YlGn9HBiR9Y7wPXPw{D13xptfXzr4$`2mk;8 delta 418 zcmXYpPfG$(6ve&%!wkjJ%%;L2N*Rz5B9g!|T^3XjW*H?qK5%42c{7oT6uOC|O}w_% zvRcOMTGzG@;Hqy>3s)_occlyO$2s?$cR#P-VblBQ@whE~F5Nx#H+bU>LH;Fr0M4>! z*CGk#0N#BdvDfh}$g&at8|<+U{|e-pD-aIm_&-|Z=-X9;)aX9?EL#nPfv}@M3>s!H z(6UYC`Mx!#)G3Kmq7b!V$Q7fe(Hxr%%{VdTG8GB~H4C9HfGXP-7NNwt!U8DlNl1^D zuycxNvVkxMZF6!uVUL{SVMkUcRBZXgqDD!vd?u2nsvEj2St;i9f*IkusBqc$>O>=o zQJ_RC8l+5H^?;(f%KUQUG0{{R3 diff --git a/core/templates/base.html b/core/templates/base.html index e6c9160..33c17ee 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -169,6 +169,9 @@ + @@ -203,7 +206,7 @@ {% endfor %} {% endif %} diff --git a/core/templates/core/dashboard.html b/core/templates/core/dashboard.html index 5bed782..3f3ef5d 100644 --- a/core/templates/core/dashboard.html +++ b/core/templates/core/dashboard.html @@ -8,7 +8,11 @@

Overview of your restaurant's performance and stock.

- Manage Users + + Manage Menu + Manage Users Go to POS
@@ -114,4 +118,44 @@ -{% endblock %} \ No newline at end of file + + + +{% endblock %} diff --git a/core/templates/core/edit_menu_item.html b/core/templates/core/edit_menu_item.html new file mode 100644 index 0000000..294aba5 --- /dev/null +++ b/core/templates/core/edit_menu_item.html @@ -0,0 +1,134 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+

Edit Menu Item

+

Updating: {{ menu_item.name }}

+
+ +
+ +
+
+
+
+
+ {% csrf_token %} +
+
+ + +
+
+ + +
+
+ + +
+
+ + + {% if menu_item.image_url %} +
+ Preview +
+ {% endif %} +
+
+
+ + +
+
+ +
+
+
Recipe / Ingredients
+
+ {% for current in current_ingredients %} +
+
+ + +
+
+ + +
+
+ +
+
+ {% empty %} +
+
+ + +
+
+ + +
+
+ +
+
+ {% endfor %} +
+ +
+ +
+ +
+
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/core/templates/core/menu_management.html b/core/templates/core/menu_management.html new file mode 100644 index 0000000..c99220d --- /dev/null +++ b/core/templates/core/menu_management.html @@ -0,0 +1,161 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+

Menu Management

+

Create and edit your restaurant's menu items.

+
+ +
+ +
+ +
+
+
+

Add New Item

+
+ {% csrf_token %} +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
Recipe / Ingredients
+
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+ + +
+
+
+

Current Menu

+
+ + + + + + + + + + + {% for item in menu_items %} + + + + + + + {% empty %} + + + + {% endfor %} + +
ItemPriceStatusActions
+
+ {% if item.image_url %} + {{ item.name }} + {% else %} +
+ +
+ {% endif %} +
+
{{ item.name }}
+
{{ item.ingredients.count }} ingredients
+
+
+
{{ item.price|floatformat:2 }} EGP + {% if item.is_active %} + Active + {% else %} + Inactive + {% endif %} + + +
+

No menu items found. Add your first one!

+
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/core/templates/core/user_management.html b/core/templates/core/user_management.html index 19a0392..27275f2 100644 --- a/core/templates/core/user_management.html +++ b/core/templates/core/user_management.html @@ -97,6 +97,12 @@ at {{ u.date_joined|date:"H:i" }} + {% if u.id != request.user.id %} + + + + {% endif %} @@ -119,4 +125,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/urls.py b/core/urls.py index 98d3001..94ac2f9 100644 --- a/core/urls.py +++ b/core/urls.py @@ -10,4 +10,9 @@ urlpatterns = [ path('receipt//', views.receipt_view, name='receipt'), path('dashboard/', views.dashboard_view, name='dashboard'), path('manage-users/', views.manage_users, name='manage_users'), + path('delete-user//', views.delete_user, name='delete_user'), + path('manage-ingredients/', views.manage_ingredients, name='manage_ingredients'), + path('manage-menu/', views.manage_menu, name='manage_menu'), + path('edit-menu-item//', views.edit_menu_item, name='edit_menu_item'), + path('toggle-menu-item//', views.toggle_menu_item_status, name='toggle_menu_item_status'), ] diff --git a/core/views.py b/core/views.py index 13d1dae..c408930 100644 --- a/core/views.py +++ b/core/views.py @@ -144,3 +144,145 @@ def manage_users(request): messages.error(request, "Please fill all fields.") return render(request, 'core/user_management.html', {'users': users}) + +@manager_required +def delete_user(request, user_id): + """Manager only: Delete a user account.""" + if request.user.id == user_id: + messages.error(request, "You cannot delete your own account.") + return redirect('manage_users') + + user = get_object_or_404(User, id=user_id) + username = user.username + user.delete() + messages.success(request, f"User {username} has been deleted.") + return redirect('manage_users') + +@manager_required +def manage_ingredients(request): + """Manager only: View and add ingredients.""" + if request.method == 'POST': + name = request.POST.get('name') + stock_quantity = request.POST.get('stock_quantity', 0) + unit = request.POST.get('unit', 'grams') + + if name: + if Ingredient.objects.filter(name__iexact=name).exists(): + messages.error(request, f"Ingredient '{name}' already exists.") + else: + Ingredient.objects.create( + name=name, + stock_quantity=stock_quantity, + unit=unit + ) + messages.success(request, f"Ingredient '{name}' added successfully.") + else: + messages.error(request, "Ingredient name is required.") + return redirect('dashboard') + + return redirect('dashboard') + +@manager_required +def manage_menu(request): + """Manager only: View and create menu items.""" + menu_items = MenuItem.objects.all() + ingredients = Ingredient.objects.all() + + if request.method == 'POST': + name = request.POST.get('name') + price = request.POST.get('price') + description = request.POST.get('description', '') + image_url = request.POST.get('image_url', '') + is_active = request.POST.get('is_active') == 'on' + + # Handle ingredient IDs and quantities from the form + ingredient_ids = request.POST.getlist('ingredients') + quantities = request.POST.getlist('quantities') + + if name and price: + try: + with transaction.atomic(): + menu_item = MenuItem.objects.create( + name=name, + price=price, + description=description, + image_url=image_url, + is_active=is_active + ) + + # Add ingredients if provided + for i_id, qty in zip(ingredient_ids, quantities): + if i_id and qty and float(qty) > 0: + ingredient = get_object_or_404(Ingredient, id=i_id) + MenuItemIngredient.objects.create( + menu_item=menu_item, + ingredient=ingredient, + quantity_required=qty + ) + + messages.success(request, f"Menu item '{name}' created successfully.") + return redirect('manage_menu') + except Exception as e: + messages.error(request, f"Error creating menu item: {str(e)}") + else: + messages.error(request, "Name and price are required.") + + return render(request, 'core/menu_management.html', { + 'menu_items': menu_items, + 'ingredients': ingredients + }) + +@manager_required +def edit_menu_item(request, pk): + """Manager only: Edit an existing menu item.""" + menu_item = get_object_or_404(MenuItem, pk=pk) + ingredients = Ingredient.objects.all() + + if request.method == 'POST': + menu_item.name = request.POST.get('name') + menu_item.price = request.POST.get('price') + menu_item.description = request.POST.get('description', '') + menu_item.image_url = request.POST.get('image_url', '') + menu_item.is_active = request.POST.get('is_active') == 'on' + + ingredient_ids = request.POST.getlist('ingredients') + quantities = request.POST.getlist('quantities') + + if menu_item.name and menu_item.price: + try: + with transaction.atomic(): + menu_item.save() + + # Update ingredients: Clear existing and re-add + menu_item.ingredients.all().delete() + for i_id, qty in zip(ingredient_ids, quantities): + if i_id and qty and float(qty) > 0: + ingredient = get_object_or_404(Ingredient, id=i_id) + MenuItemIngredient.objects.create( + menu_item=menu_item, + ingredient=ingredient, + quantity_required=qty + ) + + messages.success(request, f"Menu item '{menu_item.name}' updated successfully.") + return redirect('manage_menu') + except Exception as e: + messages.error(request, f"Error updating menu item: {str(e)}") + else: + messages.error(request, "Name and price are required.") + + return render(request, 'core/edit_menu_item.html', { + 'menu_item': menu_item, + 'ingredients': ingredients, + 'current_ingredients': menu_item.ingredients.all() + }) + +@manager_required +def toggle_menu_item_status(request, pk): + """Manager only: Toggle menu item active/inactive status.""" + menu_item = get_object_or_404(MenuItem, pk=pk) + menu_item.is_active = not menu_item.is_active + menu_item.save() + status = "activated" if menu_item.is_active else "deactivated" + messages.success(request, f"Menu item '{menu_item.name}' {status}.") + return redirect('manage_menu')