updates on the api endpoints on the customer page and more

This commit is contained in:
2026-09-02 16:32:53 +05:30
parent 8e2c484bcb
commit d12629a1e4
31 changed files with 3541 additions and 121 deletions

View File

@@ -0,0 +1,104 @@
//go:build ignore
// Read-only: does the console's page-by-page drain actually see every booking?
//
// GetAdminBookings runs Offset/Limit with NO ORDER BY. In Postgres that makes
// the row order across pages unspecified, so a paged drain can legally return
// the same row twice and never return another. This replays the exact 6 pages
// the console fetches and compares the union against the table.
//
// go run scratch/check_booking_paging.go
package main
import (
"fmt"
"log"
"sort"
"doormile/config"
"doormile/db"
"github.com/joho/godotenv"
)
const pageSize = 100 // what the server actually returns: min(100, requested)
type row struct{ Bookingid int }
func main() {
_ = godotenv.Load()
cfg := config.Load()
db.Connect(cfg)
if db.DB == nil {
log.Fatal("no DB connection")
}
var total int64
db.DB.Raw(`SELECT count(*) FROM pickupbookings`).Scan(&total)
pages := int((total + pageSize - 1) / pageSize)
fmt.Printf("bookings: %d, pages of %d: %d\n\n", total, pageSize, pages)
// The truth: every id that exists.
var all []row
db.DB.Raw(`SELECT bookingid FROM pickupbookings`).Scan(&all)
truth := map[int]bool{}
for _, r := range all {
truth[r.Bookingid] = true
}
// The drain, exactly as the console runs it — unordered Offset/Limit.
seen := map[int]int{}
for p := 0; p < pages; p++ {
var page []row
db.DB.Raw(`SELECT bookingid FROM pickupbookings LIMIT ? OFFSET ?`, pageSize, p*pageSize).Scan(&page)
for _, r := range page {
seen[r.Bookingid]++
}
fmt.Printf(" page %d: %d rows\n", p+1, len(page))
}
missed := []int{}
for id := range truth {
if seen[id] == 0 {
missed = append(missed, id)
}
}
dupes := []int{}
for id, n := range seen {
if n > 1 {
dupes = append(dupes, id)
}
}
sort.Sort(sort.Reverse(sort.IntSlice(missed)))
sort.Sort(sort.Reverse(sort.IntSlice(dupes)))
fmt.Printf("\nunordered drain saw %d distinct of %d\n", len(seen), len(truth))
fmt.Printf(" MISSED %d: %v\n", len(missed), head(missed, 20))
fmt.Printf(" DUPLICATED %d: %v\n", len(dupes), head(dupes, 20))
// The same drain with a deterministic order — the proposed fix.
seenOrdered := map[int]int{}
for p := 0; p < pages; p++ {
var page []row
db.DB.Raw(`SELECT bookingid FROM pickupbookings ORDER BY bookingid DESC LIMIT ? OFFSET ?`,
pageSize, p*pageSize).Scan(&page)
for _, r := range page {
seenOrdered[r.Bookingid]++
}
}
missedOrdered := 0
for id := range truth {
if seenOrdered[id] == 0 {
missedOrdered++
}
}
fmt.Printf("\nwith ORDER BY bookingid DESC: saw %d distinct, missed %d\n",
len(seenOrdered), missedOrdered)
}
func head(xs []int, n int) []int {
if len(xs) > n {
return xs[:n]
}
return xs
}

View File

