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.
122 lines
5.5 KiB
Go
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"
|
|
}
|