fix(loyalty): normalize card id field, fix terminal redeem bug
Some checks failed
CI / ruff (push) Successful in 15s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

The terminal redeem failed with "card not found: unknown" because
CardLookupResponse used card_id while CardDetailResponse (from
refreshCard) used id. After refresh, selectedCard.card_id was
undefined.

Fix: standardize on 'id' everywhere (the universal convention):
- CardLookupResponse: card_id → id
- _build_card_lookup_response: card_id= → id=
- loyalty-terminal.js: selectedCard.card_id → selectedCard.id (5 refs)
- Removed the card_id/model_validator approach as unnecessary

Also fixes Chart.js recursion error on analytics page (inline CDN
script instead of optional-libs.html include which caused infinite
template recursion in test context).

342 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 22:01:26 +02:00
parent 3044490a3e
commit 64fe58c171
4 changed files with 8 additions and 9 deletions

View File

@@ -420,7 +420,7 @@ def _build_card_lookup_response(card, db=None) -> CardLookupResponse:
available_rewards.append(reward)
return CardLookupResponse(
card_id=card.id,
id=card.id,
card_number=card.card_number,
customer_id=card.customer_id,
customer_name=card.customer.full_name if card.customer else None,

View File

@@ -128,7 +128,7 @@ class CardLookupResponse(BaseModel):
"""Schema for card lookup by QR code or card number."""
# Card info
card_id: int
id: int
card_number: str
# Customer

View File

@@ -284,7 +284,7 @@ function storeLoyaltyTerminal() {
loyaltyTerminalLog.info('Adding stamp...');
await apiClient.post('/store/loyalty/stamp', {
card_id: this.selectedCard.card_id,
card_id: this.selectedCard.id,
staff_pin: this.pinDigits
});
@@ -296,7 +296,7 @@ function storeLoyaltyTerminal() {
loyaltyTerminalLog.info('Redeeming stamps...');
await apiClient.post('/store/loyalty/stamp/redeem', {
card_id: this.selectedCard.card_id,
card_id: this.selectedCard.id,
staff_pin: this.pinDigits
});
@@ -308,7 +308,7 @@ function storeLoyaltyTerminal() {
loyaltyTerminalLog.info('Earning points...', { amount: this.earnAmount });
const response = await apiClient.post('/store/loyalty/points/earn', {
card_id: this.selectedCard.card_id,
card_id: this.selectedCard.id,
purchase_amount_cents: Math.round(this.earnAmount * 100),
staff_pin: this.pinDigits
});
@@ -327,7 +327,7 @@ function storeLoyaltyTerminal() {
loyaltyTerminalLog.info('Redeeming reward...', { reward: reward.name });
await apiClient.post('/store/loyalty/points/redeem', {
card_id: this.selectedCard.card_id,
card_id: this.selectedCard.id,
reward_id: this.selectedReward,
staff_pin: this.pinDigits
});
@@ -340,7 +340,7 @@ function storeLoyaltyTerminal() {
// Refresh card data
async refreshCard() {
try {
const response = await apiClient.get(`/store/loyalty/cards/${this.selectedCard.card_id}`);
const response = await apiClient.get(`/store/loyalty/cards/${this.selectedCard.id}`);
if (response) {
this.selectedCard = response;
}

View File

@@ -155,7 +155,6 @@
{% endblock %}
{% block extra_scripts %}
{% include 'shared/includes/optional-libs.html' with context %}
{{ chartjs_loader() }}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script defer src="{{ url_for('loyalty_static', path='store/js/loyalty-analytics.js') }}"></script>
{% endblock %}