Coverage for src / idx_api / models / brokerage.py: 100%

63 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2025-12-28 11:16 -0700

1"""Brokerage model - represents the real estate firm/company.""" 

2 

3from datetime import datetime 

4from typing import TYPE_CHECKING, Optional 

5 

6from sqlalchemy import JSON, Boolean, DateTime, Float, Integer, String, Text 

7from sqlalchemy.orm import Mapped, mapped_column, relationship 

8 

9from idx_api.models.base import Base, TimestampMixin 

10 

11if TYPE_CHECKING: 

12 from idx_api.models.agent import Agent 

13 from idx_api.models.api_key import APIKey 

14 from idx_api.models.broker import Broker 

15 from idx_api.models.brokerage_content_source import BrokerageContentSource 

16 from idx_api.models.brokerage_domain import BrokerageDomain 

17 from idx_api.models.brokerage_service_area import BrokerageServiceArea 

18 from idx_api.models.brokerage_vision_settings import BrokerageVisionSettings 

19 from idx_api.models.client import Client 

20 from idx_api.models.lead import Lead 

21 from idx_api.models.tour_request import TourRequest 

22 from idx_api.models.user import User 

23 

24 

25class Brokerage(Base, TimestampMixin): 

26 """Real estate brokerage/firm - represents the company/organization.""" 

27 

28 __tablename__ = "brokerages" 

29 

30 id: Mapped[int] = mapped_column(primary_key=True) 

31 slug: Mapped[str] = mapped_column(String(100), unique=True, index=True, nullable=False) 

32 name: Mapped[str] = mapped_column(String(200), nullable=False) 

33 tagline: Mapped[Optional[str]] = mapped_column(String(500)) 

34 description: Mapped[Optional[str]] = mapped_column(Text) # Longer marketing description 

35 

36 # NOTE: Contact information (email, phone) moved to Broker contact model 

37 

38 # Address information 

39 address_street: Mapped[Optional[str]] = mapped_column(String(200)) 

40 address_city: Mapped[Optional[str]] = mapped_column(String(100)) 

41 address_state: Mapped[Optional[str]] = mapped_column(String(100)) 

42 address_state_abbr: Mapped[Optional[str]] = mapped_column(String(10)) 

43 address_zip: Mapped[Optional[str]] = mapped_column(String(20)) 

44 address_country: Mapped[Optional[str]] = mapped_column(String(10), default="US") 

45 

46 # Map defaults for property search 

47 map_center_lat: Mapped[Optional[float]] = mapped_column(Float) 

48 map_center_lng: Mapped[Optional[float]] = mapped_column(Float) 

49 map_zoom: Mapped[Optional[int]] = mapped_column(Integer) 

50 map_bounds_north: Mapped[Optional[float]] = mapped_column(Float) 

51 map_bounds_south: Mapped[Optional[float]] = mapped_column(Float) 

52 map_bounds_east: Mapped[Optional[float]] = mapped_column(Float) 

53 map_bounds_west: Mapped[Optional[float]] = mapped_column(Float) 

54 

55 # Feature flags - control which features are enabled for this brokerage 

56 feature_semantic_search: Mapped[bool] = mapped_column(Boolean, default=True, server_default='1') 

57 feature_map_search: Mapped[bool] = mapped_column(Boolean, default=True, server_default='1') 

58 feature_favorites: Mapped[bool] = mapped_column(Boolean, default=True, server_default='1') 

59 feature_contact_form: Mapped[bool] = mapped_column(Boolean, default=True, server_default='1') 

60 feature_virtual_tours: Mapped[bool] = mapped_column(Boolean, default=False, server_default='0') 

61 feature_mortgage_calculator: Mapped[bool] = mapped_column(Boolean, default=True, server_default='1') 

62 

63 # License information (firm-level) 

64 license_type: Mapped[Optional[str]] = mapped_column(String(100)) 

65 license_number: Mapped[Optional[str]] = mapped_column(String(100)) 

66 

67 # Branding 

68 logo_url: Mapped[Optional[str]] = mapped_column(String(500)) 

69 primary_color: Mapped[Optional[str]] = mapped_column(String(7)) # Hex color #RRGGBB 

70 

71 # Social media links 

72 social_facebook: Mapped[Optional[str]] = mapped_column(String(500)) 

73 social_twitter: Mapped[Optional[str]] = mapped_column(String(500)) 

74 social_linkedin: Mapped[Optional[str]] = mapped_column(String(500)) 

75 social_pinterest: Mapped[Optional[str]] = mapped_column(String(500)) 

76 social_instagram: Mapped[Optional[str]] = mapped_column(String(500)) 

77 social_youtube: Mapped[Optional[str]] = mapped_column(String(500)) 

78 

79 # Franchise and Specializations 

80 franchise_affiliation: Mapped[Optional[str]] = mapped_column(String(200)) # e.g., "Keller Williams" 

81 va_loans: Mapped[bool] = mapped_column(Boolean, default=False, server_default='0') 

82 military_specialist: Mapped[bool] = mapped_column(Boolean, default=False, server_default='0') 

83 website: Mapped[Optional[str]] = mapped_column(String(500)) # Brokerage website URL 

84 

85 # Hero section customization 

86 hero_headline: Mapped[Optional[str]] = mapped_column(String(500)) 

87 hero_subtitle: Mapped[Optional[str]] = mapped_column(String(1000)) 

88 hero_search_examples: Mapped[Optional[list[str]]] = mapped_column(JSON) 

89 

90 # Navigation (JSON arrays of {label, href, disabled?}) 

91 nav_main: Mapped[Optional[list[dict]]] = mapped_column(JSON) 

92 nav_footer: Mapped[Optional[list[dict]]] = mapped_column(JSON) 

93 

94 # Billing 

95 stripe_customer_id: Mapped[Optional[str]] = mapped_column(String(100)) 

96 

97 # Soft delete 

98 disabled_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) 

99 

100 # Relationships 

101 brokers: Mapped[list["Broker"]] = relationship(back_populates="brokerage", cascade="all, delete-orphan") 

102 agents: Mapped[list["Agent"]] = relationship(back_populates="brokerage") 

103 users: Mapped[list["User"]] = relationship(back_populates="brokerage") 

104 api_keys: Mapped[list["APIKey"]] = relationship(back_populates="brokerage") 

105 tour_requests: Mapped[list["TourRequest"]] = relationship( 

106 back_populates="assigned_brokerage", foreign_keys="TourRequest.assigned_brokerage_id" 

107 ) 

108 domains: Mapped[list["BrokerageDomain"]] = relationship(back_populates="brokerage", cascade="all, delete-orphan") 

109 service_areas: Mapped[list["BrokerageServiceArea"]] = relationship( 

110 back_populates="brokerage", cascade="all, delete-orphan", order_by="BrokerageServiceArea.display_order" 

111 ) 

112 vision_settings: Mapped[Optional["BrokerageVisionSettings"]] = relationship( 

113 back_populates="brokerage", cascade="all, delete-orphan", uselist=False 

114 ) 

115 content_source: Mapped[Optional["BrokerageContentSource"]] = relationship( 

116 back_populates="brokerage", cascade="all, delete-orphan", uselist=False 

117 ) 

118 clients: Mapped[list["Client"]] = relationship(back_populates="brokerage", cascade="all, delete-orphan") 

119 leads: Mapped[list["Lead"]] = relationship(back_populates="brokerage", cascade="all, delete-orphan") 

120 

121 def __repr__(self) -> str: 

122 return f"<Brokerage(id={self.id}, slug='{self.slug}', name='{self.name}')>"