//go:build ignore // Read-only verification for the base-handover work. Reads information_schema // and pg_constraint only — no writes, no DDL, no migrations. // // go run scratch/check_handover_schema.go package main import ( "fmt" "log" "doormile/config" "doormile/db" "github.com/joho/godotenv" ) func main() { _ = godotenv.Load() cfg := config.Load() db.Connect(cfg) if db.DB == nil { log.Fatal("no DB connection") } fmt.Println("=== 1. New columns (expected ABSENT until AutoMigrate runs) ===") type col struct { Table string Name string } for _, want := range []col{ {"pickupbookings", "pickupsourcetype"}, {"pickupbookings", "pickuphubid"}, {"consignments", "inwardedat"}, } { var n int64 db.DB.Raw(`SELECT count(*) FROM information_schema.columns WHERE table_name = ? AND column_name = ?`, want.Table, want.Name).Scan(&n) fmt.Printf(" %-16s %-18s present=%v\n", want.Table, want.Name, n > 0) } fmt.Println("\n=== 2. CHECK constraints on the tables we write ===") type chk struct { Conname string Def string } var checks []chk db.DB.Raw(`SELECT c.conname, pg_get_constraintdef(c.oid) AS def FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid WHERE c.contype = 'c' AND t.relname IN ('consignments','consignmentexceptions','consignmenthistory', 'pickupbookings','bookingassignments','milerprofiles') ORDER BY t.relname, c.conname`).Scan(&checks) for _, c := range checks { fmt.Printf(" %s\n %s\n", c.Conname, c.Def) } fmt.Println("\n=== 3. Foreign keys on the user/id columns we write ===") type fk struct { Table string Conname string Def string } var fks []fk db.DB.Raw(`SELECT t.relname AS table, c.conname, pg_get_constraintdef(c.oid) AS def FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid WHERE c.contype = 'f' AND t.relname IN ('consignmentexceptions','consignmenthistory','consignments', 'tripsheets','bookingassignments','pickupbookings') ORDER BY t.relname, c.conname`).Scan(&fks) if len(fks) == 0 { fmt.Println(" (none)") } for _, f := range fks { fmt.Printf(" %-24s %s\n", f.Table, f.Def) } fmt.Println("\n=== 4. NOT NULL columns on the tables we insert into ===") type nn struct { Table string Column string Def *string } var nns []nn db.DB.Raw(`SELECT table_name AS table, column_name AS column, column_default AS def FROM information_schema.columns WHERE table_name IN ('consignmentexceptions','consignmenthistory') AND is_nullable = 'NO' ORDER BY table_name, ordinal_position`).Scan(&nns) for _, c := range nns { d := "(no default)" if c.Def != nil { d = *c.Def } fmt.Printf(" %-24s %-20s %s\n", c.Table, c.Column, d) } fmt.Println("\n=== 5. Hub master data completeness (request 29) ===") type hubRow struct { Total int64 NoAddress int64 NoPincode int64 NoCoords int64 ActiveTotal int64 } var h hubRow db.DB.Raw(`SELECT count(*) AS total, count(*) FILTER (WHERE address IS NULL OR address = '') AS no_address, count(*) FILTER (WHERE pincode IS NULL OR pincode = '') AS no_pincode, count(*) FILTER (WHERE latitude IS NULL OR latitude = 0 OR longitude IS NULL OR longitude = 0) AS no_coords, count(*) FILTER (WHERE status = 'Active') AS active_total FROM hubs WHERE deletedat IS NULL`).Scan(&h) fmt.Printf(" hubs=%d active=%d missing_address=%d missing_pincode=%d missing_coords=%d\n", h.Total, h.ActiveTotal, h.NoAddress, h.NoPincode, h.NoCoords) fmt.Println("\n=== 6. Consignment status distribution (what is live now) ===") type sc struct { Status string N int64 } var scs []sc db.DB.Raw(`SELECT status, count(*) AS n FROM consignments WHERE deletedat IS NULL GROUP BY status ORDER BY n DESC`).Scan(&scs) for _, s := range scs { fmt.Printf(" %-22s %d\n", s.Status, s.N) } fmt.Println("\n=== 7. Open assignments on already-converted bookings (the off-duty bug) ===") var stuck int64 db.DB.Raw(`SELECT count(*) FROM bookingassignments a JOIN pickupbookings b ON b.bookingid = a.bookingid WHERE a.assignmentstatus IN ('Assigned','Accepted') AND b.status = 'Converted_To_Consignment'`).Scan(&stuck) fmt.Printf(" assignments still open on a converted booking: %d\n", stuck) }