Make parcels attached to addresses optional

This commit is contained in:
Eli Ribble 2026-03-05 02:30:12 +00:00
parent 5fa4be483b
commit 78a35e5d1f
No known key found for this signature in database
8 changed files with 84 additions and 51 deletions

View file

@ -99,9 +99,9 @@ var Sites = Table[
ParcelID: column{
Name: "parcel_id",
DBType: "integer",
Default: "",
Default: "NULL",
Comment: "",
Nullable: false,
Nullable: true,
Generated: false,
AutoIncr: false,
},

View file

@ -4483,7 +4483,7 @@ func (f *Factory) FromExistingSite(m *models.Site) *SiteTemplate {
o.OrganizationID = func() int32 { return m.OrganizationID }
o.OwnerName = func() string { return m.OwnerName }
o.OwnerPhoneE164 = func() null.Val[string] { return m.OwnerPhoneE164 }
o.ParcelID = func() int32 { return m.ParcelID }
o.ParcelID = func() null.Val[int32] { return m.ParcelID }
o.ResidentOwned = func() null.Val[bool] { return m.ResidentOwned }
o.Tags = func() pgtypes.HStore { return m.Tags }
o.Version = func() int32 { return m.Version }

View file

@ -71,7 +71,7 @@ func (t ParcelTemplate) setModelRels(o *models.Parcel) {
for _, r := range t.r.Sites {
related := r.o.BuildMany(r.number)
for _, rel := range related {
rel.ParcelID = o.ID // h2
rel.ParcelID = null.From(o.ID) // h2
rel.R.Parcel = o
}
rel = append(rel, related...)

View file

@ -47,7 +47,7 @@ type SiteTemplate struct {
OrganizationID func() int32
OwnerName func() string
OwnerPhoneE164 func() null.Val[string]
ParcelID func() int32
ParcelID func() null.Val[int32]
ResidentOwned func() null.Val[bool]
Tags func() pgtypes.HStore
Version func() int32
@ -169,7 +169,7 @@ func (t SiteTemplate) setModelRels(o *models.Site) {
if t.r.Parcel != nil {
rel := t.r.Parcel.o.Build()
rel.R.Sites = append(rel.R.Sites, o)
o.ParcelID = rel.ID // h2
o.ParcelID = null.From(rel.ID) // h2
o.R.Parcel = rel
}
}
@ -217,7 +217,7 @@ func (o SiteTemplate) BuildSetter() *models.SiteSetter {
}
if o.ParcelID != nil {
val := o.ParcelID()
m.ParcelID = omit.From(val)
m.ParcelID = omitnull.FromNull(val)
}
if o.ResidentOwned != nil {
val := o.ResidentOwned()
@ -336,10 +336,6 @@ func ensureCreatableSite(m *models.SiteSetter) {
val := random_string(nil)
m.OwnerName = omit.From(val)
}
if !(m.ParcelID.IsValue()) {
val := random_int32(nil)
m.ParcelID = omit.From(val)
}
if !(m.Tags.IsValue()) {
val := random_pgtypes_HStore(nil)
m.Tags = omit.From(val)
@ -435,6 +431,25 @@ func (o *SiteTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *
}
isParcelDone, _ := siteRelParcelCtx.Value(ctx)
if !isParcelDone && o.r.Parcel != nil {
ctx = siteRelParcelCtx.WithValue(ctx, true)
if o.r.Parcel.o.alreadyPersisted {
m.R.Parcel = o.r.Parcel.o.Build()
} else {
var rel6 *models.Parcel
rel6, err = o.r.Parcel.o.Create(ctx, exec)
if err != nil {
return err
}
err = m.AttachParcel(ctx, exec, rel6)
if err != nil {
return err
}
}
}
return err
}
@ -479,23 +494,6 @@ func (o *SiteTemplate) Create(ctx context.Context, exec bob.Executor) (*models.S
opt.CreatorID = omit.From(rel4.ID)
if o.r.Parcel == nil {
SiteMods.WithNewParcel().Apply(ctx, o)
}
var rel6 *models.Parcel
if o.r.Parcel.o.alreadyPersisted {
rel6 = o.r.Parcel.o.Build()
} else {
rel6, err = o.r.Parcel.o.Create(ctx, exec)
if err != nil {
return nil, err
}
}
opt.ParcelID = omit.From(rel6.ID)
m, err := models.Sites.Insert(opt).One(ctx, exec)
if err != nil {
return nil, err
@ -503,7 +501,6 @@ func (o *SiteTemplate) Create(ctx context.Context, exec bob.Executor) (*models.S
m.R.Address = rel3
m.R.CreatorUser = rel4
m.R.Parcel = rel6
if err := o.insertOptRels(ctx, exec, m); err != nil {
return nil, err
@ -922,14 +919,14 @@ func (m siteMods) RandomOwnerPhoneE164NotNull(f *faker.Faker) SiteMod {
}
// Set the model columns to this value
func (m siteMods) ParcelID(val int32) SiteMod {
func (m siteMods) ParcelID(val null.Val[int32]) SiteMod {
return SiteModFunc(func(_ context.Context, o *SiteTemplate) {
o.ParcelID = func() int32 { return val }
o.ParcelID = func() null.Val[int32] { return val }
})
}
// Set the Column from the function
func (m siteMods) ParcelIDFunc(f func() int32) SiteMod {
func (m siteMods) ParcelIDFunc(f func() null.Val[int32]) SiteMod {
return SiteModFunc(func(_ context.Context, o *SiteTemplate) {
o.ParcelID = f
})
@ -944,10 +941,32 @@ func (m siteMods) UnsetParcelID() SiteMod {
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is sometimes null
func (m siteMods) RandomParcelID(f *faker.Faker) SiteMod {
return SiteModFunc(func(_ context.Context, o *SiteTemplate) {
o.ParcelID = func() int32 {
return random_int32(f)
o.ParcelID = func() null.Val[int32] {
if f == nil {
f = &defaultFaker
}
val := random_int32(f)
return null.From(val)
}
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is never null
func (m siteMods) RandomParcelIDNotNull(f *faker.Faker) SiteMod {
return SiteModFunc(func(_ context.Context, o *SiteTemplate) {
o.ParcelID = func() null.Val[int32] {
if f == nil {
f = &defaultFaker
}
val := random_int32(f)
return null.From(val)
}
})
}

View file

@ -0,0 +1,4 @@
-- +goose Up
ALTER TABLE site ALTER COLUMN parcel_id DROP NOT NULL;
-- +goose Down
ALTER TABLE site ALTER COLUMN parcel_id ADD NOT NULL;

View file

@ -444,7 +444,7 @@ func (os ParcelSlice) Sites(mods ...bob.Mod[*dialect.SelectQuery]) SitesQuery {
func insertParcelSites0(ctx context.Context, exec bob.Executor, sites1 []*SiteSetter, parcel0 *Parcel) (SiteSlice, error) {
for i := range sites1 {
sites1[i].ParcelID = omit.From(parcel0.ID)
sites1[i].ParcelID = omitnull.From(parcel0.ID)
}
ret, err := Sites.Insert(bob.ToMods(sites1...)).All(ctx, exec)
@ -457,7 +457,7 @@ func insertParcelSites0(ctx context.Context, exec bob.Executor, sites1 []*SiteSe
func attachParcelSites0(ctx context.Context, exec bob.Executor, count int, sites1 SiteSlice, parcel0 *Parcel) (SiteSlice, error) {
setter := &SiteSetter{
ParcelID: omit.From(parcel0.ID),
ParcelID: omitnull.From(parcel0.ID),
}
err := sites1.UpdateAll(ctx, exec, *setter)
@ -628,7 +628,10 @@ func (os ParcelSlice) LoadSites(ctx context.Context, exec bob.Executor, mods ...
for _, rel := range sites {
if !(o.ID == rel.ParcelID) {
if !rel.ParcelID.IsValue() {
continue
}
if !(rel.ParcelID.IsValue() && o.ID == rel.ParcelID.MustGet()) {
continue
}

View file

@ -35,7 +35,7 @@ type Site struct {
OrganizationID int32 `db:"organization_id" `
OwnerName string `db:"owner_name" `
OwnerPhoneE164 null.Val[string] `db:"owner_phone_e164" `
ParcelID int32 `db:"parcel_id" `
ParcelID null.Val[int32] `db:"parcel_id" `
ResidentOwned null.Val[bool] `db:"resident_owned" `
Tags pgtypes.HStore `db:"tags" `
Version int32 `db:"version,pk" `
@ -127,7 +127,7 @@ type SiteSetter struct {
OrganizationID omit.Val[int32] `db:"organization_id" `
OwnerName omit.Val[string] `db:"owner_name" `
OwnerPhoneE164 omitnull.Val[string] `db:"owner_phone_e164" `
ParcelID omit.Val[int32] `db:"parcel_id" `
ParcelID omitnull.Val[int32] `db:"parcel_id" `
ResidentOwned omitnull.Val[bool] `db:"resident_owned" `
Tags omit.Val[pgtypes.HStore] `db:"tags" `
Version omit.Val[int32] `db:"version,pk" `
@ -162,7 +162,7 @@ func (s SiteSetter) SetColumns() []string {
if !s.OwnerPhoneE164.IsUnset() {
vals = append(vals, "owner_phone_e164")
}
if s.ParcelID.IsValue() {
if !s.ParcelID.IsUnset() {
vals = append(vals, "parcel_id")
}
if !s.ResidentOwned.IsUnset() {
@ -205,8 +205,8 @@ func (s SiteSetter) Overwrite(t *Site) {
if !s.OwnerPhoneE164.IsUnset() {
t.OwnerPhoneE164 = s.OwnerPhoneE164.MustGetNull()
}
if s.ParcelID.IsValue() {
t.ParcelID = s.ParcelID.MustGet()
if !s.ParcelID.IsUnset() {
t.ParcelID = s.ParcelID.MustGetNull()
}
if !s.ResidentOwned.IsUnset() {
t.ResidentOwned = s.ResidentOwned.MustGetNull()
@ -280,8 +280,8 @@ func (s *SiteSetter) Apply(q *dialect.InsertQuery) {
vals[8] = psql.Raw("DEFAULT")
}
if s.ParcelID.IsValue() {
vals[9] = psql.Arg(s.ParcelID.MustGet())
if !s.ParcelID.IsUnset() {
vals[9] = psql.Arg(s.ParcelID.MustGetNull())
} else {
vals[9] = psql.Raw("DEFAULT")
}
@ -378,7 +378,7 @@ func (s SiteSetter) Expressions(prefix ...string) []bob.Expression {
}})
}
if s.ParcelID.IsValue() {
if !s.ParcelID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "parcel_id")...),
psql.Arg(s.ParcelID),
@ -806,7 +806,7 @@ func (o *Site) Parcel(mods ...bob.Mod[*dialect.SelectQuery]) ParcelsQuery {
}
func (os SiteSlice) Parcel(mods ...bob.Mod[*dialect.SelectQuery]) ParcelsQuery {
pkParcelID := make(pgtypes.Array[int32], 0, len(os))
pkParcelID := make(pgtypes.Array[null.Val[int32]], 0, len(os))
for _, o := range os {
if o == nil {
continue
@ -1178,7 +1178,7 @@ func (site0 *Site) AttachFile(ctx context.Context, exec bob.Executor, fileupload
func attachSiteParcel0(ctx context.Context, exec bob.Executor, count int, site0 *Site, parcel1 *Parcel) (*Site, error) {
setter := &SiteSetter{
ParcelID: omit.From(parcel1.ID),
ParcelID: omitnull.From(parcel1.ID),
}
err := site0.Update(ctx, exec, setter)
@ -1234,7 +1234,7 @@ type siteWhere[Q psql.Filterable] struct {
OrganizationID psql.WhereMod[Q, int32]
OwnerName psql.WhereMod[Q, string]
OwnerPhoneE164 psql.WhereNullMod[Q, string]
ParcelID psql.WhereMod[Q, int32]
ParcelID psql.WhereNullMod[Q, int32]
ResidentOwned psql.WhereNullMod[Q, bool]
Tags psql.WhereMod[Q, pgtypes.HStore]
Version psql.WhereMod[Q, int32]
@ -1255,7 +1255,7 @@ func buildSiteWhere[Q psql.Filterable](cols siteColumns) siteWhere[Q] {
OrganizationID: psql.Where[Q, int32](cols.OrganizationID),
OwnerName: psql.Where[Q, string](cols.OwnerName),
OwnerPhoneE164: psql.WhereNull[Q, string](cols.OwnerPhoneE164),
ParcelID: psql.Where[Q, int32](cols.ParcelID),
ParcelID: psql.WhereNull[Q, int32](cols.ParcelID),
ResidentOwned: psql.WhereNull[Q, bool](cols.ResidentOwned),
Tags: psql.Where[Q, pgtypes.HStore](cols.Tags),
Version: psql.Where[Q, int32](cols.Version),
@ -1903,8 +1903,11 @@ func (os SiteSlice) LoadParcel(ctx context.Context, exec bob.Executor, mods ...b
}
for _, rel := range parcels {
if !o.ParcelID.IsValue() {
continue
}
if !(o.ParcelID == rel.ID) {
if !(o.ParcelID.IsValue() && o.ParcelID.MustGet() == rel.ID) {
continue
}

View file

@ -79,6 +79,10 @@ func JobCommit(ctx context.Context, file_id int32) error {
if err.Error() != "sql: no rows in result set" {
return fmt.Errorf("query site: %w", err)
}
var parcel_id *int32
if parcel != nil {
parcel_id = &(*parcel).ID
}
setter := models.SiteSetter{
AddressID: omit.From(address.ID),
Created: omit.From(time.Now()),
@ -89,7 +93,7 @@ func JobCommit(ctx context.Context, file_id int32) error {
OrganizationID: omit.From(org.ID),
OwnerName: omit.From(row.PropertyOwnerName),
OwnerPhoneE164: omitnull.FromPtr(row.PropertyOwnerPhoneE164.Ptr()),
ParcelID: omit.From(parcel.ID),
ParcelID: omitnull.FromPtr(parcel_id),
ResidentOwned: omitnull.FromPtr(row.ResidentOwned.Ptr()),
Tags: omit.From(row.Tags),
Version: omit.From(int32(1)),