All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 41s
- checkout.py: use claim_entries_for_order(), emit order.created - check_sumup_status.py: use confirm_entries_for_order(), emit order.paid - global_routes.py: use get_entries_for_order() instead of relationship - order.py: remove calendar_entries relationship - api.py: remove /adopt endpoint (replaced by event-driven adoption) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
4.4 KiB
Python
124 lines
4.4 KiB
Python
"""
|
|
Internal JSON API for the cart app.
|
|
|
|
These endpoints are called by other apps (coop, market) over HTTP.
|
|
They are CSRF-exempt because they are server-to-server calls.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from quart import Blueprint, g, request, jsonify
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from market.models.market import CartItem
|
|
from market.models.market_place import MarketPlace
|
|
from events.models.calendars import CalendarEntry, Calendar
|
|
from blog.models.ghost_content import Post
|
|
from shared.browser.app.csrf import csrf_exempt
|
|
from shared.infrastructure.cart_identity import current_cart_identity
|
|
|
|
|
|
def register() -> Blueprint:
|
|
bp = Blueprint("cart_api", __name__, url_prefix="/internal/cart")
|
|
|
|
@bp.get("/summary")
|
|
@csrf_exempt
|
|
async def summary():
|
|
"""
|
|
Return a lightweight cart summary (count + total) for the
|
|
current session/user. Called by coop and market apps to
|
|
populate the cart-mini widget without importing cart services.
|
|
|
|
Optional query param: ?page_slug=<slug>
|
|
When provided, returns only items scoped to that page.
|
|
"""
|
|
ident = current_cart_identity()
|
|
|
|
# Resolve optional page filter
|
|
page_slug = request.args.get("page_slug")
|
|
page_post_id = None
|
|
if page_slug:
|
|
post = (
|
|
await g.s.execute(
|
|
select(Post).where(Post.slug == page_slug, Post.is_page == True) # noqa: E712
|
|
)
|
|
).scalar_one_or_none()
|
|
if post:
|
|
page_post_id = post.id
|
|
|
|
# --- product cart ---
|
|
cart_q = select(CartItem).where(CartItem.deleted_at.is_(None))
|
|
if ident["user_id"] is not None:
|
|
cart_q = cart_q.where(CartItem.user_id == ident["user_id"])
|
|
else:
|
|
cart_q = cart_q.where(CartItem.session_id == ident["session_id"])
|
|
|
|
if page_post_id is not None:
|
|
mp_ids = select(MarketPlace.id).where(
|
|
MarketPlace.container_type == "page",
|
|
MarketPlace.container_id == page_post_id,
|
|
MarketPlace.deleted_at.is_(None),
|
|
).scalar_subquery()
|
|
cart_q = cart_q.where(CartItem.market_place_id.in_(mp_ids))
|
|
|
|
cart_q = cart_q.options(selectinload(CartItem.product)).order_by(CartItem.created_at.desc())
|
|
|
|
result = await g.s.execute(cart_q)
|
|
cart_items = result.scalars().all()
|
|
|
|
cart_count = sum(ci.quantity for ci in cart_items)
|
|
cart_total = sum(
|
|
(ci.product.special_price or ci.product.regular_price or 0) * ci.quantity
|
|
for ci in cart_items
|
|
if ci.product and (ci.product.special_price or ci.product.regular_price)
|
|
)
|
|
|
|
# --- calendar entries ---
|
|
cal_q = select(CalendarEntry).where(
|
|
CalendarEntry.deleted_at.is_(None),
|
|
CalendarEntry.state == "pending",
|
|
)
|
|
if ident["user_id"] is not None:
|
|
cal_q = cal_q.where(CalendarEntry.user_id == ident["user_id"])
|
|
else:
|
|
cal_q = cal_q.where(CalendarEntry.session_id == ident["session_id"])
|
|
|
|
if page_post_id is not None:
|
|
cal_ids = select(Calendar.id).where(
|
|
Calendar.container_type == "page",
|
|
Calendar.container_id == page_post_id,
|
|
Calendar.deleted_at.is_(None),
|
|
).scalar_subquery()
|
|
cal_q = cal_q.where(CalendarEntry.calendar_id.in_(cal_ids))
|
|
|
|
cal_result = await g.s.execute(cal_q)
|
|
cal_entries = cal_result.scalars().all()
|
|
|
|
calendar_count = len(cal_entries)
|
|
calendar_total = sum((e.cost or 0) for e in cal_entries if e.cost is not None)
|
|
|
|
items = [
|
|
{
|
|
"slug": ci.product.slug if ci.product else None,
|
|
"title": ci.product.title if ci.product else None,
|
|
"image": ci.product.image if ci.product else None,
|
|
"quantity": ci.quantity,
|
|
"price": float(ci.product.special_price or ci.product.regular_price or 0)
|
|
if ci.product
|
|
else 0,
|
|
}
|
|
for ci in cart_items
|
|
]
|
|
|
|
return jsonify(
|
|
{
|
|
"count": cart_count,
|
|
"total": float(cart_total),
|
|
"calendar_count": calendar_count,
|
|
"calendar_total": float(calendar_total),
|
|
"items": items,
|
|
}
|
|
)
|
|
|
|
return bp
|