Add ticket-to-cart integration
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m6s

Tickets now appear as cart line items, are included in checkout totals,
confirmed on payment, and displayed on the checkout return page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-19 21:32:42 +00:00
parent b3efed2f60
commit cd41b6c8ef
11 changed files with 170 additions and 32 deletions

12
app.py
View File

@@ -21,10 +21,13 @@ from bp.cart.services import (
total, total,
get_calendar_cart_entries, get_calendar_cart_entries,
calendar_total, calendar_total,
get_ticket_cart_entries,
ticket_total,
) )
from bp.cart.services.page_cart import ( from bp.cart.services.page_cart import (
get_cart_for_page, get_cart_for_page,
get_calendar_entries_for_page, get_calendar_entries_for_page,
get_tickets_for_page,
) )
@@ -53,27 +56,32 @@ async def cart_context() -> dict:
# Cart app owns cart data — use g.cart from _load_cart # Cart app owns cart data — use g.cart from _load_cart
all_cart = getattr(g, "cart", None) or [] all_cart = getattr(g, "cart", None) or []
all_cal = await get_calendar_cart_entries(g.s) all_cal = await get_calendar_cart_entries(g.s)
all_tickets = await get_ticket_cart_entries(g.s)
# Global counts for cart-mini (always global) # Global counts for cart-mini (always global)
cart_qty = sum(ci.quantity for ci in all_cart) if all_cart else 0 cart_qty = sum(ci.quantity for ci in all_cart) if all_cart else 0
ctx["cart_count"] = cart_qty + len(all_cal) ctx["cart_count"] = cart_qty + len(all_cal) + len(all_tickets)
ctx["cart_total"] = (total(all_cart) or 0) + (calendar_total(all_cal) or 0) ctx["cart_total"] = (total(all_cart) or 0) + (calendar_total(all_cal) or 0) + (ticket_total(all_tickets) or 0)
# Page-scoped data when viewing a page cart # Page-scoped data when viewing a page cart
page_post = getattr(g, "page_post", None) page_post = getattr(g, "page_post", None)
if page_post: if page_post:
page_cart = await get_cart_for_page(g.s, page_post.id) page_cart = await get_cart_for_page(g.s, page_post.id)
page_cal = await get_calendar_entries_for_page(g.s, page_post.id) page_cal = await get_calendar_entries_for_page(g.s, page_post.id)
page_tickets = await get_tickets_for_page(g.s, page_post.id)
ctx["cart"] = page_cart ctx["cart"] = page_cart
ctx["calendar_cart_entries"] = page_cal ctx["calendar_cart_entries"] = page_cal
ctx["ticket_cart_entries"] = page_tickets
ctx["page_post"] = page_post ctx["page_post"] = page_post
ctx["page_config"] = getattr(g, "page_config", None) ctx["page_config"] = getattr(g, "page_config", None)
else: else:
ctx["cart"] = all_cart ctx["cart"] = all_cart
ctx["calendar_cart_entries"] = all_cal ctx["calendar_cart_entries"] = all_cal
ctx["ticket_cart_entries"] = all_tickets
ctx["total"] = total ctx["total"] = total
ctx["calendar_total"] = calendar_total ctx["calendar_total"] = calendar_total
ctx["ticket_total"] = ticket_total
ctx["menu_items"] = await get_navigation_tree(g.s) ctx["menu_items"] = await get_navigation_tree(g.s)

View File

