From d32c1fd545c342d96169b4b8d24dcc57c1e64efb Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 24 May 2026 23:02:07 +0200 Subject: [PATCH] fix(loyalty): show category column on card-detail for all 3 personas The transaction-history table on the card-detail page rendered a Category column only on the store frontend. Merchant and admin saw five columns instead of six, even though the merchant report prompted the audit (rewardflow.lu/merchants/loyalty/cards/6 vs fashionhub.rewardflow.lu/store/.../cards/6). Root cause was two layers: - API: only store's GET /cards/{id}/transactions enriched tx.category_names from tx.category_ids; merchant's and admin's endpoints returned raw rows with category_names=null. - Template: the shared partial's show_category_column flag was set to true only on the store wrapper. Backfill the same `category_service.validate_category_for_store` lookup loop into merchant.py::get_card_transactions and admin.py::get_merchant_card_transactions, accepting Request to read request.state.language for localised category names. Add `{% set show_category_column = true %}` to the merchant and admin card-detail wrappers. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/modules/loyalty/routes/api/admin.py | 27 +++++++++++++++---- app/modules/loyalty/routes/api/merchant.py | 27 +++++++++++++++---- .../loyalty/admin/merchant-card-detail.html | 1 + .../loyalty/merchant/card-detail.html | 1 + 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/modules/loyalty/routes/api/admin.py b/app/modules/loyalty/routes/api/admin.py index c4607fd3..1d9c2552 100644 --- a/app/modules/loyalty/routes/api/admin.py +++ b/app/modules/loyalty/routes/api/admin.py @@ -10,7 +10,7 @@ Platform admin endpoints for: import logging -from fastapi import APIRouter, Depends, Path, Query +from fastapi import APIRouter, Depends, Path, Query, Request from pydantic import BaseModel, Field from sqlalchemy.orm import Session @@ -412,6 +412,7 @@ def get_merchant_card( response_model=TransactionListResponse, ) def get_merchant_card_transactions( + request: Request, merchant_id: int = Path(..., gt=0), card_id: int = Path(..., gt=0), skip: int = Query(0, ge=0), @@ -430,10 +431,26 @@ def get_merchant_card_transactions( db, card_id, skip=skip, limit=limit ) - return TransactionListResponse( - transactions=[TransactionResponse.model_validate(t) for t in transactions], - total=total, - ) + # Enrich category_names from category_ids (same as store + merchant routes + # — keeps the transaction history aligned across personas). + from app.modules.loyalty.services.category_service import category_service + + lang = getattr(request.state, "language", "en") or "en" + tx_responses = [] + for t in transactions: + tx = TransactionResponse.model_validate(t) + if t.category_ids and isinstance(t.category_ids, list): + names = [] + for cid in t.category_ids: + name = category_service.validate_category_for_store( + db, cid, t.store_id or 0, lang=lang + ) + if name: + names.append(name) + tx.category_names = names if names else None + tx_responses.append(tx) + + return TransactionListResponse(transactions=tx_responses, total=total) @router.get("/merchants/{merchant_id}/transactions", response_model=TransactionListResponse) diff --git a/app/modules/loyalty/routes/api/merchant.py b/app/modules/loyalty/routes/api/merchant.py index 649c40aa..a9490762 100644 --- a/app/modules/loyalty/routes/api/merchant.py +++ b/app/modules/loyalty/routes/api/merchant.py @@ -23,7 +23,7 @@ registration under /api/v1/merchants/loyalty/*). import logging -from fastapi import APIRouter, Depends, Path, Query +from fastapi import APIRouter, Depends, Path, Query, Request from sqlalchemy.orm import Session from app.api.deps import get_current_merchant_api, get_merchant_for_current_user @@ -256,6 +256,7 @@ def get_card_detail( @router.get("/cards/{card_id}/transactions", response_model=TransactionListResponse) def get_card_transactions( + request: Request, card_id: int = Path(..., gt=0), skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=100), @@ -271,10 +272,26 @@ def get_card_transactions( db, card_id, skip=skip, limit=limit ) - return TransactionListResponse( - transactions=[TransactionResponse.model_validate(t) for t in transactions], - total=total, - ) + # Enrich category_names from category_ids (same as store route — keeps the + # transaction history aligned across personas). + from app.modules.loyalty.services.category_service import category_service + + lang = getattr(request.state, "language", "en") or "en" + tx_responses = [] + for t in transactions: + tx = TransactionResponse.model_validate(t) + if t.category_ids and isinstance(t.category_ids, list): + names = [] + for cid in t.category_ids: + name = category_service.validate_category_for_store( + db, cid, t.store_id or 0, lang=lang + ) + if name: + names.append(name) + tx.category_names = names if names else None + tx_responses.append(tx) + + return TransactionListResponse(transactions=tx_responses, total=total) # ============================================================================= diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html b/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html index b4879950..54631f70 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html @@ -17,6 +17,7 @@ {% set card_detail_api_prefix = '/admin/loyalty/merchants/' + merchant_id|string %} {% set card_detail_back_url = '/admin/loyalty/merchants/' + merchant_id|string + '/cards' %} +{% set show_category_column = true %} {% include 'loyalty/shared/card-detail-view.html' %} {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/card-detail.html b/app/modules/loyalty/templates/loyalty/merchant/card-detail.html index ad9e803f..0f3b926c 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/card-detail.html +++ b/app/modules/loyalty/templates/loyalty/merchant/card-detail.html @@ -20,6 +20,7 @@ {% set card_detail_api_prefix = '/merchants/loyalty' %} {% set card_detail_back_url = '/merchants/loyalty/cards' %} +{% set show_category_column = true %} {% include 'loyalty/shared/card-detail-view.html' %} {% endblock %}