@@ -0,0 +1,71 @@
//go:build ignore
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")
}
fmt.Println("=== unique constraints on bookingno / trackingno ===")
type c struct{ Conname, Def string }
var cs []c
db.DB.Raw(`SELECT c.conname, pg_get_constraintdef(c.oid) def
FROM pg_constraint c JOIN pg_class t ON t.oid=c.conrelid
WHERE t.relname IN ('pickupbookings','consignments') AND c.contype='u'`).Scan(&cs)
if len(cs) == 0 {
fmt.Println(" NONE — nothing stops a duplicate at the database level")
}
for _, x := range cs {
fmt.Printf(" %s: %s\n", x.Conname, x.Def)
}
fmt.Println("\n=== duplicate booking numbers ===")
type d struct {
Bookingno string
N int64
}
var ds []d
db.DB.Raw(`SELECT bookingno, count(*) n FROM pickupbookings
GROUP BY 1 HAVING count(*)>1 ORDER BY n DESC LIMIT 10`).Scan(&ds)
fmt.Printf(" %d duplicated\n", len(ds))
for _, x := range ds {
fmt.Printf(" %s x%d\n", x.Bookingno, x.N)
}
fmt.Println("\n=== all-zero random part (would mean rand.Read failed) ===")
var zeros int64
db.DB.Raw(`SELECT count(*) FROM pickupbookings WHERE bookingno LIKE 'DM-BK-00000000-%'`).Scan(&zeros)
fmt.Printf(" %d\n", zeros)
fmt.Println("\n=== how many bookings share a timestamp suffix ===")
type s struct {
Suffix string
N int64
}
var ss []s
db.DB.Raw(`SELECT split_part(bookingno,'-',4) suffix, count(*) n
FROM pickupbookings GROUP BY 1 ORDER BY n DESC LIMIT 5`).Scan(&ss)
for _, x := range ss {
fmt.Printf(" suffix %-8s %d bookings\n", x.Suffix, x.N)
}
fmt.Println("\n=== duplicate tracking numbers ===")
var td []d
db.DB.Raw(`SELECT trackingno bookingno, count(*) n FROM consignments
GROUP BY 1 HAVING count(*)>1 LIMIT 5`).Scan(&td)
fmt.Printf(" %d duplicated\n", len(td))
}

View File

@@ -0,0 +1,115 @@
//go:build ignore
// Read-only: why a customer-app booking may not reach the console.
//
// go run scratch/check_customer_bookings.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. How many bookings exist, by source ===")
type srcRow struct {
Bookingsource string
N int64
}
var srcs []srcRow
db.DB.Raw(`SELECT COALESCE(NULLIF(bookingsource,''),'(blank)') AS bookingsource, count(*) AS n
FROM pickupbookings GROUP BY 1 ORDER BY n DESC`).Scan(&srcs)
var total int64
for _, s := range srcs {
fmt.Printf(" %-16s %d\n", s.Bookingsource, s.N)
total += s.N
}
fmt.Printf(" %-16s %d\n", "TOTAL", total)
fmt.Println("\n=== 2. The 10 newest bookings ===")
type bRow struct {
Bookingid int
Bookingno string
Bookingsource string
Status string
Tenantid *int
Pickuplatitude float64
Pickuppincode string
Createdat string
}
var newest []bRow
db.DB.Raw(`SELECT bookingid, bookingno, bookingsource, status, tenantid,
pickuplatitude, pickuppincode, createdat::text
FROM pickupbookings ORDER BY bookingid DESC LIMIT 10`).Scan(&newest)
for _, b := range newest {
tenant := "NULL"
if b.Tenantid != nil {
tenant = fmt.Sprintf("%d", *b.Tenantid)
}
fmt.Printf(" #%-6d %-14s src=%-14s status=%-24s tenant=%-5s lat=%-10.4f pin=%-7s %s\n",
b.Bookingid, b.Bookingno, b.Bookingsource, b.Status, tenant,
b.Pickuplatitude, b.Pickuppincode, b.Createdat)
}
fmt.Println("\n=== 3. Customer-app bookings specifically ===")
var appBookings []bRow
db.DB.Raw(`SELECT bookingid, bookingno, bookingsource, status, tenantid,
pickuplatitude, pickuppincode, createdat::text
FROM pickupbookings WHERE bookingsource = 'Customer_App'
ORDER BY bookingid DESC LIMIT 10`).Scan(&appBookings)
if len(appBookings) == 0 {
fmt.Println(" none at all")
}
for _, b := range appBookings {
fmt.Printf(" #%-6d %-14s status=%-24s lat=%-10.4f pin=%-7s %s\n",
b.Bookingid, b.Bookingno, b.Status, b.Pickuplatitude, b.Pickuppincode, b.Createdat)
}
fmt.Println("\n=== 4. Customer-app bookings with no pickup coordinates ===")
var noCoords int64
db.DB.Raw(`SELECT count(*) FROM pickupbookings
WHERE bookingsource = 'Customer_App'
AND (pickuplatitude = 0 OR pickuplongitude = 0
OR pickuplatitude IS NULL OR pickuplongitude IS NULL)`).Scan(&noCoords)
fmt.Printf(" %d (these cannot be assigned a rider and fail the console's zone filter)\n", noCoords)
fmt.Println("\n=== 5. What one page of the console's own query returns ===")
// The console drains GET /admin/bookings, which runs no ORDER BY. This is
// the same shape: LIMIT/OFFSET with no ordering.
var page1, page1again []idRow
db.DB.Raw(`SELECT bookingid FROM pickupbookings LIMIT 5 OFFSET 0`).Scan(&page1)
db.DB.Raw(`SELECT bookingid FROM pickupbookings LIMIT 5 OFFSET 0`).Scan(&page1again)
fmt.Printf(" unordered page 1, call A: %v\n", ids(page1))
fmt.Printf(" unordered page 1, call B: %v\n", ids(page1again))
var lastPage []idRow
offset := total - 5
if offset < 0 {
offset = 0
}
db.DB.Raw(fmt.Sprintf(`SELECT bookingid FROM pickupbookings LIMIT 5 OFFSET %d`, offset)).Scan(&lastPage)
fmt.Printf(" unordered LAST page: %v\n", ids(lastPage))
fmt.Println(" (if the newest ids appear only on the last page, a truncated drain never sees them)")
}
type idRow struct{ Bookingid int }
func ids(rows []idRow) []int {
out := make([]int, 0, len(rows))
for _, r := range rows {
out = append(out, r.Bookingid)
}
return out
}

