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:
@@ -70,7 +70,8 @@ class CustomerAddress(Base, TimestampMixin):
|
||||
address_line_2 = Column(String(255))
|
||||
city = Column(String(100), nullable=False)
|
||||
postal_code = Column(String(20), nullable=False)
|
||||
country = Column(String(100), nullable=False)
|
||||
country_name = Column(String(100), nullable=False)
|
||||
country_iso = Column(String(5), nullable=False)
|
||||
is_default = Column(Boolean, default=False)
|
||||
|
||||
# Relationships
|
||||
|
||||
@@ -126,7 +126,8 @@ class CustomerAddressCreate(BaseModel):
|
||||
address_line_2: str | None = Field(None, max_length=255)
|
||||
city: str = Field(..., min_length=1, max_length=100)
|
||||
postal_code: str = Field(..., min_length=1, max_length=20)
|
||||
country: str = Field(..., min_length=2, max_length=100)
|
||||
country_name: str = Field(..., min_length=2, max_length=100)
|
||||
country_iso: str = Field(..., min_length=2, max_length=5)
|
||||
is_default: bool = Field(default=False)
|
||||
|
||||
|
||||
@@ -141,7 +142,8 @@ class CustomerAddressUpdate(BaseModel):
|
||||
address_line_2: str | None = Field(None, max_length=255)
|
||||
city: str | None = Field(None, min_length=1, max_length=100)
|
||||
postal_code: str | None = Field(None, min_length=1, max_length=20)
|
||||
country: str | None = Field(None, min_length=2, max_length=100)
|
||||
country_name: str | None = Field(None, min_length=2, max_length=100)
|
||||
country_iso: str | None = Field(None, min_length=2, max_length=5)
|
||||
is_default: bool | None = None
|
||||
|
||||
|
||||
@@ -159,7 +161,8 @@ class CustomerAddressResponse(BaseModel):
|
||||
address_line_2: str | None
|
||||
city: str
|
||||
postal_code: str
|
||||
country: str
|
||||
country_name: str
|
||||
country_iso: str
|
||||
is_default: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
@@ -167,6 +170,13 @@ class CustomerAddressResponse(BaseModel):
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class CustomerAddressListResponse(BaseModel):
|
||||
"""Schema for customer address list response."""
|
||||
|
||||
addresses: list[CustomerAddressResponse]
|
||||
total: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Customer Preferences
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user