feat: add customer multiple addresses management

- Add CustomerAddressService with CRUD operations
- Add shop API endpoints for address management (GET, POST, PUT, DELETE)
- Add set default endpoint for address type
- Implement addresses.html with full UI (cards, modals, Alpine.js)
- Integrate saved addresses in checkout flow
  - Address selector dropdowns for shipping/billing
  - Auto-select default addresses
  - Save new address checkbox option
- Add country_iso field alongside country_name
- Add address exceptions (NotFound, LimitExceeded, InvalidType)
- Max 10 addresses per customer limit
- One default address per type (shipping/billing)
- Add unit tests for CustomerAddressService
- Add integration tests for shop addresses API

🤖 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 19:16:35 +01:00
parent ea0218746f
commit b5b32fb351
15 changed files with 3940 additions and 17 deletions

View File

@@ -32,7 +32,7 @@ def test_customer(db, test_vendor):
@pytest.fixture
def test_customer_address(db, test_vendor, test_customer):
"""Create a test customer address."""
"""Create a test customer shipping address."""
address = CustomerAddress(
vendor_id=test_vendor.id,
customer_id=test_customer.id,
@@ -42,7 +42,8 @@ def test_customer_address(db, test_vendor, test_customer):
address_line_1="123 Main St",
city="Luxembourg",
postal_code="L-1234",
country="Luxembourg",
country_name="Luxembourg",
country_iso="LU",
is_default=True,
)
db.add(address)
@@ -51,6 +52,55 @@ def test_customer_address(db, test_vendor, test_customer):
return address
@pytest.fixture
def test_customer_billing_address(db, test_vendor, test_customer):
"""Create a test customer billing address."""
address = CustomerAddress(
vendor_id=test_vendor.id,
customer_id=test_customer.id,
address_type="billing",
first_name="John",
last_name="Doe",
company="Test Company S.A.",
address_line_1="456 Business Ave",
city="Luxembourg",
postal_code="L-5678",
country_name="Luxembourg",
country_iso="LU",
is_default=True,
)
db.add(address)
db.commit()
db.refresh(address)
return address
@pytest.fixture
def test_customer_multiple_addresses(db, test_vendor, test_customer):
"""Create multiple addresses for testing limits and listing."""
addresses = []
for i in range(3):
address = CustomerAddress(
vendor_id=test_vendor.id,
customer_id=test_customer.id,
address_type="shipping" if i % 2 == 0 else "billing",
first_name=f"Name{i}",
last_name="Test",
address_line_1=f"{i}00 Test Street",
city="Luxembourg",
postal_code=f"L-{1000+i}",
country_name="Luxembourg",
country_iso="LU",
is_default=(i == 0),
)
db.add(address)
addresses.append(address)
db.commit()
for addr in addresses:
db.refresh(addr)
return addresses
@pytest.fixture
def test_order(db, test_vendor, test_customer, test_customer_address):
"""Create a test order with customer/address snapshots."""