View File

@@ -0,0 +1,136 @@
//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)
}

62
scratch/check_latest.go Normal file
View File

@@ -0,0 +1,62 @@
//go:build ignore
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")
}
type b struct {
Bookingid int
Bookingno string
Bookingsource string
Status string
Tenantid *int
Appcustomerid int
Pickuppincode string
Pickuplatitude float64
Createdat string
}
var rows []b
db.DB.Raw(`SELECT bookingid,bookingno,bookingsource,status,tenantid,appcustomerid,
pickuppincode,pickuplatitude,createdat::text
FROM pickupbookings ORDER BY bookingid DESC LIMIT 12`).Scan(&rows)
fmt.Println("=== 12 newest bookings (any source) ===")
for _, r := range rows {
tn := "NULL"
if r.Tenantid != nil {
tn = fmt.Sprint(*r.Tenantid)
}
fmt.Printf(" #%-5d src=%-13q status=%-24s tenant=%-5s cust=%-4d pin=%-7s lat=%.4f %s\n",
r.Bookingid, r.Bookingsource, r.Status, tn, r.Appcustomerid,
r.Pickuppincode, r.Pickuplatitude, r.Createdat)
}
fmt.Println("\n=== exact distinct bookingsource values (byte-for-byte) ===")
type s struct {
V string
N int64
}
var ss []s
db.DB.Raw(`SELECT '['||bookingsource||']' v, count(*) n FROM pickupbookings GROUP BY 1 ORDER BY n DESC`).Scan(&ss)
for _, x := range ss {
fmt.Printf(" %-20s %d\n", x.V, x.N)
}
var appTotal int64
db.DB.Raw(`SELECT count(*) FROM pickupbookings WHERE bookingsource='Customer_App'`).Scan(&appTotal)
fmt.Printf("\nCustomer_App bookings the console page should list: %d\n", appTotal)
}

37
scratch/check_miler23.go Normal file
View File

