feat: add configurable currency locale and fix vendor JS init

Currency Locale Configuration:
- Add platform-level storefront settings (locale, currency)
- Create PlatformSettingsService with resolution chain:
  vendor → AdminSetting → environment → hardcoded fallback
- Add storefront_locale nullable field to Vendor model
- Update shop routes to resolve and pass locale to templates
- Add window.SHOP_CONFIG for frontend JavaScript access
- Centralize formatPrice() in shop-layout.js using SHOP_CONFIG
- Remove local formatPrice functions from shop templates

Vendor JS Bug Fix:
- Fix vendorCode being null on all vendor pages
- Root cause: page components overriding init() without calling parent
- Add parent init call to 14 vendor JS files
- Add JS-013 architecture rule to prevent future regressions
- Validator now checks vendor JS files for parent init pattern

Files changed:
- New: app/services/platform_settings_service.py
- New: alembic/versions/s7a8b9c0d1e2_add_storefront_locale_to_vendors.py
- Modified: 14 vendor JS files, shop templates, validation scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-02 21:26:12 +01:00
parent d9d34ab102
commit c87bdfa129
30 changed files with 522 additions and 48 deletions

View File

@@ -0,0 +1,40 @@
# alembic/versions/s7a8b9c0d1e2_add_storefront_locale_to_vendors.py
"""Add storefront_locale to vendors for currency formatting.
Revision ID: s7a8b9c0d1e2
Revises: r6f7a8b9c0d1
Create Date: 2026-01-02 20:00:00.000000
This migration adds a nullable storefront_locale field to vendors.
NULL means the vendor inherits from platform defaults.
Examples: 'fr-LU', 'de-DE', 'en-GB'
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "s7a8b9c0d1e2"
down_revision = "r6f7a8b9c0d1"
branch_labels = None
depends_on = None
def upgrade() -> None:
"""Add storefront_locale column to vendors table."""
# Nullable - NULL means "inherit from platform default"
op.add_column(
"vendors",
sa.Column(
"storefront_locale",
sa.String(10),
nullable=True,
comment="Currency/number formatting locale (NULL = inherit from platform)",
),
)
def downgrade() -> None:
"""Remove storefront_locale column from vendors table."""
op.drop_column("vendors", "storefront_locale")