# Store Contact Inheritance Migration

## Overview

**Feature:** Add contact information fields to Store model with inheritance from Merchant.

**Pattern:** Nullable with Fallback - Store fields are nullable; if null, inherit from parent merchant at read time.

**Benefits:**
- Stores inherit merchant contact info by default
- Can override specific fields for store-specific branding/identity
- Can reset to "inherit from merchant" by setting field to null
- Merchant updates automatically reflect in stores that haven't overridden

---

## Database Changes

### New Columns in `store` Table

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `contact_email` | VARCHAR(255) | Yes | NULL | Override merchant contact email |
| `contact_phone` | VARCHAR(50) | Yes | NULL | Override merchant contact phone |
| `website` | VARCHAR(255) | Yes | NULL | Override merchant website |
| `business_address` | TEXT | Yes | NULL | Override merchant business address |
| `tax_number` | VARCHAR(100) | Yes | NULL | Override merchant tax number |

### Resolution Logic

```
effective_value = store.field if store.field is not None else store.merchant.field
```

---

## Files to Modify

### 1. Database Model
- `models/database/store.py` - Add nullable contact fields

### 2. Alembic Migration
- `alembic/versions/xxx_add_store_contact_fields.py` - New migration

### 3. Pydantic Schemas
- `models/schema/store.py`:
  - `StoreUpdate` - Add optional contact fields
  - `StoreResponse` - Add resolved contact fields
  - `StoreDetailResponse` - Add contact fields with inheritance indicator

### 4. Service Layer
- `app/services/store_service.py` - Add contact resolution helper
- `app/services/admin_service.py` - Update create/update to handle contact fields

### 5. API Endpoints
- `app/api/v1/admin/stores.py` - Update responses to include resolved contact info

### 6. Frontend
- `app/templates/admin/store-edit.html` - Add contact fields with inheritance toggle
- `static/admin/js/store-edit.js` - Handle inheritance UI logic

---

## Implementation Steps

### Step 1: Database Model

```python
# models/database/store.py

class Store(Base):
    # ... existing fields ...

    # Contact fields (nullable = inherit from merchant)
    contact_email = Column(String(255), nullable=True)
    contact_phone = Column(String(50), nullable=True)
    website = Column(String(255), nullable=True)
    business_address = Column(Text, nullable=True)
    tax_number = Column(String(100), nullable=True)

    # Helper properties for resolved values
    @property
    def effective_contact_email(self) -> str | None:
        return self.contact_email if self.contact_email is not None else (
            self.merchant.contact_email if self.merchant else None
        )

    @property
    def effective_contact_phone(self) -> str | None:
        return self.contact_phone if self.contact_phone is not None else (
            self.merchant.contact_phone if self.merchant else None
        )

    # ... similar for other fields ...
```

### Step 2: Alembic Migration

```python
def upgrade():
    op.add_column('store', sa.Column('contact_email', sa.String(255), nullable=True))
    op.add_column('store', sa.Column('contact_phone', sa.String(50), nullable=True))
    op.add_column('store', sa.Column('website', sa.String(255), nullable=True))
    op.add_column('store', sa.Column('business_address', sa.Text(), nullable=True))
    op.add_column('store', sa.Column('tax_number', sa.String(100), nullable=True))

def downgrade():
    op.drop_column('store', 'tax_number')
    op.drop_column('store', 'business_address')
    op.drop_column('store', 'website')
    op.drop_column('store', 'contact_phone')
    op.drop_column('store', 'contact_email')
```

### Step 3: Pydantic Schemas

```python
# models/schema/store.py

class StoreUpdate(BaseModel):
    # ... existing fields ...

    # Contact fields (None = don't update, empty string could mean "clear/inherit")
    contact_email: str | None = None
    contact_phone: str | None = None
    website: str | None = None
    business_address: str | None = None
    tax_number: str | None = None

class StoreContactInfo(BaseModel):
    """Resolved contact information with inheritance indicators."""
    contact_email: str | None
    contact_phone: str | None
    website: str | None
    business_address: str | None
    tax_number: str | None

    # Inheritance flags
    contact_email_inherited: bool = False
    contact_phone_inherited: bool = False
    website_inherited: bool = False
    business_address_inherited: bool = False
    tax_number_inherited: bool = False

class StoreDetailResponse(BaseModel):
    # ... existing fields ...

    # Resolved contact info
    contact_email: str | None
    contact_phone: str | None
    website: str | None
    business_address: str | None
    tax_number: str | None

    # Inheritance indicators (for UI)
    contact_email_inherited: bool
    contact_phone_inherited: bool
    website_inherited: bool
    business_address_inherited: bool
    tax_number_inherited: bool
```

