feat(vendor): add contact info inheritance from company
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user