sandwitches 2.5.4__py3-none-any.whl → 2.5.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sandwitches/admin.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from django.contrib import admin
2
2
  from django.contrib.auth import get_user_model
3
3
  from django.contrib.auth.admin import UserAdmin
4
- from .models import Recipe, Tag, Rating, Setting, Order
4
+ from .models import Recipe, Tag, Rating, Setting, Order, OrderItem
5
5
  from django.utils.html import format_html
6
6
  from import_export import resources
7
7
  from import_export.admin import ImportExportModelAdmin
@@ -81,18 +81,23 @@ class RatingAdmin(ImportExportModelAdmin):
81
81
  resource_classes = [RatingResource]
82
82
 
83
83
 
84
+ class OrderItemInline(admin.TabularInline):
85
+ model = OrderItem
86
+ extra = 0
87
+
88
+
84
89
  @admin.register(Order)
85
90
  class OrderAdmin(ImportExportModelAdmin):
86
91
  resource_classes = [OrderResource]
92
+ inlines = [OrderItemInline]
87
93
  list_display = (
88
94
  "id",
89
95
  "user",
90
- "recipe",
91
96
  "status",
92
97
  "completed",
93
98
  "total_price",
94
99
  "created_at",
95
100
  )
96
101
  list_filter = ("status", "completed", "created_at")
97
- search_fields = ("user__username", "recipe__title")
102
+ search_fields = ("user__username",)
98
103
  readonly_fields = ("total_price", "created_at", "updated_at")
sandwitches/api.py CHANGED
@@ -220,9 +220,25 @@ def get_tag(request, tag_id: int):
220
220
 
221
221
  @api.post("v1/orders", auth=django_auth, response={201: OrderSchema, 400: Error})
222
222
  def create_order(request, payload: CreateOrderSchema):
223
+ from .models import OrderItem
224
+ from django.db import transaction
225
+ from .tasks import notify_order_submitted, send_gotify_notification
226
+
223
227
  recipe = get_object_or_404(Recipe, id=payload.recipe_id)
224
228
  try:
225
- order = Order.objects.create(user=request.user, recipe=recipe) # ty:ignore[unresolved-attribute]
229
+ with transaction.atomic():
230
+ order = Order.objects.create(user=request.user) # ty:ignore[unresolved-attribute]
231
+ OrderItem.objects.create(order=order, recipe=recipe, quantity=1) # ty:ignore[unresolved-attribute]
232
+ order.total_price = recipe.price
233
+ order.save()
234
+
235
+ notify_order_submitted.enqueue(order_id=order.pk)
236
+ send_gotify_notification.enqueue(
237
+ title="New Order Received",
238
+ message=f"Order #{order.pk} by {request.user.username}. Total: {order.total_price}€",
239
+ priority=6,
240
+ )
241
+
226
242
  return 201, order
227
243
  except (ValidationError, ValueError) as e:
228
244
  return 400, {"message": str(e)}
@@ -0,0 +1,58 @@
1
+ # Generated by Django 6.0.1 on 2026-02-02 19:20
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ("sandwitches", "0017_setting_gotify_token_setting_gotify_url"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name="order",
15
+ name="recipe",
16
+ ),
17
+ migrations.AlterField(
18
+ model_name="order",
19
+ name="total_price",
20
+ field=models.DecimalField(decimal_places=2, default=0, max_digits=6),
21
+ ),
22
+ migrations.CreateModel(
23
+ name="OrderItem",
24
+ fields=[
25
+ (
26
+ "id",
27
+ models.BigAutoField(
28
+ auto_created=True,
29
+ primary_key=True,
30
+ serialize=False,
31
+ verbose_name="ID",
32
+ ),
33
+ ),
34
+ ("quantity", models.PositiveIntegerField(default=1)),
35
+ ("price", models.DecimalField(decimal_places=2, max_digits=6)),
36
+ (
37
+ "order",
38
+ models.ForeignKey(
39
+ on_delete=django.db.models.deletion.CASCADE,
40
+ related_name="items",
41
+ to="sandwitches.order",
42
+ ),
43
+ ),
44
+ (
45
+ "recipe",
46
+ models.ForeignKey(
47
+ on_delete=django.db.models.deletion.CASCADE,
48
+ related_name="order_items",
49
+ to="sandwitches.recipe",
50
+ ),
51
+ ),
52
+ ],
53
+ options={
54
+ "verbose_name": "Order Item",
55
+ "verbose_name_plural": "Order Items",
56
+ },
57
+ ),
58
+ ]
sandwitches/models.py CHANGED
@@ -276,10 +276,9 @@ class Order(models.Model):
276
276
  user = models.ForeignKey(
277
277
  settings.AUTH_USER_MODEL, related_name="orders", on_delete=models.CASCADE
278
278
  )