@@ -16,6 +16,8 @@ from .services import (
clear_cart_for_order, clear_cart_for_order,
get_calendar_cart_entries, get_calendar_cart_entries,
calendar_total, calendar_total,
get_ticket_cart_entries,
ticket_total,
check_sumup_status, check_sumup_status,
) )
from .services.checkout import ( from .services.checkout import (
@@ -107,19 +109,21 @@ def register(url_prefix: str) -> Blueprint:
"""Legacy global checkout (for orphan items without page scope).""" """Legacy global checkout (for orphan items without page scope)."""
cart = await get_cart(g.s) cart = await get_cart(g.s)
calendar_entries = await get_calendar_cart_entries(g.s) calendar_entries = await get_calendar_cart_entries(g.s)
tickets = await get_ticket_cart_entries(g.s)
if not cart and not calendar_entries: if not cart and not calendar_entries and not tickets:
return redirect(url_for("cart_overview.overview")) return redirect(url_for("cart_overview.overview"))
product_total = total(cart) or 0 product_total = total(cart) or 0
calendar_amount = calendar_total(calendar_entries) or 0 calendar_amount = calendar_total(calendar_entries) or 0
cart_total = product_total + calendar_amount ticket_amount = ticket_total(tickets) or 0
cart_total = product_total + calendar_amount + ticket_amount
if cart_total <= 0: if cart_total <= 0:
return redirect(url_for("cart_overview.overview")) return redirect(url_for("cart_overview.overview"))
try: try:
page_config = await resolve_page_config(g.s, cart, calendar_entries) page_config = await resolve_page_config(g.s, cart, calendar_entries, tickets)
except ValueError as e: except ValueError as e:
html = await render_template( html = await render_template(
"_types/cart/checkout_error.html", "_types/cart/checkout_error.html",
@@ -137,6 +141,7 @@ def register(url_prefix: str) -> Blueprint:
ident.get("session_id"), ident.get("session_id"),
product_total, product_total,
calendar_amount, calendar_amount,
ticket_total=ticket_amount,
) )
if page_config: if page_config:
@@ -144,7 +149,7 @@ def register(url_prefix: str) -> Blueprint:
redirect_url = url_for("cart_global.checkout_return", order_id=order.id, _external=True) redirect_url = url_for("cart_global.checkout_return", order_id=order.id, _external=True)
order.sumup_reference = build_sumup_reference(order.id, page_config=page_config) order.sumup_reference = build_sumup_reference(order.id, page_config=page_config)
description = build_sumup_description(cart, order.id) description = build_sumup_description(cart, order.id, ticket_count=len(tickets))
webhook_base_url = url_for("cart_global.checkout_webhook", order_id=order.id, _external=True) webhook_base_url = url_for("cart_global.checkout_webhook", order_id=order.id, _external=True)
webhook_url = build_webhook_url(webhook_base_url) webhook_url = build_webhook_url(webhook_base_url)
@@ -253,6 +258,7 @@ def register(url_prefix: str) -> Blueprint:
status = (order.status or "pending").lower() status = (order.status or "pending").lower()
calendar_entries = await services.calendar.get_entries_for_order(g.s, order.id) calendar_entries = await services.calendar.get_entries_for_order(g.s, order.id)
order_tickets = await services.calendar.get_tickets_for_order(g.s, order.id)
await g.s.flush() await g.s.flush()
html = await render_template( html = await render_template(
@@ -260,6 +266,7 @@ def register(url_prefix: str) -> Blueprint:
order=order, order=order,
status=status, status=status,
calendar_entries=calendar_entries, calendar_entries=calendar_entries,
order_tickets=order_tickets,
) )
return await make_response(html) return await make_response(html)

View File

@@ -11,8 +11,9 @@ from .services import (
total, total,
clear_cart_for_order, clear_cart_for_order,
calendar_total, calendar_total,
ticket_total,
) )
from .services.page_cart import get_cart_for_page, get_calendar_entries_for_page from .services.page_cart import get_cart_for_page, get_calendar_entries_for_page, get_tickets_for_page
from .services.checkout import ( from .services.checkout import (
create_order_from_cart, create_order_from_cart,
build_sumup_description, build_sumup_description,
@@ -30,14 +31,17 @@ def register(url_prefix: str) -> Blueprint:
post = g.page_post post = g.page_post
cart = await get_cart_for_page(g.s, post.id) cart = await get_cart_for_page(g.s, post.id)
cal_entries = await get_calendar_entries_for_page(g.s, post.id) cal_entries = await get_calendar_entries_for_page(g.s, post.id)
page_tickets = await get_tickets_for_page(g.s, post.id)
tpl_ctx = dict( tpl_ctx = dict(
page_post=post, page_post=post,
page_config=getattr(g, "page_config", None), page_config=getattr(g, "page_config", None),
cart=cart, cart=cart,
calendar_cart_entries=cal_entries, calendar_cart_entries=cal_entries,
ticket_cart_entries=page_tickets,
total=total, total=total,
calendar_total=calendar_total, calendar_total=calendar_total,
ticket_total=ticket_total,
) )
if not is_htmx_request(): if not is_htmx_request():
@@ -53,13 +57,15 @@ def register(url_prefix: str) -> Blueprint:
cart = await get_cart_for_page(g.s, post.id) cart = await get_cart_for_page(g.s, post.id)
cal_entries = await get_calendar_entries_for_page(g.s, post.id) cal_entries = await get_calendar_entries_for_page(g.s, post.id)
page_tickets = await get_tickets_for_page(g.s, post.id)
if not cart and not cal_entries: if not cart and not cal_entries and not page_tickets:
return redirect(url_for("page_cart.page_view")) return redirect(url_for("page_cart.page_view"))
product_total = total(cart) or 0 product_total = total(cart) or 0
calendar_amount = calendar_total(cal_entries) or 0 calendar_amount = calendar_total(cal_entries) or 0
cart_total = product_total + calendar_amount ticket_amount = ticket_total(page_tickets) or 0
cart_total = product_total + calendar_amount + ticket_amount
if cart_total <= 0: if cart_total <= 0:
return redirect(url_for("page_cart.page_view")) return redirect(url_for("page_cart.page_view"))
@@ -74,6 +80,7 @@ def register(url_prefix: str) -> Blueprint:
ident.get("session_id"), ident.get("session_id"),
product_total, product_total,
calendar_amount, calendar_amount,
ticket_total=ticket_amount,
page_post_id=post.id, page_post_id=post.id,
) )
@@ -84,7 +91,7 @@ def register(url_prefix: str) -> Blueprint:
# Build SumUp checkout details — webhook/return use global routes # Build SumUp checkout details — webhook/return use global routes
redirect_url = url_for("cart_global.checkout_return", order_id=order.id, _external=True) redirect_url = url_for("cart_global.checkout_return", order_id=order.id, _external=True)
order.sumup_reference = build_sumup_reference(order.id, page_config=page_config) order.sumup_reference = build_sumup_reference(order.id, page_config=page_config)
description = build_sumup_description(cart, order.id) description = build_sumup_description(cart, order.id, ticket_count=len(page_tickets))
webhook_base_url = url_for("cart_global.checkout_webhook", order_id=order.id, _external=True) webhook_base_url = url_for("cart_global.checkout_webhook", order_id=order.id, _external=True)
webhook_url = build_webhook_url(webhook_base_url) webhook_url = build_webhook_url(webhook_base_url)

View File

@@ -2,11 +2,12 @@ from .get_cart import get_cart
from .identity import current_cart_identity from .identity import current_cart_identity
from .total import total from .total import total
from .clear_cart_for_order import clear_cart_for_order from .clear_cart_for_order import clear_cart_for_order
from .calendar_cart import get_calendar_cart_entries, calendar_total from .calendar_cart import get_calendar_cart_entries, calendar_total, get_ticket_cart_entries, ticket_total
from .check_sumup_status import check_sumup_status from .check_sumup_status import check_sumup_status
from .page_cart import ( from .page_cart import (
get_cart_for_page, get_cart_for_page,
get_calendar_entries_for_page, get_calendar_entries_for_page,
get_tickets_for_page,
get_cart_grouped_by_page, get_cart_grouped_by_page,
) )

View File

@@ -26,3 +26,18 @@ def calendar_total(entries) -> float:
for e in entries for e in entries
if e.cost is not None if e.cost is not None
) )
async def get_ticket_cart_entries(session):
"""Return all reserved tickets (as TicketDTOs) for the current identity."""
ident = current_cart_identity()
return await services.calendar.pending_tickets(
session,
user_id=ident["user_id"],
session_id=ident["session_id"],
)
def ticket_total(tickets) -> float:
"""Total cost of reserved tickets."""
return sum(float(t.price or 0) for t in tickets)

