updates on the milerappcontroller and routes allother thing
This commit is contained in:
@@ -37,7 +37,7 @@ func MilerStartDuty(c *fiber.Ctx) error {
|
|||||||
var existing models.MilerDutyLog
|
var existing models.MilerDutyLog
|
||||||
if err := db.DB.Where("userid = ? AND onduty = ? AND logoutat IS NULL AND loginat >= ?", milerUserID, true, midnight).
|
if err := db.DB.Where("userid = ? AND onduty = ? AND logoutat IS NULL AND loginat >= ?", milerUserID, true, midnight).
|
||||||
First(&existing).Error; err == nil {
|
First(&existing).Error; err == nil {
|
||||||
return utils.BadRequest(c, "already on duty, end current duty first")
|
return resumeMilerDuty(c, milerUserID, existing, req.Lat, req.Lon)
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
@@ -79,6 +79,37 @@ func MilerStartDuty(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resumeMilerDuty answers a start-duty for a rider whose duty from earlier
|
||||||
|
// today is still open — after a reinstall, a crash, or signing in again. It
|
||||||
|
// used to answer 400 "already on duty" and change nothing: the app treated
|
||||||
|
// that as "on duty" and showed the rider online, while their profile stayed
|
||||||
|
// Offline, so the console listed them Offline and auto-assignment skipped
|
||||||
|
// them. Now the open duty is resumed: an Offline rider becomes Available
|
||||||
|
// (a rider mid-delivery or on a break keeps that status) and is made findable
|
||||||
|
// again from where they are.
|
||||||
|
func resumeMilerDuty(c *fiber.Ctx, milerUserID int, duty models.MilerDutyLog, lat, lon float64) error {
|
||||||
|
now := time.Now()
|
||||||
|
db.DB.Model(&models.MilerProfile{}).
|
||||||
|
Where("userid = ? AND availabilitystatus = ?", milerUserID, constants.MilerOffline).
|
||||||
|
Updates(map[string]interface{}{"availabilitystatus": constants.MilerAvailable, "updatedat": now})
|
||||||
|
if lat != 0 && lon != 0 {
|
||||||
|
db.DB.Model(&models.MilerProfile{}).Where("userid = ?", milerUserID).Updates(map[string]interface{}{
|
||||||
|
"currentlatitude": lat,
|
||||||
|
"currentlongitude": lon,
|
||||||
|
"lastlocationupdatedat": now,
|
||||||
|
})
|
||||||
|
indexMilerLocation(milerUserID, lat, lon)
|
||||||
|
go assignment.SweepNear(lat, lon)
|
||||||
|
}
|
||||||
|
db.DB.Model(&models.AppUser{}).Where("userid = ?", milerUserID).Update("onduty", 1)
|
||||||
|
|
||||||
|
return utils.OK(c, fiber.Map{
|
||||||
|
"dutylogid": duty.Dutylogid,
|
||||||
|
"loginat": duty.Loginat,
|
||||||
|
"resumed": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func MilerEndDuty(c *fiber.Ctx) error {
|
func MilerEndDuty(c *fiber.Ctx) error {
|
||||||
milerUserID := c.Locals("userid").(int)
|
milerUserID := c.Locals("userid").(int)
|
||||||
|
|
||||||
|
|||||||
@@ -450,12 +450,14 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
|
|||||||
adminAuth.Delete("/carrier-pricing/:id", middlewares.DoormileStaffOnly, controllers.DeleteCarrierPricing)
|
adminAuth.Delete("/carrier-pricing/:id", middlewares.DoormileStaffOnly, controllers.DeleteCarrierPricing)
|
||||||
|
|
||||||
// Pricing
|
// Pricing
|
||||||
|
// A client login reads its own rate card (GetPricing is tenant-scoped); every
|
||||||
|
// change, and the quote (which picks a rule across ALL tenants), is staff-only.
|
||||||
adminAuth.Get("/pricing", controllers.GetPricing)
|
adminAuth.Get("/pricing", controllers.GetPricing)
|
||||||
adminAuth.Post("/pricing", controllers.CreatePricing)
|
adminAuth.Post("/pricing", middlewares.DoormileStaffOnly, controllers.CreatePricing)
|
||||||
adminAuth.Put("/pricing/:id", controllers.UpdatePricing)
|
adminAuth.Put("/pricing/:id", middlewares.DoormileStaffOnly, controllers.UpdatePricing)
|
||||||
adminAuth.Delete("/pricing/:id", controllers.DeletePricing)
|
adminAuth.Delete("/pricing/:id", middlewares.DoormileStaffOnly, controllers.DeletePricing)
|
||||||
adminAuth.Post("/pricing/simulate", controllers.GetPricingQuoteSimulate)
|
adminAuth.Post("/pricing/simulate", middlewares.DoormileStaffOnly, controllers.GetPricingQuoteSimulate)
|
||||||
adminAuth.Post("/pricing/quote", controllers.GetPricingQuoteSimulate)
|
adminAuth.Post("/pricing/quote", middlewares.DoormileStaffOnly, controllers.GetPricingQuoteSimulate)
|
||||||
|
|
||||||
// Doormile pricing bands
|
// Doormile pricing bands
|
||||||
adminAuth.Get("/doormile-pricing", controllers.GetDoormilePricing)
|
adminAuth.Get("/doormile-pricing", controllers.GetDoormilePricing)
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import (
|
|||||||
// token on the server, not just be hidden in the UI.
|
// token on the server, not just be hidden in the UI.
|
||||||
var staffOnlyFleetRoutes = []struct{ method, path string }{
|
var staffOnlyFleetRoutes = []struct{ method, path string }{
|
||||||
{http.MethodPost, "/api/v1/admin/tenants"},
|
{http.MethodPost, "/api/v1/admin/tenants"},
|
||||||
|
{http.MethodPost, "/api/v1/admin/pricing"},
|
||||||
|
{http.MethodPut, "/api/v1/admin/pricing/1"},
|
||||||
|
{http.MethodDelete, "/api/v1/admin/pricing/1"},
|
||||||
|
{http.MethodPost, "/api/v1/admin/pricing/simulate"},
|
||||||
|
{http.MethodPost, "/api/v1/admin/pricing/quote"},
|
||||||
{http.MethodGet, "/api/v1/admin/users"},
|
{http.MethodGet, "/api/v1/admin/users"},
|
||||||
{http.MethodPost, "/api/v1/admin/users"},
|
{http.MethodPost, "/api/v1/admin/users"},
|
||||||
{http.MethodPut, "/api/v1/admin/users/1"},
|
{http.MethodPut, "/api/v1/admin/users/1"},
|
||||||
|
|||||||
86
routes/routes_miler_duty_pg_test.go
Normal file
86
routes/routes_miler_duty_pg_test.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"doormile/constants"
|
||||||
|
"doormile/db"
|
||||||
|
"doormile/internal/testpg"
|
||||||
|
"doormile/models"
|
||||||
|
"doormile/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A rider whose duty from earlier today is still open (reinstall, crash,
|
||||||
|
// signing in again) and who starts duty again. The server used to answer 400
|
||||||
|
// "already on duty" and leave the profile Offline, while the app showed the
|
||||||
|
// rider online: the console listed them Offline and auto-assignment skipped
|
||||||
|
// them. Postgres-gated; the DSN must be a THROWAWAY database.
|
||||||
|
func TestStartDutyResumesAnOpenDutyAndMarksTheRiderAvailable(t *testing.T) {
|
||||||
|
dsn := os.Getenv("REGISTRY_TEST_DSN")
|
||||||
|
if dsn == "" {
|
||||||
|
t.Skip("REGISTRY_TEST_DSN not set; skipping Postgres duty test")
|
||||||
|
}
|
||||||
|
gdb := testpg.Open(t, dsn, "miler_duty_routes_test")
|
||||||
|
all := []any{&models.AppUser{}, &models.MilerProfile{}, &models.MilerDutyLog{}}
|
||||||
|
if err := gdb.Migrator().DropTable(all...); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := gdb.AutoMigrate(all...); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
prev := db.DB
|
||||||
|
db.DB = gdb
|
||||||
|
t.Cleanup(func() { db.DB = prev })
|
||||||
|
|
||||||
|
app := onboardingApp()
|
||||||
|
rider := func(name, phone, status string) (models.AppUser, string) {
|
||||||
|
u := models.AppUser{Authname: name, Email: name + "@riders.test", Contactno: phone, Password: "x", Roleid: 5, Status: "Active", Configid: 1001}
|
||||||
|
if err := gdb.Create(&u).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := gdb.Create(&models.MilerProfile{Userid: u.Userid, Displayname: name, Phone: phone, Availabilitystatus: status}).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// The duty left open from earlier today.
|
||||||
|
if err := gdb.Create(&models.MilerDutyLog{Userid: u.Userid, Loginat: time.Now(), Onduty: true}).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tok, err := utils.GenerateToken(u.Userid, u.Email, 5, 0, 1001, jwtSecret)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return u, tok
|
||||||
|
}
|
||||||
|
statusOf := func(uid int) string {
|
||||||
|
var p models.MilerProfile
|
||||||
|
gdb.Where("userid = ?", uid).First(&p)
|
||||||
|
return p.Availabilitystatus
|
||||||
|
}
|
||||||
|
|
||||||
|
offline, tok := rider("nagalakshmi", "9000000101", constants.MilerOffline)
|
||||||
|
code, body := do(t, app, http.MethodPost, "/api/v1/miler/duty/start", tok, `{"lat":0,"lon":0}`)
|
||||||
|
if code != 200 || !strings.Contains(body, `"resumed":true`) {
|
||||||
|
t.Fatalf("start duty with an open duty = %d %s, want 200 resumed", code, body)
|
||||||
|
}
|
||||||
|
if got := statusOf(offline.Userid); got != constants.MilerAvailable {
|
||||||
|
t.Fatalf("rider status after resuming = %q, want Available", got)
|
||||||
|
}
|
||||||
|
var open int64
|
||||||
|
gdb.Model(&models.MilerDutyLog{}).Where("userid = ? AND logoutat IS NULL", offline.Userid).Count(&open)
|
||||||
|
if open != 1 {
|
||||||
|
t.Fatalf("open duty logs = %d, want the one resumed, not a second", open)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A rider mid-delivery keeps that status; resuming only lifts Offline.
|
||||||
|
busy, tok := rider("busy", "9000000102", constants.MilerOnDelivery)
|
||||||
|
if code, body := do(t, app, http.MethodPost, "/api/v1/miler/duty/start", tok, `{"lat":0,"lon":0}`); code != 200 {
|
||||||
|
t.Fatalf("start duty = %d %s", code, body)
|
||||||
|
}
|
||||||
|
if got := statusOf(busy.Userid); got != constants.MilerOnDelivery {
|
||||||
|
t.Fatalf("on-delivery rider became %q, want On_Delivery kept", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user