@@ -0,0 +1,37 @@
//go:build ignore
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")
}
var avail string
db.DB.Raw(`SELECT availabilitystatus FROM milerprofiles WHERE userid = 23`).Scan(&avail)
fmt.Printf("miler 23 availability: %s\n", avail)
var open int64
db.DB.Raw(`SELECT count(*) FROM bookingassignments ba
JOIN pickupbookings b ON b.bookingid = ba.bookingid
WHERE ba.mileruserid = 23 AND ba.assignmentstatus IN ('Assigned','Accepted')`).Scan(&open)
fmt.Printf("miler 23 open assignments: %d\n", open)
var onCancelled int64
db.DB.Raw(`SELECT count(*) FROM bookingassignments ba
JOIN pickupbookings b ON b.bookingid = ba.bookingid
WHERE ba.assignmentstatus IN ('Assigned','Accepted') AND b.status = 'Cancelled'`).Scan(&onCancelled)
fmt.Printf("\nACROSS ALL RIDERS — open assignments on a CANCELLED booking: %d\n", onCancelled)
}

38
scratch/check_phone.go Normal file
View File

@@ -0,0 +1,38 @@
//go:build ignore
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")
}
type c struct {
Appcustomerid int
Firstname string
Phone string
Configid int
Status string
}
var rows []c
db.DB.Raw(`SELECT appcustomerid,firstname,phone,configid,status FROM appcustomers
WHERE phone IN ('9876543210') OR appcustomerid = 5`).Scan(&rows)
if len(rows) == 0 {
fmt.Println("no appcustomer with phone 9876543210, and no id 5")
}
for _, r := range rows {
fmt.Printf(" id=%d name=%q phone=%s configid=%d status=%s\n",
r.Appcustomerid, r.Firstname, r.Phone, r.Configid, r.Status)
}
}

View File

@@ -0,0 +1,80 @@
//go:build ignore
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")
}
type b struct {
Bookingid int
Bookingno string
Status string
Createdat string
Updatedat string
Assignedmileruserid *int
Pickuplatitude float64
Pickuplongitude float64
Pickuppincode string
Deliverypincode string
Deliveryaddress string
Appcustomerid int
}
var rows []b
db.DB.Raw(`SELECT bookingid, bookingno, status, createdat::text, updatedat::text,
assignedmileruserid, pickuplatitude, pickuplongitude, pickuppincode,
deliverypincode, deliveryaddress, appcustomerid
FROM pickupbookings WHERE bookingid IN (546,547,179) ORDER BY bookingid DESC`).Scan(&rows)
for _, r := range rows {
miler := "none"
if r.Assignedmileruserid != nil {
miler = fmt.Sprintf("%d", *r.Assignedmileruserid)
}
fmt.Printf("#%d %s\n status=%s created=%s\n updated=%s miler=%s customer=%d\n pickup=(%.4f,%.4f) %s -> %s drop=%q\n\n",
r.Bookingid, r.Bookingno, r.Status, r.Createdat, r.Updatedat, miler,
r.Appcustomerid, r.Pickuplatitude, r.Pickuplongitude, r.Pickuppincode,
r.Deliverypincode, r.Deliveryaddress)
}
fmt.Println("=== assignments on those bookings ===")
type a struct {
Bookingid int
Assignmentstatus string
Assignedat string
Remarks string
}
var as []a
db.DB.Raw(`SELECT bookingid, assignmentstatus, assignedat::text, remarks
FROM bookingassignments WHERE bookingid IN (546,547,179)`).Scan(&as)
if len(as) == 0 {
fmt.Println(" none — no rider was ever assigned")
}
for _, x := range as {
fmt.Printf(" booking %d: %s at %s %q\n", x.Bookingid, x.Assignmentstatus, x.Assignedat, x.Remarks)
}
fmt.Println("\n=== how many bookings sit in each status ===")
type s struct {
Status string
N int64
}
var ss []s
db.DB.Raw(`SELECT status, count(*) n FROM pickupbookings GROUP BY 1 ORDER BY n DESC`).Scan(&ss)
for _, x := range ss {
fmt.Printf(" %-26s %d\n", x.Status, x.N)
}
}