Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 21s
Splits the monolithic cart blueprint into three: cart_overview (GET /), page_cart (/<page_slug>/), and cart_global (webhook, return, add). - New page_cart.py service: get_cart_for_page(), get_calendar_entries_for_page(), get_cart_grouped_by_page() - clear_cart_for_order() and create_order_from_cart() accept page_post_id for scoping - Cart app hydrates page_slug via url_value_preprocessor/url_defaults/hydrate_page - Context processor provides page-scoped cart data when g.page_post exists - Internal API /internal/cart/summary accepts ?page_slug= for page-scoped counts - Overview template shows page cards with item counts and totals - Page cart template reuses show_cart() macro with page-specific header Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
4.1 KiB
Python
124 lines
4.1 KiB
Python
# bp/cart/page_routes.py — Per-page cart (view + checkout)
|
|
|
|
from __future__ import annotations
|
|
|
|
from quart import Blueprint, g, render_template, redirect, make_response, url_for
|
|
|
|
from suma_browser.app.utils.htmx import is_htmx_request
|
|
from suma_browser.app.payments.sumup import create_checkout as sumup_create_checkout
|
|
from config import config
|
|
from .services import (
|
|
total,
|
|
clear_cart_for_order,
|
|
calendar_total,
|
|
check_sumup_status,
|
|
)
|
|
from .services.page_cart import get_cart_for_page, get_calendar_entries_for_page
|
|
from .services.checkout import (
|
|
create_order_from_cart,
|
|
build_sumup_description,
|
|
build_sumup_reference,
|
|
build_webhook_url,
|
|
get_order_with_details,
|
|
)
|
|
from .services import current_cart_identity
|
|
|
|
|
|
def register(url_prefix: str) -> Blueprint:
|
|
bp = Blueprint("page_cart", __name__, url_prefix=url_prefix)
|
|
|
|
@bp.get("/")
|
|
async def page_view():
|
|
post = g.page_post
|
|
cart = await get_cart_for_page(g.s, post.id)
|
|
cal_entries = await get_calendar_entries_for_page(g.s, post.id)
|
|
|
|
tpl_ctx = dict(
|
|
page_post=post,
|
|
page_config=getattr(g, "page_config", None),
|
|
cart=cart,
|
|
calendar_cart_entries=cal_entries,
|
|
total=total,
|
|
calendar_total=calendar_total,
|
|
)
|
|
|
|
if not is_htmx_request():
|
|
html = await render_template("_types/cart/page/index.html", **tpl_ctx)
|
|
else:
|
|
html = await render_template("_types/cart/page/_oob_elements.html", **tpl_ctx)
|
|
return await make_response(html)
|
|
|
|
@bp.post("/checkout/")
|
|
async def page_checkout():
|
|
post = g.page_post
|
|
page_config = getattr(g, "page_config", None)
|
|
|
|
cart = await get_cart_for_page(g.s, post.id)
|
|
cal_entries = await get_calendar_entries_for_page(g.s, post.id)
|
|
|
|
if not cart and not cal_entries:
|
|
return redirect(url_for("page_cart.page_view"))
|
|
|
|
product_total = total(cart) or 0
|
|
calendar_amount = calendar_total(cal_entries) or 0
|
|
cart_total = product_total + calendar_amount
|
|
|
|
if cart_total <= 0:
|
|
return redirect(url_for("page_cart.page_view"))
|
|
|
|
# Create order scoped to this page
|
|
ident = current_cart_identity()
|
|
order = await create_order_from_cart(
|
|
g.s,
|
|
cart,
|
|
cal_entries,
|
|
ident.get("user_id"),
|
|
ident.get("session_id"),
|
|
product_total,
|
|
calendar_amount,
|
|
page_post_id=post.id,
|
|
)
|
|
|
|
# Set page_config on order
|
|
if page_config:
|
|
order.page_config_id = page_config.id
|
|
|
|
# Build SumUp checkout details — webhook/return use global routes
|
|
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)
|
|
description = build_sumup_description(cart, order.id)
|
|
|
|
webhook_base_url = url_for("cart_global.checkout_webhook", order_id=order.id, _external=True)
|
|
webhook_url = build_webhook_url(webhook_base_url)
|
|
|
|
checkout_data = await sumup_create_checkout(
|
|
order,
|
|
redirect_url=redirect_url,
|
|
webhook_url=webhook_url,
|
|
description=description,
|
|
page_config=page_config,
|
|
)
|
|
await clear_cart_for_order(g.s, order, page_post_id=post.id)
|
|
|
|
order.sumup_checkout_id = checkout_data.get("id")
|
|
order.sumup_status = checkout_data.get("status")
|
|
order.description = checkout_data.get("description")
|
|
|
|
hosted_cfg = checkout_data.get("hosted_checkout") or {}
|
|
hosted_url = hosted_cfg.get("hosted_checkout_url") or checkout_data.get("hosted_checkout_url")
|
|
order.sumup_hosted_url = hosted_url
|
|
|
|
await g.s.flush()
|
|
|
|
if not hosted_url:
|
|
html = await render_template(
|
|
"_types/cart/checkout_error.html",
|
|
order=order,
|
|
error="No hosted checkout URL returned from SumUp.",
|
|
)
|
|
return await make_response(html, 500)
|
|
|
|
return redirect(hosted_url)
|
|
|
|
return bp
|