View File

@@ -16,6 +16,7 @@ async def check_sumup_status(session, order):
await services.calendar.confirm_entries_for_order( await services.calendar.confirm_entries_for_order(
session, order.id, order.user_id, order.session_id session, order.id, order.user_id, order.session_id
) )
await services.calendar.confirm_tickets_for_order(session, order.id)
await emit_event(session, "order.paid", "order", order.id, { await emit_event(session, "order.paid", "order", order.id, {
"order_id": order.id, "order_id": order.id,
"user_id": order.user_id, "user_id": order.user_id,

View File

@@ -65,6 +65,7 @@ async def resolve_page_config(
session: AsyncSession, session: AsyncSession,
cart: list[CartItem], cart: list[CartItem],
calendar_entries: list[CalendarEntryDTO], calendar_entries: list[CalendarEntryDTO],
tickets=None,
) -> Optional["PageConfig"]: ) -> Optional["PageConfig"]:
"""Determine the PageConfig for this order. """Determine the PageConfig for this order.
@@ -85,6 +86,11 @@ async def resolve_page_config(
if entry.calendar_container_id: if entry.calendar_container_id:
post_ids.add(entry.calendar_container_id) post_ids.add(entry.calendar_container_id)
# From tickets via calendar_container_id
for tk in (tickets or []):
if tk.calendar_container_id:
post_ids.add(tk.calendar_container_id)
if len(post_ids) > 1: if len(post_ids) > 1:
raise ValueError("Cannot checkout items from multiple pages") raise ValueError("Cannot checkout items from multiple pages")
@@ -110,16 +116,17 @@ async def create_order_from_cart(
product_total: float, product_total: float,
calendar_total: float, calendar_total: float,
*, *,
ticket_total: float = 0,
page_post_id: int | None = None, page_post_id: int | None = None,
) -> Order: ) -> Order:
""" """
Create an Order and OrderItems from the current cart + calendar entries. Create an Order and OrderItems from the current cart + calendar entries + tickets.
When *page_post_id* is given, only calendar entries whose calendar When *page_post_id* is given, only calendar entries/tickets whose calendar
belongs to that page are marked as "ordered". Otherwise all pending belongs to that page are marked as "ordered". Otherwise all pending
entries are updated (legacy behaviour). entries are updated (legacy behaviour).
""" """
cart_total = product_total + calendar_total cart_total = product_total + calendar_total + ticket_total
# Determine currency from first product # Determine currency from first product
first_product = cart[0].product if cart else None first_product = cart[0].product if cart else None
@@ -154,6 +161,11 @@ async def create_order_from_cart(
session, order.id, user_id, session_id, page_post_id session, order.id, user_id, session_id, page_post_id
) )
# Claim reserved tickets for this order
await services.calendar.claim_tickets_for_order(
session, order.id, user_id, session_id, page_post_id
)
await emit_event(session, "order.created", "order", order.id, { await emit_event(session, "order.created", "order", order.id, {
"order_id": order.id, "order_id": order.id,
"user_id": user_id, "user_id": user_id,
@@ -163,20 +175,24 @@ async def create_order_from_cart(
return order return order
def build_sumup_description(cart: list[CartItem], order_id: int) -> str: def build_sumup_description(cart: list[CartItem], order_id: int, *, ticket_count: int = 0) -> str:
"""Build a human-readable description for SumUp checkout.""" """Build a human-readable description for SumUp checkout."""
titles = [ci.product.title for ci in cart if ci.product and ci.product.title] titles = [ci.product.title for ci in cart if ci.product and ci.product.title]
item_count = sum(ci.quantity for ci in cart) item_count = sum(ci.quantity for ci in cart)
parts = []
if titles: if titles:
if len(titles) <= 3: if len(titles) <= 3:
summary = ", ".join(titles) parts.append(", ".join(titles))
else: else:
summary = ", ".join(titles[:3]) + f" + {len(titles) - 3} more" parts.append(", ".join(titles[:3]) + f" + {len(titles) - 3} more")
else: if ticket_count:
summary = "order items" parts.append(f"{ticket_count} ticket{'s' if ticket_count != 1 else ''}")
return f"Order {order_id} ({item_count} item{'s' if item_count != 1 else ''}): {summary}" summary = ", ".join(parts) if parts else "order items"
total_count = item_count + ticket_count
return f"Order {order_id} ({total_count} item{'s' if total_count != 1 else ''}): {summary}"
def build_sumup_reference(order_id: int, page_config=None) -> str: def build_sumup_reference(order_id: int, page_config=None) -> str:

View File

@@ -57,6 +57,16 @@ async def get_calendar_entries_for_page(session, post_id: int):
) )
async def get_tickets_for_page(session, post_id: int):
"""Return reserved tickets (DTOs) scoped to a specific page."""
ident = current_cart_identity()
return await services.calendar.tickets_for_page(
session, post_id,
user_id=ident["user_id"],
session_id=ident["session_id"],
)
async def get_cart_grouped_by_page(session) -> list[dict]: async def get_cart_grouped_by_page(session) -> list[dict]:
""" """
Load all cart items + calendar entries for the current identity, Load all cart items + calendar entries for the current identity,
@@ -80,12 +90,13 @@ async def get_cart_grouped_by_page(session) -> list[dict]:
Items without a market_place go in an orphan bucket (post=None). Items without a market_place go in an orphan bucket (post=None).
""" """
from .get_cart import get_cart from .get_cart import get_cart
from .calendar_cart import get_calendar_cart_entries from .calendar_cart import get_calendar_cart_entries, get_ticket_cart_entries
from .total import total as calc_product_total from .total import total as calc_product_total
from .calendar_cart import calendar_total as calc_calendar_total from .calendar_cart import calendar_total as calc_calendar_total, ticket_total as calc_ticket_total
cart_items = await get_cart(session) cart_items = await get_cart(session)
cal_entries = await get_calendar_cart_entries(session) cal_entries = await get_calendar_cart_entries(session)
all_tickets = await get_ticket_cart_entries(session)
# Group cart items by market_place_id # Group cart items by market_place_id
market_groups: dict[int | None, dict] = {} market_groups: dict[int | None, dict] = {}
@@ -97,6 +108,7 @@ async def get_cart_grouped_by_page(session) -> list[dict]:
"post_id": ci.market_place.container_id if ci.market_place else None, "post_id": ci.market_place.container_id if ci.market_place else None,
"cart_items": [], "cart_items": [],
"calendar_entries": [], "calendar_entries": [],
"tickets": [],
} }
market_groups[mp_id]["cart_items"].append(ci) market_groups[mp_id]["cart_items"].append(ci)
@@ -121,11 +133,31 @@ async def get_cart_grouped_by_page(session) -> list[dict]:
"post_id": pid, "post_id": pid,
"cart_items": [], "cart_items": [],
"calendar_entries": [], "calendar_entries": [],
"tickets": [],
} }
if pid is not None: if pid is not None:
page_to_market[pid] = key page_to_market[pid] = key
market_groups[key]["calendar_entries"].append(ce) market_groups[key]["calendar_entries"].append(ce)
# Attach tickets to page groups (via calendar_container_id)
for tk in all_tickets:
pid = tk.calendar_container_id or None
if pid in page_to_market:
market_groups[page_to_market[pid]]["tickets"].append(tk)
else:
key = ("tk", pid)
if key not in market_groups:
market_groups[key] = {
"market_place": None,
"post_id": pid,
"cart_items": [],
"calendar_entries": [],
"tickets": [],
}
if pid is not None:
page_to_market[pid] = key
market_groups[key]["tickets"].append(tk)
# Batch-load Post DTOs and PageConfig objects # Batch-load Post DTOs and PageConfig objects
post_ids = list({ post_ids = list({
grp["post_id"] for grp in market_groups.values() grp["post_id"] for grp in market_groups.values()
@@ -155,8 +187,10 @@ async def get_cart_grouped_by_page(session) -> list[dict]:
): ):
items = grp["cart_items"] items = grp["cart_items"]
entries = grp["calendar_entries"] entries = grp["calendar_entries"]
tks = grp["tickets"]
prod_total = calc_product_total(items) or 0 prod_total = calc_product_total(items) or 0
cal_total = calc_calendar_total(entries) or 0 cal_total = calc_calendar_total(entries) or 0
tk_total = calc_ticket_total(tks) or 0
pid = grp["post_id"] pid = grp["post_id"]
result.append({ result.append({
@@ -165,11 +199,14 @@ async def get_cart_grouped_by_page(session) -> list[dict]:
"market_place": grp["market_place"], "market_place": grp["market_place"],
"cart_items": items, "cart_items": items,
"calendar_entries": entries, "calendar_entries": entries,
"tickets": tks,
"product_count": sum(ci.quantity for ci in items), "product_count": sum(ci.quantity for ci in items),
"product_total": prod_total, "product_total": prod_total,
"calendar_count": len(entries), "calendar_count": len(entries),
"calendar_total": cal_total, "calendar_total": cal_total,
"total": prod_total + cal_total, "ticket_count": len(tks),
"ticket_total": tk_total,
"total": prod_total + cal_total + tk_total,
}) })
return result return result

2
shared

Submodule shared updated: 71729ffb28...7ee8638d6e

View File

@@ -1,7 +1,7 @@
{% macro show_cart(oob=False) %} {% macro show_cart(oob=False) %}
<div id="cart" {% if oob %} hx-swap-oob="{{oob}}" {% endif%}> <div id="cart" {% if oob %} hx-swap-oob="{{oob}}" {% endif%}>
{# Empty cart #} {# Empty cart #}
{% if not cart and not calendar_cart_entries %} {% if not cart and not calendar_cart_entries and not ticket_cart_entries %}
<div class="rounded-2xl border border-dashed border-stone-300 bg-white/80 p-6 sm:p-8 text-center"> <div class="rounded-2xl border border-dashed border-stone-300 bg-white/80 p-6 sm:p-8 text-center">
<div class="inline-flex h-10 w-10 sm:h-12 sm:w-12 items-center justify-center rounded-full bg-stone-100 mb-3"> <div class="inline-flex h-10 w-10 sm:h-12 sm:w-12 items-center justify-center rounded-full bg-stone-100 mb-3">
<i class="fa fa-shopping-cart text-stone-500 text-sm sm:text-base" aria-hidden="true"></i> <i class="fa fa-shopping-cart text-stone-500 text-sm sm:text-base" aria-hidden="true"></i>
@@ -60,8 +60,41 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
{% if ticket_cart_entries %}
<div class="mt-6 border-t border-stone-200 pt-4">
<h2 class="text-base font-semibold mb-2">
Event tickets
</h2>
<ul class="space-y-2">
{% for tk in ticket_cart_entries %}
<li class="flex items-start justify-between text-sm">
<div>
<div class="font-medium">
{{ tk.entry_name }}
</div>
{% if tk.ticket_type_name %}
<div class="text-xs text-stone-500">
{{ tk.ticket_type_name }}
</div>
{% endif %}
<div class="text-xs text-stone-500">
{{ tk.entry_start_at.strftime('%-d %b %Y, %H:%M') }}
{% if tk.entry_end_at %}
{{ tk.entry_end_at.strftime('%-d %b %Y, %H:%M') }}
{% endif %}
</div>
</div>
<div class="ml-4 font-medium">
£{{ "%.2f"|format(tk.price or 0) }}
</div>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</section> </section>
{{summary(cart, total, calendar_total, calendar_cart_entries,)}} {{summary(cart, total, calendar_total, calendar_cart_entries, ticket_total, ticket_cart_entries)}}
</div> </div>
@@ -70,7 +103,7 @@
{% endmacro %} {% endmacro %}
{% macro summary(cart, total, calendar_total, calendar_cart_entries, oob=False) %} {% macro summary(cart, total, calendar_total, calendar_cart_entries, ticket_total, ticket_cart_entries, oob=False) %}
<aside id="cart-summary" class="lg:pl-2" {% if oob %} hx-swap-oob="{{oob}}" {% endif %}> <aside id="cart-summary" class="lg:pl-2" {% if oob %} hx-swap-oob="{{oob}}" {% endif %}>
<div class="rounded-2xl bg-white shadow-sm border border-stone-200 p-4 sm:p-5"> <div class="rounded-2xl bg-white shadow-sm border border-stone-200 p-4 sm:p-5">
<h2 class="text-sm sm:text-base font-semibold text-stone-900 mb-3 sm:mb-4"> <h2 class="text-sm sm:text-base font-semibold text-stone-900 mb-3 sm:mb-4">
@@ -87,7 +120,7 @@
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<dt class="text-stone-600">Subtotal</dt> <dt class="text-stone-600">Subtotal</dt>
<dd class="text-stone-900"> <dd class="text-stone-900">
{{ cart_grand_total(cart, total, calendar_total, calendar_cart_entries ) }} {{ cart_grand_total(cart, total, calendar_total, calendar_cart_entries, ticket_total, ticket_cart_entries) }}
</dd> </dd>
</div> </div>
</dl> </dl>
@@ -154,10 +187,11 @@
{% endmacro %} {% endmacro %}
{% macro cart_grand_total(cart, total, calendar_total, calendar_cart_entries) %} {% macro cart_grand_total(cart, total, calendar_total, calendar_cart_entries, ticket_total, ticket_cart_entries) %}
{% set product_total = total(cart) or 0 %} {% set product_total = total(cart) or 0 %}
{% set cal_total = calendar_total(calendar_cart_entries) or 0 %} {% set cal_total = calendar_total(calendar_cart_entries) or 0 %}
{% set grand = product_total + cal_total %} {% set tk_total = ticket_total(ticket_cart_entries) or 0 %}
{% set grand = product_total + cal_total + tk_total %}
{% if cart and cart[0].product.regular_price_currency %} {% if cart and cart[0].product.regular_price_currency %}
{% set symbol = "£" if cart[0].product.regular_price_currency == "GBP" else cart[0].product.regular_price_currency %} {% set symbol = "£" if cart[0].product.regular_price_currency == "GBP" else cart[0].product.regular_price_currency %}

View File

@@ -13,7 +13,7 @@
{# Check if there are any items at all across all groups #} {# Check if there are any items at all across all groups #}
{% set ns = namespace(has_items=false) %} {% set ns = namespace(has_items=false) %}
{% for grp in page_groups %} {% for grp in page_groups %}
{% if grp.cart_items or grp.calendar_entries %} {% if grp.cart_items or grp.calendar_entries or grp.get('tickets') %}
{% set ns.has_items = true %} {% set ns.has_items = true %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
@@ -30,7 +30,7 @@
{% else %} {% else %}
<div class="space-y-4"> <div class="space-y-4">
{% for grp in page_groups %} {% for grp in page_groups %}
{% if grp.cart_items or grp.calendar_entries %} {% if grp.cart_items or grp.calendar_entries or grp.get('tickets') %}
{% if grp.post %} {% if grp.post %}
{# Market / page cart card #} {# Market / page cart card #}
@@ -76,6 +76,12 @@
{{ grp.calendar_count }} booking{{ 's' if grp.calendar_count != 1 }} {{ grp.calendar_count }} booking{{ 's' if grp.calendar_count != 1 }}
</span> </span>
{% endif %} {% endif %}
{% if grp.ticket_count is defined and grp.ticket_count > 0 %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-stone-100">
<i class="fa fa-ticket" aria-hidden="true"></i>
{{ grp.ticket_count }} ticket{{ 's' if grp.ticket_count != 1 }}
</span>
{% endif %}
</div> </div>
</div> </div>
@@ -115,6 +121,12 @@
{{ grp.calendar_count }} booking{{ 's' if grp.calendar_count != 1 }} {{ grp.calendar_count }} booking{{ 's' if grp.calendar_count != 1 }}
</span> </span>
{% endif %} {% endif %}
{% if grp.ticket_count is defined and grp.ticket_count > 0 %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-amber-100">
<i class="fa fa-ticket" aria-hidden="true"></i>
{{ grp.ticket_count }} ticket{{ 's' if grp.ticket_count != 1 }}
</span>
{% endif %}
</div> </div>
</div> </div>