279
- recipe = models.ForeignKey(Recipe, related_name="orders", on_delete=models.CASCADE)
280
279
  status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="PENDING")
281
280
  completed = models.BooleanField(default=False)
282
- total_price = models.DecimalField(max_digits=6, decimal_places=2)
281
+ total_price = models.DecimalField(max_digits=6, decimal_places=2, default=0)
283
282
  created_at = models.DateTimeField(auto_now_add=True)
284
283
  updated_at = models.DateTimeField(auto_now=True)
285
284
 
@@ -289,40 +288,54 @@ class Order(models.Model):
289
288
  verbose_name_plural = "Orders"
290
289
 
291
290
  def save(self, *args, **kwargs):
292
- if not self.recipe.price: # ty:ignore[possibly-missing-attribute]
293
- raise ValueError("Cannot order a recipe without a price.")
294
- if not self.total_price:
295
- self.total_price = self.recipe.price # ty:ignore[possibly-missing-attribute]
296
-
297
291
  is_new = self.pk is None
298
- if is_new:
299
- # We use select_for_update to lock the row and prevent race conditions
300
- # However, since 'self.recipe' is already fetched, we need to re-fetch it with lock if we want to be strict.
301
- # For simplicity in this context, we will reload it or trust the current instance but ideally:
302
-
303
- # We need to wrap this in a transaction if not already
304
- # But simple increment logic:
305
- if (
306
- self.recipe.max_daily_orders is not None # ty:ignore[possibly-missing-attribute]
307
- and self.recipe.daily_orders_count >= self.recipe.max_daily_orders # ty:ignore[possibly-missing-attribute]
308
- ):
309
- raise ValidationError("Daily order limit reached for this recipe.")
310
-
311
- self.recipe.daily_orders_count += 1 # ty:ignore[possibly-missing-attribute]
312
- self.recipe.save(update_fields=["daily_orders_count"]) # ty:ignore[possibly-missing-attribute]
313
-
314
292
  super().save(*args, **kwargs)
315
-
316
293
  if is_new:
317
294
  notify_order_submitted.enqueue(order_id=self.pk)
318
295
  send_gotify_notification.enqueue(
319
296
  title="New Order Received",
320
- message=f"Order #{self.pk} for '{self.recipe.title}' by {self.user.username}. Total: {self.total_price}€",
297
+ message=f"Order #{self.pk} by {self.user.username}. Total: {self.total_price}€",
321
298
  priority=6,
322
299
  )
323
300
 
324
301
  def __str__(self):
325
- return f"Order #{self.pk} - {self.user} - {self.recipe}"
302
+ return f"Order #{self.pk} - {self.user}"
303
+
304
+
305
+ class OrderItem(models.Model):
306
+ order = models.ForeignKey(Order, related_name="items", on_delete=models.CASCADE)
307
+ recipe = models.ForeignKey(
308
+ Recipe, related_name="order_items", on_delete=models.CASCADE
309
+ )
310
+ quantity = models.PositiveIntegerField(default=1)
311
+ price = models.DecimalField(max_digits=6, decimal_places=2)
312
+
313
+ class Meta:
314
+ verbose_name = "Order Item"
315
+ verbose_name_plural = "Order Items"
316
+
317
+ def save(self, *args, **kwargs):
318
+ if not self.price:
319
+ self.price = self.recipe.price
320
+
321
+ is_new = self.pk is None
322
+ if is_new:
323
+ if (
324
+ self.recipe.max_daily_orders is not None
325
+ and self.recipe.daily_orders_count + self.quantity
326
+ > self.recipe.max_daily_orders
327
+ ):
328
+ raise ValidationError(
329
+ f"Daily order limit reached for {self.recipe.title}."
330
+ )
331
+
332
+ self.recipe.daily_orders_count += self.quantity
333
+ self.recipe.save(update_fields=["daily_orders_count"])
334
+
335
+ super().save(*args, **kwargs)
336
+
337
+ def __str__(self):
338
+ return f"{self.quantity}x {self.recipe.title} in Order #{self.order.pk}"
326
339
 
