From 846f92e7e480c57a8a5c41a7ca1d6d0478980e41 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Wed, 3 Dec 2025 22:30:31 +0100 Subject: [PATCH] feat(vendor): add contact info inheritance from company MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendors can now override company contact information for specific branding. Fields are nullable - if null, value is inherited from parent company. Database changes: - Add vendor.contact_email, contact_phone, website, business_address, tax_number - All nullable (null = inherit from company) - Alembic migration: 28d44d503cac Model changes: - Add effective_* properties for resolved values - Add get_contact_info_with_inheritance() helper Schema changes: - VendorCreate: Optional contact fields for override at creation - VendorUpdate: Contact fields + reset_contact_to_company flag - VendorDetailResponse: Resolved values + *_inherited flags API changes: - GET/PUT vendor endpoints return resolved contact info - PUT accepts contact overrides (empty string = reset to inherit) - _build_vendor_detail_response helper for consistent responses Service changes: - admin_service.update_vendor handles reset_contact_to_company flag - Empty strings converted to None for inheritance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ...d44d503cac_add_contact_fields_to_vendor.py | 37 +++ app/api/v1/admin/vendors.py | 125 +++----- app/services/admin_service.py | 15 + .../migration/vendor-contact-inheritance.md | 301 ++++++++++++++++++ models/database/vendor.py | 71 +++++ models/schema/vendor.py | 53 ++- 6 files changed, 506 insertions(+), 96 deletions(-) create mode 100644 alembic/versions/28d44d503cac_add_contact_fields_to_vendor.py create mode 100644 docs/development/migration/vendor-contact-inheritance.md diff --git a/alembic/versions/28d44d503cac_add_contact_fields_to_vendor.py b/alembic/versions/28d44d503cac_add_contact_fields_to_vendor.py new file mode 100644 index 00000000..efe0a18d --- /dev/null +++ b/alembic/versions/28d44d503cac_add_contact_fields_to_vendor.py @@ -0,0 +1,37 @@ +"""add contact fields to vendor + +Revision ID: 28d44d503cac +Revises: 9f3a25ea4991 +Create Date: 2025-12-03 22:26:02.161087 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '28d44d503cac' +down_revision: Union[str, None] = '9f3a25ea4991' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Add nullable contact fields to vendor table + # These allow vendor-specific branding/identity, overriding company defaults + op.add_column('vendors', sa.Column('contact_email', sa.String(255), nullable=True)) + op.add_column('vendors', sa.Column('contact_phone', sa.String(50), nullable=True)) + op.add_column('vendors', sa.Column('website', sa.String(255), nullable=True)) + op.add_column('vendors', sa.Column('business_address', sa.Text(), nullable=True)) + op.add_column('vendors', sa.Column('tax_number', sa.String(100), nullable=True)) + + +def downgrade() -> None: + # Remove contact fields from vendor table + op.drop_column('vendors', 'tax_number') + op.drop_column('vendors', 'business_address') + op.drop_column('vendors', 'website') + op.drop_column('vendors', 'contact_phone') + op.drop_column('vendors', 'contact_email') diff --git a/app/api/v1/admin/vendors.py b/app/api/v1/admin/vendors.py index b2081734..9546d26f 100644 --- a/app/api/v1/admin/vendors.py +++ b/app/api/v1/admin/vendors.py @@ -116,23 +116,14 @@ def get_vendor_statistics_endpoint( ) -@router.get("/{vendor_identifier}", response_model=VendorDetailResponse) -def get_vendor_details( - vendor_identifier: str = Path(..., description="Vendor ID or vendor_code"), - db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_api), -): +def _build_vendor_detail_response(vendor) -> VendorDetailResponse: """ - Get detailed vendor information including company and owner details (Admin only). + Helper to build VendorDetailResponse with resolved contact info. - Accepts either vendor ID (integer) or vendor_code (string). - - Returns vendor info with company contact details and owner info. - - Raises: - VendorNotFoundException: If vendor not found (404) + Contact fields are resolved using vendor override or company fallback. + Inheritance flags indicate if value comes from company. """ - vendor = vendor_service.get_vendor_by_identifier(db, vendor_identifier) + contact_info = vendor.get_contact_info_with_inheritance() return VendorDetailResponse( # Vendor fields @@ -151,15 +142,39 @@ def get_vendor_details( updated_at=vendor.updated_at, # Company info company_name=vendor.company.name, - company_contact_email=vendor.company.contact_email, - company_contact_phone=vendor.company.contact_phone, - company_website=vendor.company.website, # Owner details (from company) owner_email=vendor.company.owner.email, owner_username=vendor.company.owner.username, + # Resolved contact info with inheritance flags + **contact_info, + # Original company values for UI reference + company_contact_email=vendor.company.contact_email, + company_contact_phone=vendor.company.contact_phone, + company_website=vendor.company.website, ) +@router.get("/{vendor_identifier}", response_model=VendorDetailResponse) +def get_vendor_details( + vendor_identifier: str = Path(..., description="Vendor ID or vendor_code"), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_api), +): + """ + Get detailed vendor information including company and owner details (Admin only). + + Accepts either vendor ID (integer) or vendor_code (string). + + Returns vendor info with company contact details, owner info, and + resolved contact fields (vendor override or company default). + + Raises: + VendorNotFoundException: If vendor not found (404) + """ + vendor = vendor_service.get_vendor_by_identifier(db, vendor_identifier) + return _build_vendor_detail_response(vendor) + + @router.put("/{vendor_identifier}", response_model=VendorDetailResponse) def update_vendor( vendor_identifier: str = Path(..., description="Vendor ID or vendor_code"), @@ -176,40 +191,18 @@ def update_vendor( - Basic info: name, description, subdomain - Marketplace URLs - Status: is_active, is_verified + - Contact info: contact_email, contact_phone, website, business_address, tax_number + (these override company defaults; set to empty to reset to inherit) **Cannot update:** - `vendor_code` (immutable) - - Business contact info (use company update endpoints) Raises: VendorNotFoundException: If vendor not found (404) """ vendor = vendor_service.get_vendor_by_identifier(db, vendor_identifier) vendor = admin_service.update_vendor(db, vendor.id, vendor_update) - - return VendorDetailResponse( - id=vendor.id, - vendor_code=vendor.vendor_code, - subdomain=vendor.subdomain, - name=vendor.name, - description=vendor.description, - company_id=vendor.company_id, - letzshop_csv_url_fr=vendor.letzshop_csv_url_fr, - letzshop_csv_url_en=vendor.letzshop_csv_url_en, - letzshop_csv_url_de=vendor.letzshop_csv_url_de, - is_active=vendor.is_active, - is_verified=vendor.is_verified, - created_at=vendor.created_at, - updated_at=vendor.updated_at, - # Company info - company_name=vendor.company.name, - company_contact_email=vendor.company.contact_email, - company_contact_phone=vendor.company.contact_phone, - company_website=vendor.company.website, - # Owner details (from company) - owner_email=vendor.company.owner.email, - owner_username=vendor.company.owner.username, - ) + return _build_vendor_detail_response(vendor) # NOTE: Ownership transfer is now at the Company level. @@ -243,29 +236,7 @@ def toggle_vendor_verification( db.commit() # ✅ ARCH: Commit at API level for transaction control logger.info(f"Vendor verification updated: {message}") - return VendorDetailResponse( - id=vendor.id, - vendor_code=vendor.vendor_code, - subdomain=vendor.subdomain, - name=vendor.name, - description=vendor.description, - company_id=vendor.company_id, - letzshop_csv_url_fr=vendor.letzshop_csv_url_fr, - letzshop_csv_url_en=vendor.letzshop_csv_url_en, - letzshop_csv_url_de=vendor.letzshop_csv_url_de, - is_active=vendor.is_active, - is_verified=vendor.is_verified, - created_at=vendor.created_at, - updated_at=vendor.updated_at, - # Company info - company_name=vendor.company.name, - company_contact_email=vendor.company.contact_email, - company_contact_phone=vendor.company.contact_phone, - company_website=vendor.company.website, - # Owner details (from company) - owner_email=vendor.company.owner.email, - owner_username=vendor.company.owner.username, - ) + return _build_vendor_detail_response(vendor) @router.put("/{vendor_identifier}/status", response_model=VendorDetailResponse) @@ -294,29 +265,7 @@ def toggle_vendor_status( db.commit() # ✅ ARCH: Commit at API level for transaction control logger.info(f"Vendor status updated: {message}") - return VendorDetailResponse( - id=vendor.id, - vendor_code=vendor.vendor_code, - subdomain=vendor.subdomain, - name=vendor.name, - description=vendor.description, - company_id=vendor.company_id, - letzshop_csv_url_fr=vendor.letzshop_csv_url_fr, - letzshop_csv_url_en=vendor.letzshop_csv_url_en, - letzshop_csv_url_de=vendor.letzshop_csv_url_de, - is_active=vendor.is_active, - is_verified=vendor.is_verified, - created_at=vendor.created_at, - updated_at=vendor.updated_at, - # Company info - company_name=vendor.company.name, - company_contact_email=vendor.company.contact_email, - company_contact_phone=vendor.company.contact_phone, - company_website=vendor.company.website, - # Owner details (from company) - owner_email=vendor.company.owner.email, - owner_username=vendor.company.owner.username, - ) + return _build_vendor_detail_response(vendor) @router.delete("/{vendor_identifier}") diff --git a/app/services/admin_service.py b/app/services/admin_service.py index 4da081a1..7f6c9dc7 100644 --- a/app/services/admin_service.py +++ b/app/services/admin_service.py @@ -369,6 +369,21 @@ class AdminService: # Get update data update_data = vendor_update.model_dump(exclude_unset=True) + # Handle reset_contact_to_company flag + if update_data.pop("reset_contact_to_company", False): + # Reset all contact fields to None (inherit from company) + update_data["contact_email"] = None + update_data["contact_phone"] = None + update_data["website"] = None + update_data["business_address"] = None + update_data["tax_number"] = None + + # Convert empty strings to None for contact fields (empty = inherit) + contact_fields = ["contact_email", "contact_phone", "website", "business_address", "tax_number"] + for field in contact_fields: + if field in update_data and update_data[field] == "": + update_data[field] = None + # Check subdomain uniqueness if changing if ( "subdomain" in update_data diff --git a/docs/development/migration/vendor-contact-inheritance.md b/docs/development/migration/vendor-contact-inheritance.md new file mode 100644 index 00000000..af3f72be --- /dev/null +++ b/docs/development/migration/vendor-contact-inheritance.md @@ -0,0 +1,301 @@ +# Vendor Contact Inheritance Migration + +## Overview + +**Feature:** Add contact information fields to Vendor model with inheritance from Company. + +**Pattern:** Nullable with Fallback - Vendor fields are nullable; if null, inherit from parent company at read time. + +**Benefits:** +- Vendors inherit company contact info by default +- Can override specific fields for vendor-specific branding/identity +- Can reset to "inherit from company" by setting field to null +- Company updates automatically reflect in vendors that haven't overridden + +--- + +## Database Changes + +### New Columns in `vendor` Table + +| Column | Type | Nullable | Default | Description | +|--------|------|----------|---------|-------------| +| `contact_email` | VARCHAR(255) | Yes | NULL | Override company contact email | +| `contact_phone` | VARCHAR(50) | Yes | NULL | Override company contact phone | +| `website` | VARCHAR(255) | Yes | NULL | Override company website | +| `business_address` | TEXT | Yes | NULL | Override company business address | +| `tax_number` | VARCHAR(100) | Yes | NULL | Override company tax number | + +### Resolution Logic + +``` +effective_value = vendor.field if vendor.field is not None else vendor.company.field +``` + +--- + +## Files to Modify + +### 1. Database Model +- `models/database/vendor.py` - Add nullable contact fields + +### 2. Alembic Migration +- `alembic/versions/xxx_add_vendor_contact_fields.py` - New migration + +### 3. Pydantic Schemas +- `models/schema/vendor.py`: + - `VendorUpdate` - Add optional contact fields + - `VendorResponse` - Add resolved contact fields + - `VendorDetailResponse` - Add contact fields with inheritance indicator + +### 4. Service Layer +- `app/services/vendor_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/vendors.py` - Update responses to include resolved contact info + +### 6. Frontend +- `app/templates/admin/vendor-edit.html` - Add contact fields with inheritance toggle +- `static/admin/js/vendor-edit.js` - Handle inheritance UI logic + +--- + +## Implementation Steps + +### Step 1: Database Model + +```python +# models/database/vendor.py + +class Vendor(Base): + # ... existing fields ... + + # Contact fields (nullable = inherit from company) + 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.company.contact_email if self.company else None + ) + + @property + def effective_contact_phone(self) -> str | None: + return self.contact_phone if self.contact_phone is not None else ( + self.company.contact_phone if self.company else None + ) + + # ... similar for other fields ... +``` + +### Step 2: Alembic Migration + +```python +def upgrade(): + op.add_column('vendor', sa.Column('contact_email', sa.String(255), nullable=True)) + op.add_column('vendor', sa.Column('contact_phone', sa.String(50), nullable=True)) + op.add_column('vendor', sa.Column('website', sa.String(255), nullable=True)) + op.add_column('vendor', sa.Column('business_address', sa.Text(), nullable=True)) + op.add_column('vendor', sa.Column('tax_number', sa.String(100), nullable=True)) + +def downgrade(): + op.drop_column('vendor', 'tax_number') + op.drop_column('vendor', 'business_address') + op.drop_column('vendor', 'website') + op.drop_column('vendor', 'contact_phone') + op.drop_column('vendor', 'contact_email') +``` + +### Step 3: Pydantic Schemas + +```python +# models/schema/vendor.py + +class VendorUpdate(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 VendorContactInfo(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 VendorDetailResponse(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/vendor_service.py + +def get_resolved_contact_info(self, vendor: Vendor) -> dict: + """ + Get resolved contact information with inheritance flags. + + Returns dict with both values and flags indicating if inherited. + """ + company = vendor.company + + return { + "contact_email": vendor.contact_email or (company.contact_email if company else None), + "contact_email_inherited": vendor.contact_email is None and company is not None, + + "contact_phone": vendor.contact_phone or (company.contact_phone if company else None), + "contact_phone_inherited": vendor.contact_phone is None and company is not None, + + "website": vendor.website or (company.website if company else None), + "website_inherited": vendor.website is None and company is not None, + + "business_address": vendor.business_address or (company.business_address if company else None), + "business_address_inherited": vendor.business_address is None and company is not None, + + "tax_number": vendor.tax_number or (company.tax_number if company else None), + "tax_number_inherited": vendor.tax_number is None and company is not None, + } +``` + +### Step 5: API Endpoint Updates + +```python +# app/api/v1/admin/vendors.py + +@router.get("/{vendor_identifier}", response_model=VendorDetailResponse) +def get_vendor_details(...): + vendor = vendor_service.get_vendor_by_identifier(db, vendor_identifier) + contact_info = vendor_service.get_resolved_contact_info(vendor) + + return VendorDetailResponse( + # ... existing fields ... + **contact_info, # Includes values and inheritance flags + ) +``` + +### Step 6: Frontend UI + +```html + + +``` + +--- + +## API Behavior + +### GET /api/v1/admin/vendors/{id} + +Returns resolved contact info with inheritance flags: + +```json +{ + "id": 1, + "vendor_code": "VENDOR001", + "name": "My Vendor", + "contact_email": "sales@company.com", + "contact_email_inherited": true, + "contact_phone": "+352 123 456", + "contact_phone_inherited": false, + "website": "https://company.com", + "website_inherited": true +} +``` + +### PUT /api/v1/admin/vendors/{id} + +To override a field: +```json +{ + "contact_email": "vendor-specific@example.com" +} +``` + +To reset to inherit from company (set to null): +```json +{ + "contact_email": null +} +``` + +--- + +## Testing Plan + +1. **Create vendor** - Verify contact fields are null (inheriting) +2. **Read vendor** - Verify resolved values come from company +3. **Update vendor contact** - Verify override works +4. **Reset to inherit** - Verify setting null restores inheritance +5. **Update company** - Verify change reflects in inheriting vendors +6. **Update company** - Verify change does NOT affect overridden vendors + +--- + +## 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 diff --git a/models/database/vendor.py b/models/database/vendor.py index adf9c011..2de495e6 100644 --- a/models/database/vendor.py +++ b/models/database/vendor.py @@ -63,6 +63,17 @@ class Vendor(Base, TimestampMixin): Boolean, default=False ) # Boolean to indicate if the vendor brand is verified + # ======================================================================== + # Contact Information (nullable = inherit from company) + # ======================================================================== + # These fields allow vendor-specific branding/identity. + # If null, the value is inherited from the parent company. + contact_email = Column(String(255), nullable=True) # Override company contact email + contact_phone = Column(String(50), nullable=True) # Override company contact phone + website = Column(String(255), nullable=True) # Override company website + business_address = Column(Text, nullable=True) # Override company business address + tax_number = Column(String(100), nullable=True) # Override company tax number + # ======================================================================== # Relationships # ======================================================================== @@ -202,6 +213,66 @@ class Vendor(Base, TimestampMixin): domains.append(domain.domain) # Add other active custom domains return domains + # ======================================================================== + # Contact Resolution Helper Properties + # ======================================================================== + # These properties return the effective value (vendor override or company fallback) + + @property + def effective_contact_email(self) -> str | None: + """Get contact email (vendor override or company fallback).""" + if self.contact_email is not None: + return self.contact_email + return self.company.contact_email if self.company else None + + @property + def effective_contact_phone(self) -> str | None: + """Get contact phone (vendor override or company fallback).""" + if self.contact_phone is not None: + return self.contact_phone + return self.company.contact_phone if self.company else None + + @property + def effective_website(self) -> str | None: + """Get website (vendor override or company fallback).""" + if self.website is not None: + return self.website + return self.company.website if self.company else None + + @property + def effective_business_address(self) -> str | None: + """Get business address (vendor override or company fallback).""" + if self.business_address is not None: + return self.business_address + return self.company.business_address if self.company else None + + @property + def effective_tax_number(self) -> str | None: + """Get tax number (vendor override or company fallback).""" + if self.tax_number is not None: + return self.tax_number + return self.company.tax_number if self.company else None + + def get_contact_info_with_inheritance(self) -> dict: + """ + Get all contact info with inheritance flags. + + Returns dict with resolved values and flags indicating if inherited from company. + """ + company = self.company + return { + "contact_email": self.effective_contact_email, + "contact_email_inherited": self.contact_email is None and company is not None, + "contact_phone": self.effective_contact_phone, + "contact_phone_inherited": self.contact_phone is None and company is not None, + "website": self.effective_website, + "website_inherited": self.website is None and company is not None, + "business_address": self.effective_business_address, + "business_address_inherited": self.business_address is None and company is not None, + "tax_number": self.effective_tax_number, + "tax_number_inherited": self.tax_number is None and company is not None, + } + class VendorUserType(str, enum.Enum): """Types of vendor users.""" diff --git a/models/schema/vendor.py b/models/schema/vendor.py index be35f881..f79a8660 100644 --- a/models/schema/vendor.py +++ b/models/schema/vendor.py @@ -25,7 +25,8 @@ class VendorCreate(BaseModel): """ Schema for creating a new vendor (storefront/brand) under an existing company. - Business contact info is inherited from the parent company. + Contact info is inherited from the parent company by default. + Optionally, provide contact fields to override from the start. """ # Parent company @@ -51,6 +52,13 @@ class VendorCreate(BaseModel): letzshop_csv_url_en: str | None = Field(None, description="English CSV URL") letzshop_csv_url_de: str | None = Field(None, description="German CSV URL") + # Contact Info (optional - if not provided, inherited from company) + contact_email: str | None = Field(None, description="Override company contact email") + contact_phone: str | None = Field(None, description="Override company contact phone") + website: str | None = Field(None, description="Override company website") + business_address: str | None = Field(None, description="Override company business address") + tax_number: str | None = Field(None, description="Override company tax number") + @field_validator("subdomain") @classmethod def validate_subdomain(cls, v): @@ -72,8 +80,8 @@ class VendorUpdate(BaseModel): """ Schema for updating vendor information (Admin only). - Note: Business contact info (contact_email, etc.) is at the Company level. - Use company update endpoints to modify those fields. + Contact fields can be overridden at the vendor level. + Set to null/empty to reset to company default (inherit). """ # Basic Information @@ -90,6 +98,18 @@ class VendorUpdate(BaseModel): is_active: bool | None = None is_verified: bool | None = None + # Contact Info (set value to override, set to empty string to reset to inherit) + contact_email: str | None = Field(None, description="Override company contact email") + contact_phone: str | None = Field(None, description="Override company contact phone") + website: str | None = Field(None, description="Override company website") + business_address: str | None = Field(None, description="Override company business address") + tax_number: str | None = Field(None, description="Override company tax number") + + # Special flag to reset contact fields to inherit from company + reset_contact_to_company: bool | None = Field( + None, description="If true, reset all contact fields to inherit from company" + ) + @field_validator("subdomain") @classmethod def subdomain_lowercase(cls, v): @@ -135,16 +155,14 @@ class VendorResponse(BaseModel): class VendorDetailResponse(VendorResponse): """ - Extended vendor response including company information. + Extended vendor response including company information and resolved contact info. - Includes company details like contact info and owner information. + Contact fields show the effective value (vendor override or company default) + with flags indicating if the value is inherited from the parent company. """ # Company info company_name: str = Field(..., description="Name of the parent company") - company_contact_email: str = Field(..., description="Company business contact email") - company_contact_phone: str | None = Field(None, description="Company phone number") - company_website: str | None = Field(None, description="Company website URL") # Owner info (at company level) owner_email: str = Field( @@ -152,6 +170,25 @@ class VendorDetailResponse(VendorResponse): ) owner_username: str = Field(..., description="Username of the company owner") + # Resolved contact info (vendor override or company default) + contact_email: str | None = Field(None, description="Effective contact email") + contact_phone: str | None = Field(None, description="Effective contact phone") + website: str | None = Field(None, description="Effective website") + business_address: str | None = Field(None, description="Effective business address") + tax_number: str | None = Field(None, description="Effective tax number") + + # Inheritance flags (True = value is inherited from company, not overridden) + contact_email_inherited: bool = Field(False, description="True if contact_email is from company") + contact_phone_inherited: bool = Field(False, description="True if contact_phone is from company") + website_inherited: bool = Field(False, description="True if website is from company") + business_address_inherited: bool = Field(False, description="True if business_address is from company") + tax_number_inherited: bool = Field(False, description="True if tax_number is from company") + + # Original company values (for reference in UI) + company_contact_email: str | None = Field(None, description="Company's contact email") + company_contact_phone: str | None = Field(None, description="Company's phone number") + company_website: str | None = Field(None, description="Company's website URL") + class VendorCreateResponse(VendorDetailResponse): """