fix: loyalty storefront and store card detail — enrollment, context, and Alpine.js
Some checks failed
Some checks failed
- Fix storefront enabled_modules always empty (page_context overwrote computed
set with empty default via extra_context)
- Fix storefront loyalty JS using store's data() instead of shopLayoutData()
- Remove defer from storefront loyalty scripts to prevent Alpine race condition
- Fix enrollment field name mismatch (customer_email → email) in both store
and storefront JS
- Add self-enrollment customer creation (resolve_customer_id with
create_if_missing) including hashed_password and customer_number
- Fix card list showing "Unknown" — add customer_name/email to CardResponse
- Add GET /cards/{card_id} detail endpoint for store card detail page
- Fix enroll-success.html using data() instead of shopLayoutData()
- Fix enrollment redirect reading response.card_number instead of
response.card.card_number
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -138,6 +138,10 @@ class CardService:
|
||||
customer_id: int | None,
|
||||
email: str | None,
|
||||
store_id: int,
|
||||
create_if_missing: bool = False,
|
||||
customer_name: str | None = None,
|
||||
customer_phone: str | None = None,
|
||||
customer_birthday: str | None = None,
|
||||
) -> int:
|
||||
"""
|
||||
Resolve a customer ID from either a direct ID or email lookup.
|
||||
@@ -147,13 +151,18 @@ class CardService:
|
||||
customer_id: Direct customer ID (used if provided)
|
||||
email: Customer email to look up
|
||||
store_id: Store ID for scoping the email lookup
|
||||
create_if_missing: If True, create customer when email not found
|
||||
(used for self-enrollment)
|
||||
customer_name: Full name for customer creation
|
||||
customer_phone: Phone for customer creation
|
||||
customer_birthday: Birthday (YYYY-MM-DD) for customer creation
|
||||
|
||||
Returns:
|
||||
Resolved customer ID
|
||||
|
||||
Raises:
|
||||
CustomerIdentifierRequiredException: If neither customer_id nor email provided
|
||||
CustomerNotFoundByEmailException: If email lookup fails
|
||||
CustomerNotFoundByEmailException: If email lookup fails and create_if_missing is False
|
||||
"""
|
||||
if customer_id:
|
||||
return customer_id
|
||||
@@ -166,9 +175,53 @@ class CardService:
|
||||
.filter(Customer.email == email, Customer.store_id == store_id)
|
||||
.first()
|
||||
)
|
||||
if not customer:
|
||||
raise CustomerNotFoundByEmailException(email)
|
||||
return customer.id
|
||||
if customer:
|
||||
return customer.id
|
||||
|
||||
if create_if_missing:
|
||||
import secrets
|
||||
|
||||
from app.modules.customers.services.customer_service import (
|
||||
customer_service,
|
||||
)
|
||||
from app.modules.tenancy.models.store import Store
|
||||
|
||||
store = db.query(Store).filter(Store.id == store_id).first()
|
||||
store_code = store.store_code if store else "STORE"
|
||||
|
||||
# Parse name into first/last
|
||||
first_name = customer_name or ""
|
||||
last_name = ""
|
||||
if customer_name and " " in customer_name:
|
||||
parts = customer_name.split(" ", 1)
|
||||
first_name = parts[0]
|
||||
last_name = parts[1]
|
||||
|
||||
# Generate unusable password hash and unique customer number
|
||||
unusable_hash = f"!loyalty-enroll!{secrets.token_hex(32)}"
|
||||
cust_number = customer_service._generate_customer_number(
|
||||
db, store_id, store_code
|
||||
)
|
||||
|
||||
customer = Customer(
|
||||
email=email,
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
phone=customer_phone,
|
||||
hashed_password=unusable_hash,
|
||||
customer_number=cust_number,
|
||||
store_id=store_id,
|
||||
is_active=True,
|
||||
)
|
||||
db.add(customer)
|
||||
db.flush()
|
||||
logger.info(
|
||||
f"Created customer {customer.id} ({email}) "
|
||||
f"number={cust_number} for self-enrollment"
|
||||
)
|
||||
return customer.id
|
||||
|
||||
raise CustomerNotFoundByEmailException(email)
|
||||
|
||||
raise CustomerIdentifierRequiredException()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user