327
340
 
328
341
  class CartItem(models.Model):
sandwitches/tasks.py CHANGED
@@ -49,7 +49,11 @@ def notify_order_submitted(order_id):
49
49
  from .models import Order
50
50
 
51
51
  try:
52
- order = Order.objects.select_related("user", "recipe").get(pk=order_id) # ty:ignore[unresolved-attribute]
52
+ order = (
53
+ Order.objects.select_related("user") # ty:ignore[unresolved-attribute]
54
+ .prefetch_related("items__recipe")
55
+ .get(pk=order_id)
56
+ ) # ty:ignore[unresolved-attribute]
53
57
  except Order.DoesNotExist: # ty:ignore[unresolved-attribute]
54
58
  logging.warning(f"Order {order_id} not found. Skipping notification.")
55
59
  return
@@ -59,22 +63,38 @@ def notify_order_submitted(order_id):
59
63
  logging.warning(f"User {user.username} has no email. Skipping notification.")
60
64
  return
61
65
 
62
- recipe = order.recipe
63
- subject = _("Order Confirmation: %(recipe_title)s") % {"recipe_title": recipe.title}
66
+ items = order.items.all()
67
+ if not items:
68
+ logging.warning(f"Order {order_id} has no items. Skipping notification.")
69
+ return
70
+
71
+ # Construct item list string
72
+ item_lines = []
73
+ for item in items:
74
+ item_lines.append(f"- {item.quantity}x {item.recipe.title}")
75
+
76
+ items_summary = "\n".join(item_lines)
77
+ items_html_list = "".join(
78
+ [f"<li>{item.quantity}x {item.recipe.title}</li>" for item in items]
79
+ )
80
+
81
+ subject = _("Order Confirmation: Order #%(order_id)s") % {"order_id": order.id}
64
82
  from_email = getattr(settings, "EMAIL_FROM_ADDRESS")
65
83
 
66
84
  context_data = {
67
85
  "user_name": user.get_full_name() or user.username,
68
- "recipe_title": recipe.title,
86
+ "items_summary": items_summary,
69
87
  "order_id": order.id,
70
88
  "total_price": order.total_price,
89
+ "items_html_list": items_html_list,
71
90
  }
72
91
 