### Step 4: Service Layer Helper

```python
# app/services/store_service.py

def get_resolved_contact_info(self, store: Store) -> dict:
    """
    Get resolved contact information with inheritance flags.

    Returns dict with both values and flags indicating if inherited.
    """
    merchant = store.merchant

    return {
        "contact_email": store.contact_email or (merchant.contact_email if merchant else None),
        "contact_email_inherited": store.contact_email is None and merchant is not None,

        "contact_phone": store.contact_phone or (merchant.contact_phone if merchant else None),
        "contact_phone_inherited": store.contact_phone is None and merchant is not None,

        "website": store.website or (merchant.website if merchant else None),
        "website_inherited": store.website is None and merchant is not None,

        "business_address": store.business_address or (merchant.business_address if merchant else None),
        "business_address_inherited": store.business_address is None and merchant is not None,

        "tax_number": store.tax_number or (merchant.tax_number if merchant else None),
        "tax_number_inherited": store.tax_number is None and merchant is not None,
    }
```

### Step 5: API Endpoint Updates

```python
# app/api/v1/admin/stores.py

@router.get("/{store_identifier}", response_model=StoreDetailResponse)
def get_store_details(...):
    store = store_service.get_store_by_identifier(db, store_identifier)
    contact_info = store_service.get_resolved_contact_info(store)

    return StoreDetailResponse(
        # ... existing fields ...
        **contact_info,  # Includes values and inheritance flags
    )
```

### Step 6: Frontend UI

```html
<!-- Store edit form with inheritance toggle -->
<label class="block text-sm">
    <span class="text-gray-700 dark:text-gray-400">
        Contact Email
        <span x-show="contactEmailInherited" class="text-xs text-purple-500">(inherited from merchant)</span>
    </span>
    <div class="flex gap-2">
        <input type="email" x-model="formData.contact_email"
               :placeholder="merchantContactEmail"
               :class="{ 'bg-gray-100': contactEmailInherited }">
        <button type="button" @click="resetToMerchant('contact_email')"
                x-show="!contactEmailInherited"
                class="text-sm text-purple-600">
            Reset to Merchant
        </button>
    </div>
</label>
```

---

## API Behavior

### GET /api/v1/admin/stores/{id}

Returns resolved contact info with inheritance flags:

```json
{
  "id": 1,
  "store_code": "STORE001",
  "name": "My Store",
  "contact_email": "sales@merchant.com",
  "contact_email_inherited": true,
  "contact_phone": "+352 123 456",
  "contact_phone_inherited": false,
  "website": "https://merchant.com",
  "website_inherited": true
}
```

### PUT /api/v1/admin/stores/{id}

To override a field:
```json
{
  "contact_email": "store-specific@example.com"
}
```

To reset to inherit from merchant (set to null):
```json
{
  "contact_email": null
}
```

---

## Testing Plan

1. **Create store** - Verify contact fields are null (inheriting)
2. **Read store** - Verify resolved values come from merchant
3. **Update store contact** - Verify override works
4. **Reset to inherit** - Verify setting null restores inheritance
5. **Update merchant** - Verify change reflects in inheriting stores
6. **Update merchant** - Verify change does NOT affect overridden stores

---

## Rollback Plan

If issues occur:
1. Run downgrade migration: `alembic downgrade -1`
2. Revert code changes
3. Re-deploy

---

## Progress Tracking

- [ ] Database model updated
- [ ] Alembic migration created and applied
- [ ] Pydantic schemas updated
- [ ] Service layer helper added
- [ ] API endpoints updated
- [ ] Frontend forms updated
- [ ] Tests written and passing
- [ ] Documentation updated
