//go:build ignore // seed_test_data.go — fills the live DB with the reference + test data the // current flows need, so Dharaneesh's customer/miler app builds have something // real to work against. // // It seeds: (1) reference states/districts + pricing, (2) the base/hub network // and tenant locations, (3) TEST MILERS WITH NO PIN so the first-login set-PIN // flow can be exercised, and (4) a sample customer plus a multi-destination // customer-app booking with two consignments in the rider's hands (ready to // test the base-handover fix). // // It is IDEMPOTENT and NON-DESTRUCTIVE: every block is create-if-absent by a // natural key. It never TRUNCATEs, never overwrites a curated live row, and — // critically — never resets a test miler's Password once set, so re-running it // after a rider has chosen their PIN does not wipe it. // // Run: go run scratch/seed_test_data.go (needs .env pointing at the target DB) package main import ( "fmt" "log" "doormile/config" "doormile/db" "doormile/migrations" "doormile/models" "doormile/utils" "github.com/joho/godotenv" ) func main() { _ = godotenv.Load() cfg := config.Load() db.Connect(cfg) if db.DB == nil { log.Fatal("DB connection is nil") } if err := migrations.Migrate(db.DB); err != nil { log.Fatalf("migration failed: %v", err) } now := utils.DBNow() // ───────────────────────────────────────────────────────────────────────── // 1. REFERENCE DATA — serviceable states + districts (customer app reads // these), plus a little pricing. Create-if-absent so curated live rows // are never overwritten. // ───────────────────────────────────────────────────────────────────────── states := []models.ServiceableState{ {Statecode: "TN", Statename: "Tamil Nadu", Transittag: "Ultra-fast transit", Displayorder: 1, Status: "Active"}, {Statecode: "KA", Statename: "Karnataka", Transittag: "Next-day transit", Displayorder: 2, Status: "Active"}, {Statecode: "KL", Statename: "Kerala", Transittag: "2-day transit", Displayorder: 3, Status: "Active"}, } for _, s := range states { var ex models.ServiceableState if db.DB.Where("statecode = ?", s.Statecode).First(&ex).Error != nil { if err := db.DB.Create(&s).Error; err != nil { log.Fatalf("seed state %s: %v", s.Statecode, err) } } } fmt.Println("✔ reference states") // Ensure the two anchor bases exist first, so districts can point at them. baseSeeds := []models.Hub{ {Hubname: "Coimbatore Jupiter Hub", Hubtype: "sorting_center", Applocationid: 1, Contactno: "9876543210", Address: "Gandhipuram, Coimbatore", Latitude: 11.0168, Longitude: 76.9558, Pincode: "641012", Status: "Active"}, {Hubname: "Coimbatore Neptune Hub", Hubtype: "delivery_hub", Applocationid: 1, Contactno: "9876543210", Address: "Peelamedu, Coimbatore", Latitude: 11.0301, Longitude: 77.0034, Pincode: "641004", Status: "Active"}, {Hubname: "Chennai Comet Hub", Hubtype: "delivery_hub", Applocationid: 1, Contactno: "9876543210", Address: "Guindy, Chennai", Latitude: 13.0067, Longitude: 80.2206, Pincode: "600032", Status: "Active"}, {Hubname: "Bangalore Earth Hub", Hubtype: "sorting_center", Applocationid: 3, Contactno: "9876543210", Address: "Koramangala, Bangalore", Latitude: 12.9352, Longitude: 77.6244, Pincode: "560034", Status: "Active"}, } for _, h := range baseSeeds { var ex models.Hub if db.DB.Where("hubname = ?", h.Hubname).First(&ex).Error != nil { if err := db.DB.Create(&h).Error; err != nil { log.Fatalf("seed hub %s: %v", h.Hubname, err) } } } fmt.Println("✔ base / hub network") hubID := func(name string) *int { var h models.Hub if db.DB.Where("hubname = ?", name).First(&h).Error != nil { return nil } return &h.Hubid } cbeHub := hubID("Coimbatore Jupiter Hub") chnHub := hubID("Chennai Comet Hub") blrHub := hubID("Bangalore Earth Hub") districts := []models.ServiceableDistrict{ {Districtcode: "CBE", Statecode: "TN", Districtname: "Coimbatore", Available: true, Hubid: cbeHub, Promise: "Same-day delivery", Centrelatitude: 11.0168, Centrelongitude: 76.9558, Pincodeprefix: "6410", Displayorder: 1}, {Districtcode: "CHN", Statecode: "TN", Districtname: "Chennai", Available: true, Hubid: chnHub, Promise: "Next-day delivery", Centrelatitude: 13.0827, Centrelongitude: 80.2707, Pincodeprefix: "6000", Displayorder: 2}, {Districtcode: "BLR", Statecode: "KA", Districtname: "Bengaluru Urban", Available: true, Hubid: blrHub, Promise: "Next-day delivery", Centrelatitude: 12.9716, Centrelongitude: 77.5946, Pincodeprefix: "5600", Displayorder: 3}, {Districtcode: "EKM", Statecode: "KL", Districtname: "Ernakulam", Available: true, Hubid: nil, Promise: "2-day delivery", Centrelatitude: 9.9816, Centrelongitude: 76.2999, Pincodeprefix: "6820", Displayorder: 4}, } for _, d := range districts { var ex models.ServiceableDistrict if db.DB.Where("districtcode = ?", d.Districtcode).First(&ex).Error != nil { if err := db.DB.Create(&d).Error; err != nil { log.Fatalf("seed district %s: %v", d.Districtcode, err) } } } fmt.Println("✔ reference districts") // Pricing (create-if-absent by tenant+location+vehicle). farFuture := now.AddDate(1, 0, 0) pricing := []models.Pricing{ {Tenantid: 1, Applocationid: 1, Vehicletype: "Bike", Baseprice: 40, Baseweight: 5, Priceperkg: 8, Basedistance: 5, Priceperkm: 8, Handlingcharges: 5, Effectivefrom: now, Effectiveto: farFuture, Currency: "INR", Priority: 1, Status: "Active"}, {Tenantid: 1, Applocationid: 1, Vehicletype: "Auto", Baseprice: 60, Baseweight: 10, Priceperkg: 10, Basedistance: 5, Priceperkm: 12, Handlingcharges: 10, Effectivefrom: now, Effectiveto: farFuture, Currency: "INR", Priority: 1, Status: "Active"}, } for _, p := range pricing { var ex models.Pricing if db.DB.Where("tenantid = ? AND applocationid = ? AND vehicletype = ?", p.Tenantid, p.Applocationid, p.Vehicletype).First(&ex).Error != nil { if err := db.DB.Create(&p).Error; err != nil { log.Fatalf("seed pricing %s: %v", p.Vehicletype, err) } } } fmt.Println("✔ pricing") // ───────────────────────────────────────────────────────────────────────── // 2. TENANT LOCATIONS — the client sites parcels are collected from. // Tenant 1 must exist (create-if-absent), then its primary location. // ───────────────────────────────────────────────────────────────────────── var t1 models.Tenant if db.DB.Where("tenantid = ?", 1).First(&t1).Error != nil { t1 = models.Tenant{Tenantid: 1, Tenantname: "Doormile Coimbatore Logistics", Primaryemail: "cbe-ops@doormile.com", Primarycontact: "9876543210", Status: "Active"} if err := db.DB.Create(&t1).Error; err != nil { log.Fatalf("seed tenant 1: %v", err) } } var tl models.TenantLocation if db.DB.Where("tenantid = ? AND address = ?", 1, "124 Gandhipuram Main Rd").First(&tl).Error != nil { tl = models.TenantLocation{Tenantid: 1, Address: "124 Gandhipuram Main Rd", City: "Coimbatore", State: "Tamil Nadu", Pincode: "641012", Latitude: 11.0168, Longitude: 76.9558, Isprimary: true, Status: "Active"} if err := db.DB.Create(&tl).Error; err != nil { log.Fatalf("seed tenant location: %v", err) } } fmt.Println("✔ tenant locations") // ───────────────────────────────────────────────────────────────────────── // 3. TEST MILERS — NO PIN. Created with an empty Password so LoginMiler // reports pin_set:false and the app shows the set-PIN screen. On re-run, // an already-created rider is LEFT ALONE (their chosen PIN is never wiped). // ───────────────────────────────────────────────────────────────────────── testMilers := []struct { Authname, Email, Phone, Display string Lat, Lng float64 Pincode string Hubid *int }{ {"Test Rider One", "test.rider1@doormile.in", "8000000001", "Test Rider One (Coimbatore)", 11.0175, 76.9565, "641012", cbeHub}, {"Test Rider Two", "test.rider2@doormile.in", "8000000002", "Test Rider Two (Coimbatore)", 11.0301, 77.0034, "641004", cbeHub}, {"Test Rider Three", "test.rider3@doormile.in", "8000000003", "Test Rider Three (Coimbatore)", 11.0205, 76.9760, "641018", cbeHub}, } var firstTestRiderID int for i, m := range testMilers { var user models.AppUser if db.DB.Where("contactno = ? AND configid = ?", m.Phone, 1001).First(&user).Error != nil { user = models.AppUser{ Authname: m.Authname, Email: m.Email, Contactno: m.Phone, Password: "", // no PIN — rider self-sets on first login Roleid: 5, Configid: 1001, Tenantid: 1, Applocationid: 1, Status: "Active", } if m.Hubid != nil { user.Hubid = m.Hubid } if err := db.DB.Create(&user).Error; err != nil { log.Fatalf("seed test miler %s: %v", m.Phone, err) } fmt.Printf(" created %s (%s) — no PIN, ready for set-pin\n", m.Display, m.Phone) } else { state := "PIN already set" if user.Password == "" { state = "no PIN — ready for set-pin" } fmt.Printf(" exists %s (%s) — %s (left untouched)\n", m.Display, m.Phone, state) } var profile models.MilerProfile if db.DB.Where("userid = ?", user.Userid).First(&profile).Error != nil { profile = models.MilerProfile{ Userid: user.Userid, Displayname: m.Display, Phone: m.Phone, Availabilitystatus: "Offline", Rating: 5.0, Currentlatitude: m.Lat, Currentlongitude: m.Lng, Currentpincode: m.Pincode, Defaultvehicletype: "Bike", Applocationid: 1, } if m.Hubid != nil { profile.Hubid = m.Hubid } if err := db.DB.Create(&profile).Error; err != nil { log.Fatalf("seed test miler profile %s: %v", m.Phone, err) } } if i == 0 { firstTestRiderID = user.Userid } } fmt.Println("✔ test milers (no PIN)") // ───────────────────────────────────────────────────────────────────────── // 4. SAMPLE CUSTOMER + MULTI-DESTINATION BOOKING. // One pickup in Coimbatore → two destinations (Chennai, Bengaluru), two // consignments in status Created (in the rider's hands), assigned to the // first test rider — the exact shape to test the base-handover fix: // inwarding parcel #1 must NOT free the rider while #2 is still carried. // ───────────────────────────────────────────────────────────────────────── var customer models.AppCustomer if db.DB.Where("phone = ?", "9000000001").First(&customer).Error != nil { customer = models.AppCustomer{ Firstname: "Test", Lastname: "Customer", Phone: "9000000001", Email: "test.customer@doormile.in", Defaultlatitude: 11.0168, Defaultlongitude: 76.9558, Defaultpincode: "641012", Status: "Active", Configid: 1001, } if err := db.DB.Create(&customer).Error; err != nil { log.Fatalf("seed customer: %v", err) } } fmt.Println("✔ sample customer") const seedBookingNo = "DM-SEED-CX-001" var existingBooking models.PickupBooking if db.DB.Where("bookingno = ?", seedBookingNo).First(&existingBooking).Error == nil { fmt.Printf("✔ sample multi-destination booking already present (%s) — skipped\n", seedBookingNo) } else { tenant1 := 1 booking := models.PickupBooking{ Bookingno: seedBookingNo, Tenantid: &tenant1, Appcustomerid: customer.Appcustomerid, Tenantlocationid: &tl.Tenantlocationid, Pickupsourcetype: "merchant", Pickupaddress: "124 Gandhipuram Main Rd, Coimbatore 641012", Pickuptitle: "124 Gandhipuram Main Rd", Pickupsub: "Gandhipuram, Coimbatore 641012", Pickuppincode: "641012", Pickuplatitude: 11.0168, Pickuplongitude: 76.9558, Deliveryaddress: "Guindy, Chennai 600032", Deliverypincode: "600032", Deliverylatitude: 13.0067, Deliverylongitude: 80.2206, Deliverycity: "Chennai", Nearesthubid: cbeHub, Bookingsource: "Customer_App", Status: "Converted_To_Consignment", Customerstage: "assigned", Customerstatus: "active", Estimateminrupees: 220, Estimatemaxrupees: 280, Routekm: 505, Assignedmileruserid: &firstTestRiderID, Createdat: now, Updatedat: now, } if err := db.DB.Create(&booking).Error; err != nil { log.Fatalf("seed booking: %v", err) } srv := models.BookingServiceOption{Bookingid: booking.Bookingid, Servicetype: "Normal", Estimatedprice: 250, Createdat: now} if err := db.DB.Create(&srv).Error; err != nil { log.Fatalf("seed service option: %v", err) } // One consignment + one destination per leg. Consignments are Created — // collected, in the rider's hands, on the way to a base. legs := []struct { Seq int StateCode, StateName string DistrictCode, DistrictName string DelLat, DelLng float64 DelPincode string Packages int }{ {0, "TN", "Tamil Nadu", "CHN", "Chennai", 13.0827, 80.2707, "600032", 2}, {1, "KA", "Karnataka", "BLR", "Bengaluru Urban", 12.9716, 77.5946, "560001", 1}, } for _, leg := range legs { trackingno := fmt.Sprintf("%s-%d", seedBookingNo, leg.Seq+1) cons := models.Consignment{ Trackingno: trackingno, Tenantid: 1, Tenantlocationid: &tl.Tenantlocationid, Pickuppincode: "641012", Deliverypincode: leg.DelPincode, Pickuplatitude: 11.0168, Pickuplongitude: 76.9558, Deliverylatitude: leg.DelLat, Deliverylongitude: leg.DelLng, Deadweight: 2.0, Chargeableweight: 2.0, Paymentmode: "Prepaid", Billingstatus: "Unbilled", Status: "Created", Createdat: now, Updatedat: now, } if err := db.DB.Create(&cons).Error; err != nil { log.Fatalf("seed consignment %s: %v", trackingno, err) } dest := models.BookingDestination{ Bookingid: booking.Bookingid, Seq: leg.Seq, Statecode: leg.StateCode, Statename: leg.StateName, Districtcode: leg.DistrictCode, Districtname: leg.DistrictName, Packagecount: leg.Packages, Recipientname: "Test Recipient", Recipientphone: "9000000009", Consignmentid: &cons.Consignmentid, Trackingno: trackingno, Stage: "assigned", } if err := db.DB.Create(&dest).Error; err != nil { log.Fatalf("seed destination %d: %v", leg.Seq, err) } } assignment := models.BookingAssignment{ Bookingid: booking.Bookingid, Mileruserid: firstTestRiderID, Assignmentstatus: "Accepted", Assignedat: now, Acceptedat: &now, } if err := db.DB.Create(&assignment).Error; err != nil { log.Fatalf("seed assignment: %v", err) } fmt.Printf("✔ sample multi-destination booking %s (2 destinations, 2 consignments, assigned to test rider %d)\n", seedBookingNo, firstTestRiderID) } fmt.Println("\n✅ seed complete.") fmt.Println(" Test rider phones (no PIN, set on first login): 8000000001 / 8000000002 / 8000000003") fmt.Println(" Test customer phone: 9000000001") fmt.Println(" Sample multi-destination booking: DM-SEED-CX-001") }