73
92
  text_content = (
74
93
  _(
75
94
  "Hello %(user_name)s,\n\n"
76
- "Your order for %(recipe_title)s has been successfully submitted!\n"
95
+ "Your order has been successfully submitted!\n"
77
96
  "Order ID: %(order_id)s\n"
97
+ "Items:\n%(items_summary)s\n"
78
98
  "Total Price: %(total_price)s\n\n"
79
99
  "Thank you for ordering with Sandwitches.\n"
80
100
  )
@@ -86,9 +106,10 @@ def notify_order_submitted(order_id):
86
106
  "<div style='font-family: sans-serif;'>"
87
107
  "<h2>Order Confirmation</h2>"
88
108
  "<p>Hello <strong>%(user_name)s</strong>,</p>"
89
- "<p>Your order for <strong>%(recipe_title)s</strong> has been successfully submitted!</p>"
109
+ "<p>Your order has been successfully submitted!</p>"
90
110
  "<ul>"
91
111
  "<li>Order ID: %(order_id)s</li>"
112
+ "%(items_html_list)s"
92
113
  "<li>Total Price: %(total_price)s</li>"
93
114
  "</ul>"
94
115
  "<p>Thank you for ordering with Sandwitches.</p>"
@@ -16,7 +16,7 @@
16
16
  <tr>
17
17
  <th>ID</th>
18
18
  <th>{% trans "User" %}</th>
19
- <th>{% trans "Recipe" %}</th>
19
+ <th>{% trans "Items" %}</th>
20
20
  <th>{% trans "Price" %}</th>
21
21
  <th>{% trans "Status" %}</th>
22
22
  <th>{% trans "Date" %}</th>
@@ -12,7 +12,11 @@
12
12
  <span class="max">{{ order.user.username }}</span>
13
13
  </div>
14
14
  </td>
15
- <td>{{ order.recipe.title }}</td>
15
+ <td>
16
+ {% for item in order.items.all %}
17
+ <div>{{ item.quantity }}x {{ item.recipe.title }}</div>
18
+ {% endfor %}
19
+ </td>
16
20
  <td>€ {{ order.total_price }} </td>
17
21
  <td>
18
22
  <form method="post" action="{% url 'admin_order_update_status' order.pk %}" class="row align-center no-space">
@@ -35,22 +35,24 @@
35
35
  <div class="divider"></div>
36
36
 
37
37
  <h6>{% trans "Item Details" %}</h6>
38
+ {% for item in order.items.all %}
38
39
  <div class="row">
39
40
  <div class="max">
40
- <a href="{% url 'recipe_detail' order.recipe.slug %}" class="bold primary-text">{{ order.recipe.title }}</a>
41
- <p class="small-text">{{ order.recipe.description|truncatewords:20 }}</p>
41
+ <a href="{% url 'recipe_detail' item.recipe.slug %}" class="bold primary-text">{{ item.quantity }}x {{ item.recipe.title }}</a>
42
+ <p class="small-text">{{ item.recipe.description|truncatewords:20 }}</p>
42
43
  </div>
43
44
  <div class="min">
44
- <span class="bold">{{ order.total_price }} €</span>
45
+ <!-- Calculate item total: price * quantity -->
46
+ <span class="bold">{% widthratio item.price 1 item.quantity %} €</span>
45
47
  </div>
46
48
  </div>
47
49
 
48
- {% if order.recipe.image %}
50
+ {% if item.recipe.image %}
49
51
  <div class="space"></div>
50
- <img src="{{ order.recipe.image.url }}" class="responsive round border" alt="{{ order.recipe.title }}">
52
+ <img src="{{ item.recipe.image.url }}" class="responsive round border" alt="{{ item.recipe.title }}" style="max-height: 200px; object-fit: cover;">
51
53
  {% endif %}
52
-
53
54
  <div class="divider"></div>
55
+ {% endfor %}
54
56
 
55
57
  <div class="row">
56
58
  <div class="max text-right">
@@ -136,7 +136,16 @@
136
136
  <tr>
137
137
  <td>{{ order.id }}</td>
138
138
  <td>
139
- <a href="{% url 'recipe_detail' order.recipe.slug %}">{{ order.recipe.title }}</a>
139
+ {% with first_item=order.items.first %}
140
+ {% if first_item %}
141
+ <a href="{% url 'recipe_detail' first_item.recipe.slug %}">{{ first_item.recipe.title }}</a>
142
+ {% if order.items.count > 1 %}
143
+ <span class="small-text">(+{{ order.items.count|add:"-1" }})</span>
144
+ {% endif %}
145
+ {% else %}
146
+ <span>{% trans "No items" %}</span>
147
+ {% endif %}
148
+ {% endwith %}
140
149
  </td>
141
150
  <td>{{ order.created_at|date:"d/m/Y" }}</td>
142
151
  <td>
sandwitches/urls.py CHANGED
@@ -50,9 +50,6 @@ urlpatterns = [
50
50
  path("cart/checkout/", views.checkout_cart, name="checkout_cart"),
51
51
  path("", views.index, name="index"),
52
52
  path("feeds/latest/", LatestRecipesFeed(), name="latest_recipes_feed"),
53
- path(
54
- "feeds/latest/", LatestRecipesFeed(), name="latest_recipes_feed"
55
- ), # Add this line
56
53
  ]
57
54
 
58
55
  urlpatterns += i18n_patterns(
sandwitches/views.py CHANGED
@@ -9,7 +9,11 @@ from django.contrib.auth.decorators import login_required
9
9
  from django.contrib.admin.views.decorators import staff_member_required
10
10
  from django.utils.translation import gettext as _
11
11
  from django.utils import translation
12
- from .models import Recipe, Rating, Tag, Order, CartItem, Setting
12
+ from .models import Recipe, Rating, Tag, Order, CartItem, Setting, OrderItem
13
+ from .tasks import (
14
+ notify_order_submitted,
15
+ send_gotify_notification,
16
+ )
13
17
  from .utils import ORDER_DB
14
18
  from .forms import (
15
19
  RecipeForm,
@@ -542,7 +546,8 @@ def admin_order_update_status(request, pk):
542
546
  @staff_member_required
543
547
  def admin_order_list(request):
544
548
  orders = (
545
- Order.objects.select_related("user", "recipe") # ty:ignore[unresolved-attribute]
549
+ Order.objects.select_related("user") # ty:ignore[unresolved-attribute]
550
+ .prefetch_related("items__recipe")
546
551
  .all()
547
552
  .order_by("-created_at")
548
553
  )
@@ -626,12 +631,27 @@ def order_recipe(request, pk):
626
631
  """
627
632
  Create an order for the given recipe by the logged-in user.
628
633
  """
634
+ from django.db import transaction
635
+ from .models import OrderItem
636
+
629
637
  recipe = get_object_or_404(Recipe, pk=pk)
630
638
  if request.method != "POST":
631
639
  return redirect("recipe_detail", slug=recipe.slug)
632
640
 
633
641
  try:
634
- order = Order.objects.create(user=request.user, recipe=recipe) # ty:ignore[unresolved-attribute]
642
+ with transaction.atomic():
643
+ order = Order.objects.create(user=request.user) # ty:ignore[unresolved-attribute]
644
+ OrderItem.objects.create(order=order, recipe=recipe, quantity=1) # ty:ignore[unresolved-attribute]
645
+ order.total_price = recipe.price
646
+ order.save()
647
+
648
+ notify_order_submitted.enqueue(order_id=order.pk)
649
+ send_gotify_notification.enqueue(
650
+ title="New Order Received",
651
+ message=f"Order #{order.pk} by {request.user.username}. Total: {order.total_price}€",
652
+ priority=6,
653
+ )
654
+
635
655
  logging.debug(f"Created {order}")
636
656
  messages.success(
637
657
  request,
@@ -918,7 +938,7 @@ def user_profile(request):
918
938
  else:
919
939
  form = UserProfileForm(instance=request.user)
920
940
 
921
- orders = request.user.orders.select_related("recipe").all()
941
+ orders = request.user.orders.prefetch_related("items__recipe").all()
922
942
 
923
943
  # Filtering
924
944
  status_filter = request.GET.get("status")
@@ -1052,39 +1072,82 @@ def update_cart_quantity(request, pk):
1052
1072
 
1053
1073
  @login_required
1054
1074
  def checkout_cart(request):
1055
- cart_items = CartItem.objects.filter(user=request.user) # ty:ignore[unresolved-attribute]
1075
+ if request.method != "POST":
1076
+ return redirect("view_cart")
1077
+
1078
+ cart_items = CartItem.objects.filter(user=request.user).select_related("recipe") # ty:ignore[unresolved-attribute]
1056
1079
  if not cart_items.exists():
1057
1080
  messages.error(request, _("Your cart is empty."))
1058
1081
  return redirect("view_cart")
1059
1082
 
1060
- created_orders = [] # noqa: F841
1061
- errors = []
1062
-
1063
- # We use a transaction to ensure either all orders are created or none if something goes wrong
1064
1083
  from django.db import transaction
1065
1084
 
1066
1085
  try:
1067
1086
  with transaction.atomic():
1087
+ # 1. Validate all items have prices and satisfy limits
1088
+ for item in cart_items:
1089
+ if not item.recipe.price:
1090
+ raise ValidationError(
1091
+ _(
1092
+ "Recipe '%(title)s' cannot be ordered because it has no price."
1093
+ )
1094
+ % {"title": item.recipe.title}
1095
+ )
1096
+
1097
+ if (
1098
+ item.recipe.max_daily_orders is not None
1099
+ and item.recipe.daily_orders_count + item.quantity
1100
+ > item.recipe.max_daily_orders
1101
+ ):
1102
+ raise ValidationError(
1103
+ _("Daily order limit reached for %(title)s.")
1104
+ % {"title": item.recipe.title}
1105
+ )
1106
+
1107
+ # 2. Create the order
1108
+ order = Order.objects.create(user=request.user) # ty:ignore[unresolved-attribute]
1109
+ total_price = 0
1110
+
1111
+ # 3. Create order items and update recipe counts
1068
1112
  for item in cart_items:
1069
- # Create Order for each recipe in cart (quantity times?)
1070
- # Current Order model doesn't have quantity, so we create multiple orders or update Order model.
1071
- # For now, let's create 'quantity' number of orders as per current schema
1072
- # OR we could update Order model to support quantity.
1073
- # Let's see if Order has quantity. (Checked: it does not).
1074
- for i in range(item.quantity):
1075
- try:
1076
- Order.objects.create(user=request.user, recipe=item.recipe) # ty:ignore[unresolved-attribute]
1077
- except (ValidationError, ValueError) as e:
1078
- errors.append(f"{item.recipe.title}: {str(e)}")
1079
- raise e # Trigger rollback
1113
+ order_item = OrderItem.objects.create( # ty:ignore[unresolved-attribute]
1114
+ order=order,
1115
+ recipe=item.recipe,
1116
+ quantity=item.quantity,
1117
+ price=item.recipe.price,
1118
+ )
1119
+ total_price += order_item.price * order_item.quantity
1080
1120
 
1121
+ # Note: OrderItem.save already updates daily_orders_count
1122
+ # But since we already checked it, it should be fine.
1123
+
1124
+ # 4. Finalize order
1125
+ order.total_price = total_price
1126
+ order.save()
1127
+
1128
+ # 5. Clear cart
1081
1129
  cart_items.delete()
1082
- messages.success(request, _("Orders submitted successfully!"))
1083
- return redirect("user_profile")
1084
- except Exception:
1085
- if errors:
1086
- for error in errors:
1087
- messages.error(request, error)
1088
- else:
1089
- messages.error(request, _("An error occurred during checkout."))
1130
+
1131
+ # 6. Notifications (outside atomic block for better reliability if using DB-backed tasks)
1132
+ notify_order_submitted.enqueue(order_id=order.pk)
1133
+ send_gotify_notification.enqueue(
1134
+ title="New Order Received",
1135
+ message=f"Order #{order.pk} by {request.user.username}. Total: {order.total_price}€",
1136
+ priority=6,
1137
+ )
1138
+
1139
+ messages.success(request, _("Orders submitted successfully!"))
1140
+ return redirect("user_profile")
1141
+
1142
+ except ValidationError as e:
1143
+ # Specific validation error (e.g. price missing or limit reached)
1144
+ messages.error(request, str(e))
1145
+ return redirect("view_cart")
1146
+ except Exception as e:
1147
+ # General error
1148
+ logging.exception("Error during checkout")
1149
+ messages.error(
1150
+ request,
1151
+ _("An error occurred during checkout: %(error)s") % {"error": str(e)},
1152
+ )
1090
1153
  return redirect("view_cart")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sandwitches
3
- Version: 2.5.4
3
+ Version: 2.5.6
4
4
  Summary: Add your description here
5
5
  Author: Martyn van Dijke
6
6
  Author-email: Martyn van Dijke <martijnvdijke600@gmail.com>
@@ -1,6 +1,6 @@
1
1
  sandwitches/__init__.py,sha256=YTDsQDSdJmxV2Z0dTbBqZhuJRuXcNLSKL0SX73Lu2u8,195
2
- sandwitches/admin.py,sha256=-4QA5InEvLHyb6VFAGKapWbJO9mXdiV4GeQcGsM4xlI,2510
3
- sandwitches/api.py,sha256=ruD5QeOPY-l9PvkJQiaOYoI0sRARDpqpFrFDgBxo9cQ,6389
2
+ sandwitches/admin.py,sha256=5RnqrPGdI8441ukP8AjhaXfv2pfTN2IYKI1dr7UBSv4,2601
3
+ sandwitches/api.py,sha256=6p-VaoGxt5LRFwrgZMI987XMqFDF8smZ82hUi_5o-FY,7040
4
4
  sandwitches/asgi.py,sha256=cygnXdXSSVspM7ZXuj47Ef6oz7HSTw4D7BPzgE2PU5w,399
5
5
  sandwitches/feeds.py,sha256=iz1d11dV0utA0ZNsB7VIAp0h8Zr5mFNSKJWHbw_j6YM,683
6
6
  sandwitches/forms.py,sha256=yTiPRw5tknik8D5_8p5FM8cxuTtU5P18v3-tsgHi5kM,9637
@@ -26,17 +26,18 @@ sandwitches/migrations/0014_ensure_groups_exist.py,sha256=5FSA742bEQtwHZl5CWZQYI
26
26
  sandwitches/migrations/0015_order_completed_alter_order_status_and_more.py,sha256=PTXQZUE8RqTAK8l0vkZhiGKv2T0PDiWEue7f6qz3AQ0,1670
27
27
  sandwitches/migrations/0016_user_theme.py,sha256=2tJnT6Bjd6fuwLAr2J47IXTfmmde1BpPGM_1HB37sSg,537
28
28
  sandwitches/migrations/0017_setting_gotify_token_setting_gotify_url.py,sha256=ABebUfj92uZcRg9XnAz-W_am74l9qjrg_vwDKaMjjBc,839
29
+ sandwitches/migrations/0018_remove_order_recipe_alter_order_total_price_and_more.py,sha256=me3UD5r7tI0OrKEmXx-671EF_YYL1np5yt3I7IFM_NI,1862
29
30
  sandwitches/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- sandwitches/models.py,sha256=L8qMFoJ7WpIito2nTgsAB8s-UPUQMwDux63aKbP71aE,12468
31
+ sandwitches/models.py,sha256=XnO2NZ2wjwcd1RUzNVUVxhWBKkbNASoWXRPB1StOZ9o,12308
31
32
  sandwitches/settings.py,sha256=5_eQAJCAV093hnhr3XOxHekT4IF-PEJcRiTecq71_SQ,5841
32
33
  sandwitches/storage.py,sha256=ibBG6tVtArqzgEKsRimZPwsqW7i9j4WiPLLHrOJchow,3578
33
- sandwitches/tasks.py,sha256=cGPgEqxGOCwliIq0oxNZBGjnCtvrTdJN-XyIp1ciJ3k,6865
34
+ sandwitches/tasks.py,sha256=eIcLaOBti1g4rFIaQTNF3tLyP-oGt69yNkfAtc-08PU,7455
34
35
  sandwitches/templates/admin/admin_base.html,sha256=a3eA0zTEDFhnX0ZFfMlPanmUImMTHmrqOO_2co2MUdE,4528
35
36
  sandwitches/templates/admin/confirm_delete.html,sha256=HfsZI_gV8JQTKz215TYgPWBrgrFhGv1UB3N-0Hln-14,804
36
37
  sandwitches/templates/admin/dashboard.html,sha256=ExLPzEmg7h0SvCuXr0uG1INDPLyirIBfD3Yvw4A1zjQ,5355
37
- sandwitches/templates/admin/order_list.html,sha256=eHFUn2speXaaj5_SFUG0Z0HfWVUR9-VCDRBeb8ufFb0,819
38
+ sandwitches/templates/admin/order_list.html,sha256=YPVilffd3wShmAd5T5Jem2wlBTfZyv5TDwyZdGJ_In4,818
38
39
  sandwitches/templates/admin/partials/dashboard_charts.html,sha256=NYrt-LDZO4__2KDWhAYL5K_f-2Zgj0iiuaZQiRZlBWg,3639
39
- sandwitches/templates/admin/partials/order_rows.html,sha256=4ogAFno4rfQJTyT4QQpKIaGqtI8akIo37xyZPsR7iCc,1638
40
+ sandwitches/templates/admin/partials/order_rows.html,sha256=a23_EdSGX_seFbPop9umE_ijjgAz71FhMxFQHMNU3Ig,1750
40
41
  sandwitches/templates/admin/rating_list.html,sha256=8CHAsBfKfs4izhb-IyOiDjJXqAZxFcStoRSGh4pRlgM,1365
41
42
  sandwitches/templates/admin/recipe_approval_list.html,sha256=M6GFYI45lAkLkvqP44cu5tDYVOeeVNklEphof1euesM,2281
42
43
  sandwitches/templates/admin/recipe_form.html,sha256=wVKKBFl3vN11aknnmv2Hxkj66zZk9iZ0x_iS1j_X_Ro,12884
@@ -69,19 +70,19 @@ sandwitches/templates/detail.html,sha256=_0Cc7Tba8GCQMJ5UO_ltf_gkvsDeEM5EtRRa3yQ
69
70
  sandwitches/templates/favorites.html,sha256=0cPpW07N6Isrb8XpvA5Eh97L2-12QFZ43EzeJvbOlXo,917
70
71
  sandwitches/templates/index.html,sha256=XYECUStc0zX4XG_OBLWM4FUdUkMML4ZKV9tB2Cu71Fg,2546
71
72
  sandwitches/templates/login.html,sha256=LiQskhkOkfx0EE4ssA1ToqQ3oEll08OPYLDIkLjHfU8,2177
72
- sandwitches/templates/order_detail.html,sha256=D6MjUVibQuED2VRNHSjKVnLHcLgFtLvcVmuwlzfoJzo,2498
73
+ sandwitches/templates/order_detail.html,sha256=OC6mEK8NzIg0OXkryXljDy141NSVwlb46i8YG-bZWlk,2712
73
74
  sandwitches/templates/partials/recipe_list.html,sha256=lED9_V4UKXqzsnbNSMrIem3iEXA4wiETtqzMlZPObxQ,4886
74
- sandwitches/templates/profile.html,sha256=TMh07Bxs32svn4aV01hjf22sJYB54qnPw6tJ3deXtbw,8617
75
+ sandwitches/templates/profile.html,sha256=yB4Ul3f2VUxi9NlGrnWe5pCm0hWmOFK4lRpiBqrzpYA,9143
75
76
  sandwitches/templates/settings.html,sha256=Q3dwXdwiNgPxevfArjiHhIu-wCsQaii2Uymf_CqbWWg,2121
76
77
  sandwitches/templates/setup.html,sha256=iNveFgePATsCSO4XMbGPa8TnWHyvj8S_5WwcW6i7pbo,4661
77
78
  sandwitches/templates/signup.html,sha256=wVRyj6Sy1z5TRvJDT9ufEOp8-tKbW44Fzer4SYq9AGw,5518
78
79
  sandwitches/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
80
  sandwitches/templatetags/custom_filters.py,sha256=0KDFlFz4b5LwlcURBAmzyYWKKea-LwydZytJGVkkuKA,243
80
81
  sandwitches/templatetags/markdown_extras.py,sha256=0ibmRzxE3r85x4k7kK71R-9UT0CgeegYF7MHzj3juTI,344
81
- sandwitches/urls.py,sha256=CPLC8WyC2H2KvJEq74J2w7PHK6l4nAySWlAmn_hnlQA,5229
82
+ sandwitches/urls.py,sha256=WWOkBAFG8sFzKyJ-1VeutpbNUJ-MGBKoK8M1gPMzPF8,5122
82
83
  sandwitches/utils.py,sha256=Vxi89fIHge897P_zDDcE5aXHviYLgePXkinj2_lCCPM,7308
83
- sandwitches/views.py,sha256=xxCYx5FM8oxk5GGstUQ2fI5rk6j-4xZ51BmilKzUvVo,34804
84
+ sandwitches/views.py,sha256=rDLr3FM7KHYD9MJPwneVITTRh1WOwav5cOhYtOoLycs,36898
84
85
  sandwitches/wsgi.py,sha256=Eyncpnahq_4s3Lr9ruB-R3Lu9j9zBXqgPbUj7qhIbwU,399
85
- sandwitches-2.5.4.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
86
- sandwitches-2.5.4.dist-info/METADATA,sha256=HxVAdH0pmrkLh7HJ9dSOkeZrWvYG678VVVQSYXPTrJQ,3143
87
- sandwitches-2.5.4.dist-info/RECORD,,
86
+ sandwitches-2.5.6.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
87
+ sandwitches-2.5.6.dist-info/METADATA,sha256=g2HEjfBTIG4HssaFVf-aXsaSVoT728p5ur9jhHt0Ygo,3143
88
+ sandwitches-2.5.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.28
2
+ Generator: uv 0.9.29
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any