- Add total_points_voided column to LoyaltyCard with migration (loyalty_002) - Fix storefront self_enroll to use correct service method signature and schema fields - Fix get_my_card/get_my_transactions to use get_card_by_customer_and_merchant - Fix transaction history field reference (balance_after -> points_balance_after) - Fix point_expiration task: wrong field names and manual balance update -> card.expire_points() - Register storefront_router in definition.py and export all routers from __init__.py - Enforce MerchantLoyaltySettings in storefront enrollment, points, and stamp void operations - Fix test fixture using non-existent balance_after column - Suppress intentional architecture validator warnings in templates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
170 lines
4.9 KiB
Python
170 lines
4.9 KiB
Python
# tests/fixtures/loyalty_fixtures.py
|
|
"""
|
|
Loyalty module test fixtures.
|
|
|
|
Provides fixtures for:
|
|
- Loyalty programs (merchant-based)
|
|
- Loyalty cards
|
|
- Transactions
|
|
- Staff PINs
|
|
"""
|
|
|
|
import uuid
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
import pytest
|
|
|
|
from app.modules.loyalty.models import (
|
|
LoyaltyCard,
|
|
LoyaltyProgram,
|
|
LoyaltyTransaction,
|
|
StaffPin,
|
|
)
|
|
from app.modules.loyalty.models.loyalty_program import LoyaltyType
|
|
from app.modules.loyalty.models.loyalty_transaction import TransactionType
|
|
|
|
|
|
@pytest.fixture
|
|
def test_loyalty_program(db, test_merchant):
|
|
"""Create a test loyalty program for a merchant."""
|
|
program = LoyaltyProgram(
|
|
merchant_id=test_merchant.id,
|
|
loyalty_type=LoyaltyType.POINTS.value,
|
|
points_per_euro=10,
|
|
welcome_bonus_points=50,
|
|
minimum_redemption_points=100,
|
|
minimum_purchase_cents=0,
|
|
points_expiration_days=365,
|
|
cooldown_minutes=15,
|
|
max_daily_stamps=5,
|
|
require_staff_pin=True,
|
|
card_name="Test Rewards",
|
|
card_color="#4F46E5",
|
|
is_active=True,
|
|
points_rewards=[
|
|
{"id": "reward_1", "name": "€5 off", "points_required": 100, "description": "€5 discount"},
|
|
{"id": "reward_2", "name": "€10 off", "points_required": 200, "description": "€10 discount"},
|
|
{"id": "reward_3", "name": "€25 off", "points_required": 500, "description": "€25 discount"},
|
|
],
|
|
)
|
|
db.add(program)
|
|
db.commit()
|
|
db.refresh(program)
|
|
return program
|
|
|
|
|
|
@pytest.fixture
|
|
def test_loyalty_program_no_expiration(db, test_merchant):
|
|
"""Create a test loyalty program without point expiration."""
|
|
from app.modules.tenancy.models import Merchant
|
|
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
merchant = Merchant(
|
|
name=f"No Expiry Merchant {unique_id}",
|
|
contact_email=f"noexpiry{unique_id}@test.com",
|
|
is_active=True,
|
|
)
|
|
db.add(merchant)
|
|
db.flush()
|
|
|
|
program = LoyaltyProgram(
|
|
merchant_id=merchant.id,
|
|
loyalty_type=LoyaltyType.POINTS.value,
|
|
points_per_euro=1,
|
|
welcome_bonus_points=0,
|
|
minimum_redemption_points=50,
|
|
points_expiration_days=None, # No expiration
|
|
cooldown_minutes=0,
|
|
max_daily_stamps=10,
|
|
require_staff_pin=False,
|
|
card_name="No Expiry Rewards",
|
|
is_active=True,
|
|
)
|
|
db.add(program)
|
|
db.commit()
|
|
db.refresh(program)
|
|
return program
|
|
|
|
|
|
@pytest.fixture
|
|
def test_loyalty_card(db, test_loyalty_program, test_customer, test_store):
|
|
"""Create a test loyalty card."""
|
|
unique_id = str(uuid.uuid4())[:8].upper()
|
|
card = LoyaltyCard(
|
|
merchant_id=test_loyalty_program.merchant_id,
|
|
program_id=test_loyalty_program.id,
|
|
customer_id=test_customer.id,
|
|
enrolled_at_store_id=test_store.id,
|
|
card_number=f"CARD-{unique_id}",
|
|
points_balance=100,
|
|
total_points_earned=150,
|
|
points_redeemed=50,
|
|
is_active=True,
|
|
last_activity_at=datetime.now(UTC),
|
|
)
|
|
db.add(card)
|
|
db.commit()
|
|
db.refresh(card)
|
|
return card
|
|
|
|
|
|
@pytest.fixture
|
|
def test_loyalty_card_inactive(db, test_loyalty_program, test_store):
|
|
"""Create a test loyalty card that hasn't been used in a long time (for expiration tests)."""
|
|
unique_id = str(uuid.uuid4())[:8].upper()
|
|
card = LoyaltyCard(
|
|
merchant_id=test_loyalty_program.merchant_id,
|
|
program_id=test_loyalty_program.id,
|
|
customer_id=None,
|
|
enrolled_at_store_id=test_store.id,
|
|
card_number=f"INACTIVE-{unique_id}",
|
|
points_balance=500,
|
|
total_points_earned=500,
|
|
is_active=True,
|
|
# Last activity was 400 days ago (beyond 365-day expiration)
|
|
last_activity_at=datetime.now(UTC) - timedelta(days=400),
|
|
)
|
|
db.add(card)
|
|
db.commit()
|
|
db.refresh(card)
|
|
return card
|
|
|
|
|
|
@pytest.fixture
|
|
def test_loyalty_transaction(db, test_loyalty_card, test_store):
|
|
"""Create a test loyalty transaction."""
|
|
transaction = LoyaltyTransaction(
|
|
merchant_id=test_loyalty_card.merchant_id,
|
|
card_id=test_loyalty_card.id,
|
|
store_id=test_store.id,
|
|
transaction_type=TransactionType.POINTS_EARNED.value,
|
|
points_delta=50,
|
|
points_balance_after=150,
|
|
stamps_delta=0,
|
|
stamps_balance_after=0,
|
|
notes="Test purchase",
|
|
transaction_at=datetime.now(UTC),
|
|
)
|
|
db.add(transaction)
|
|
db.commit()
|
|
db.refresh(transaction)
|
|
return transaction
|
|
|
|
|
|
@pytest.fixture
|
|
def test_staff_pin(db, test_loyalty_program, test_store):
|
|
"""Create a test staff PIN."""
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
pin = StaffPin(
|
|
program_id=test_loyalty_program.id,
|
|
merchant_id=test_loyalty_program.merchant_id,
|
|
store_id=test_store.id,
|
|
name=f"Test Staff {unique_id}",
|
|
is_active=True,
|
|
)
|
|
pin.set_pin("1234")
|
|
db.add(pin)
|
|
db.commit()
|
|
db.refresh(pin)
|
|
return pin
|