// Who can actually open a till, across the whole platform. // // Read-only. Answers the question the account model raises the moment sign-in // becomes real: the endpoint is open to every tenant, so *which* of them can // genuinely reach it, and does anyone reach it who should not. package main import ( "fmt" "log" "os" "github.com/joho/godotenv" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) func main() { _ = godotenv.Load() dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME")) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}) if err != nil { log.Fatal(err) } // The exact predicate posLoginCandidates + PosLogin apply. eligible := ` FROM app_users a WHERE LOWER(COALESCE(a.status,'active')) <> 'inactive' AND COALESCE(a.password,'') <> '' AND COALESCE(a.authname,'') <> '' AND COALESCE(a.tenantid,0) > 0 AND EXISTS (SELECT 1 FROM tenantlocations l WHERE l.tenantid = a.tenantid AND LOWER(COALESCE(l.status,'active')) <> 'inactive' AND (COALESCE(a.locationid,0) = 0 OR l.locationid = a.locationid))` var total, tenants int db.Raw(`SELECT COUNT(*) ` + eligible).Scan(&total) db.Raw(`SELECT COUNT(DISTINCT a.tenantid) ` + eligible).Scan(&tenants) var allUsers, allTenants int db.Raw(`SELECT COUNT(*) FROM app_users`).Scan(&allUsers) db.Raw(`SELECT COUNT(*) FROM tenants`).Scan(&allTenants) fmt.Printf("app_users rows %d\n", allUsers) fmt.Printf(" can open a till %d\n", total) fmt.Printf("tenants %d\n", allTenants) fmt.Printf(" with a usable login %d\n\n", tenants) // Which tenants, and whether they have a catalogue to sell. var rows []struct { Tenantid int Tenantname string Users int Outlets int Products int } db.Raw(` SELECT t.tenantid, COALESCE(t.tenantname,'') AS tenantname, (SELECT COUNT(*) FROM app_users a WHERE a.tenantid=t.tenantid AND LOWER(COALESCE(a.status,'active'))<>'inactive' AND COALESCE(a.password,'')<>'' AND COALESCE(a.authname,'')<>'') AS users, (SELECT COUNT(*) FROM tenantlocations l WHERE l.tenantid=t.tenantid AND LOWER(COALESCE(l.status,'active'))<>'inactive') AS outlets, (SELECT COUNT(*) FROM productlocations p WHERE p.tenantid=t.tenantid) AS products FROM tenants t WHERE EXISTS (SELECT 1 FROM app_users a WHERE a.tenantid=t.tenantid AND LOWER(COALESCE(a.status,'active'))<>'inactive' AND COALESCE(a.password,'')<>'' AND COALESCE(a.authname,'')<>'') ORDER BY products DESC, t.tenantid`).Scan(&rows) fmt.Printf("%-8s %-34s %6s %8s %9s\n", "tenant", "name", "logins", "outlets", "products") fmt.Println("---------------------------------------------------------------------------") for _, r := range rows { fmt.Printf("%-8d %-34s %6d %8d %9d\n", r.Tenantid, r.Tenantname, r.Users, r.Outlets, r.Products) } // Roles. The POS login does not check one, so this says who slips through. var roles []struct { Roleid int C int } db.Raw(`SELECT COALESCE(a.roleid,0) AS roleid, COUNT(*) AS c ` + eligible + ` GROUP BY 1 ORDER BY c DESC`).Scan(&roles) fmt.Println("\neligible logins by roleid:") for _, r := range roles { fmt.Printf(" role %-4d %d\n", r.Roleid, r.C) } }