Files
honeyDueAPI/internal/models/residence.go
Trey T cb7080c460 Smart onboarding: residence home profile + suggestion engine
14 new optional residence fields (heating, cooling, water heater, roof,
pool, sprinkler, septic, fireplace, garage, basement, attic, exterior,
flooring, landscaping) with JSONB conditions on templates.

Suggestion engine scores templates against home profile: string match
+0.25, bool +0.3, property type +0.15, universal base 0.3. Graceful
degradation from minimal to full profile info.

GET /api/tasks/suggestions/?residence_id=X returns ranked templates.
54 template conditions across 44 templates in seed data.
8 suggestion service tests.
2026-03-30 09:02:03 -05:00

122 lines
5.5 KiB
Go

package models
import (
"time"
"github.com/shopspring/decimal"
)
// ResidenceType represents the residence_residencetype table
type ResidenceType struct {
BaseModel
Name string `gorm:"column:name;size:20;not null" json:"name"`
}
// TableName returns the table name for GORM
func (ResidenceType) TableName() string {
return "residence_residencetype"
}
// Residence represents the residence_residence table
type Residence struct {
BaseModel
OwnerID uint `gorm:"column:owner_id;index;not null" json:"owner_id"`
Owner User `gorm:"foreignKey:OwnerID" json:"owner,omitempty"`
Users []User `gorm:"many2many:residence_residence_users;" json:"users,omitempty"`
Name string `gorm:"column:name;size:200;not null" json:"name"`
PropertyTypeID *uint `gorm:"column:property_type_id" json:"property_type_id"`
PropertyType *ResidenceType `gorm:"foreignKey:PropertyTypeID" json:"property_type,omitempty"`
// Address
StreetAddress string `gorm:"column:street_address;size:255" json:"street_address"`
ApartmentUnit string `gorm:"column:apartment_unit;size:50" json:"apartment_unit"`
City string `gorm:"column:city;size:100" json:"city"`
StateProvince string `gorm:"column:state_province;size:100" json:"state_province"`
PostalCode string `gorm:"column:postal_code;size:20" json:"postal_code"`
Country string `gorm:"column:country;size:100;default:'USA'" json:"country"`
// Property Details
Bedrooms *int `gorm:"column:bedrooms" json:"bedrooms"`
Bathrooms *decimal.Decimal `gorm:"column:bathrooms;type:decimal(3,1)" json:"bathrooms"`
SquareFootage *int `gorm:"column:square_footage" json:"square_footage"`
LotSize *decimal.Decimal `gorm:"column:lot_size;type:decimal(10,2)" json:"lot_size"`
YearBuilt *int `gorm:"column:year_built" json:"year_built"`
Description string `gorm:"column:description;type:text" json:"description"`
PurchaseDate *time.Time `gorm:"column:purchase_date;type:date" json:"purchase_date"`
PurchasePrice *decimal.Decimal `gorm:"column:purchase_price;type:decimal(12,2)" json:"purchase_price"`
// Home Profile (for smart onboarding suggestions)
HeatingType *string `gorm:"column:heating_type;size:50" json:"heating_type"`
CoolingType *string `gorm:"column:cooling_type;size:50" json:"cooling_type"`
WaterHeaterType *string `gorm:"column:water_heater_type;size:50" json:"water_heater_type"`
RoofType *string `gorm:"column:roof_type;size:50" json:"roof_type"`
HasPool bool `gorm:"column:has_pool;default:false" json:"has_pool"`
HasSprinklerSystem bool `gorm:"column:has_sprinkler_system;default:false" json:"has_sprinkler_system"`
HasSeptic bool `gorm:"column:has_septic;default:false" json:"has_septic"`
HasFireplace bool `gorm:"column:has_fireplace;default:false" json:"has_fireplace"`
HasGarage bool `gorm:"column:has_garage;default:false" json:"has_garage"`
HasBasement bool `gorm:"column:has_basement;default:false" json:"has_basement"`
HasAttic bool `gorm:"column:has_attic;default:false" json:"has_attic"`
ExteriorType *string `gorm:"column:exterior_type;size:50" json:"exterior_type"`
FlooringPrimary *string `gorm:"column:flooring_primary;size:50" json:"flooring_primary"`
LandscapingType *string `gorm:"column:landscaping_type;size:50" json:"landscaping_type"`
IsPrimary bool `gorm:"column:is_primary;default:true" json:"is_primary"`
IsActive bool `gorm:"column:is_active;default:true;index" json:"is_active"` // Soft delete flag
// Relations (to be implemented in Phase 3)
// Tasks []Task `gorm:"foreignKey:ResidenceID" json:"tasks,omitempty"`
// Documents []Document `gorm:"foreignKey:ResidenceID" json:"documents,omitempty"`
// ShareCodes []ResidenceShareCode `gorm:"foreignKey:ResidenceID" json:"-"`
}
// TableName returns the table name for GORM
func (Residence) TableName() string {
return "residence_residence"
}
// GetAllUsers returns all users with access to this residence (owner + shared users)
func (r *Residence) GetAllUsers() []User {
users := make([]User, 0, len(r.Users)+1)
users = append(users, r.Owner)
users = append(users, r.Users...)
return users
}
// HasAccess checks if a user has access to this residence
func (r *Residence) HasAccess(userID uint) bool {
if r.OwnerID == userID {
return true
}
for _, u := range r.Users {
if u.ID == userID {
return true
}
}
return false
}
// IsPrimaryOwner checks if a user is the primary owner
func (r *Residence) IsPrimaryOwner(userID uint) bool {
return r.OwnerID == userID
}
// ResidenceShareCode represents the residence_residencesharecode table
type ResidenceShareCode struct {
BaseModel
ResidenceID uint `gorm:"column:residence_id;index;not null" json:"residence_id"`
Residence Residence `gorm:"foreignKey:ResidenceID" json:"-"`
Code string `gorm:"column:code;uniqueIndex;size:6;not null" json:"code"`
CreatedByID uint `gorm:"column:created_by_id;not null" json:"created_by_id"`
CreatedBy User `gorm:"foreignKey:CreatedByID" json:"-"`
IsActive bool `gorm:"column:is_active;default:true;index" json:"is_active"`
ExpiresAt *time.Time `gorm:"column:expires_at" json:"expires_at"`
}
// TableName returns the table name for GORM
func (ResidenceShareCode) TableName() string {
return "residence_residencesharecode"
}