263 lines
8.2 KiB
Go
263 lines
8.2 KiB
Go
package controllers
|
|
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
|
|
"doormile/db"
|
|
"doormile/dto"
|
|
"doormile/models"
|
|
"doormile/utils"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
// Customer profile and saved addresses.
|
|
//
|
|
// The rest of the customer surface — auth, catalogue, estimate, bookings,
|
|
// tracking, places, devices — lives in the cx*Controller.go files and answers
|
|
// in the customer envelope (utils.CxOK / utils.CxFail). The PIN login,
|
|
// single-destination booking create/list/detail/cancel and the /customer/track
|
|
// read that used to live here were replaced by that surface, not moved: a
|
|
// customer books a pickup with 1..N destinations now, and there is no shape in
|
|
// which the old single-address request is still a valid booking.
|
|
|
|
func calculateDistance(lat1, lon1, lat2, lon2 float64) float64 {
|
|
const R = 6371.0
|
|
dLat := (lat2 - lat1) * math.Pi / 180.0
|
|
dLon := (lon2 - lon1) * math.Pi / 180.0
|
|
a := math.Sin(dLat/2)*math.Sin(dLat/2) +
|
|
math.Cos(lat1*math.Pi/180.0)*math.Cos(lat2*math.Pi/180.0)*
|
|
math.Sin(dLon/2)*math.Sin(dLon/2)
|
|
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
|
return R * c
|
|
}
|
|
|
|
func calculateVolumetricWeight(length, width, height float64) float64 {
|
|
return (length * width * height) / 5000.0
|
|
}
|
|
|
|
func GetCustomerProfile(c *fiber.Ctx) error {
|
|
customerID := c.Locals("userid").(int)
|
|
|
|
var customer models.AppCustomer
|
|
if err := db.DB.First(&customer, customerID).Error; err != nil {
|
|
return utils.CxNotFound(c, "We could not find your profile")
|
|
}
|
|
|
|
return utils.CxOK(c, renderCustomer(&customer))
|
|
}
|
|
|
|
func UpdateCustomerProfile(c *fiber.Ctx) error {
|
|
customerID := c.Locals("userid").(int)
|
|
|
|
var customer models.AppCustomer
|
|
if err := db.DB.First(&customer, customerID).Error; err != nil {
|
|
return utils.CxNotFound(c, "We could not find your profile")
|
|
}
|
|
|
|
// Pointer fields: an omitted key leaves the stored value alone, an explicit
|
|
// value overwrites it. The previous version cleared email and lastname on
|
|
// every call that did not resend them, which quietly wiped a customer's
|
|
// email the first time they edited their name.
|
|
var req struct {
|
|
Name *string `json:"name"`
|
|
Email *string `json:"email"`
|
|
Defaultlatitude *float64 `json:"defaultLatitude"`
|
|
Defaultlongitude *float64 `json:"defaultLongitude"`
|
|
Defaultpincode *string `json:"defaultPincode"`
|
|
}
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return utils.CxBadRequest(c, "We could not read that request")
|
|
}
|
|
|
|
if req.Name != nil {
|
|
first, last := splitName(*req.Name)
|
|
if first == "" {
|
|
return utils.CxFail(c, fiber.StatusBadRequest, utils.CxErrInvalidName, "Enter your full name")
|
|
}
|
|
customer.Firstname, customer.Lastname = first, last
|
|
}
|
|
if req.Email != nil {
|
|
customer.Email = *req.Email
|
|
}
|
|
if req.Defaultlatitude != nil {
|
|
customer.Defaultlatitude = *req.Defaultlatitude
|
|
}
|
|
if req.Defaultlongitude != nil {
|
|
customer.Defaultlongitude = *req.Defaultlongitude
|
|
}
|
|
if req.Defaultpincode != nil {
|
|
customer.Defaultpincode = *req.Defaultpincode
|
|
}
|
|
|
|
if err := db.DB.Save(&customer).Error; err != nil {
|
|
utils.Error("UpdateCustomerProfile: save failed", "customer_id", customerID, "error", err)
|
|
return utils.CxInternal(c)
|
|
}
|
|
|
|
return utils.CxOK(c, renderCustomer(&customer))
|
|
}
|
|
|
|
func GetCustomerLocations(c *fiber.Ctx) error {
|
|
customerID := c.Locals("userid").(int)
|
|
|
|
var locations []models.AppCustomerLocation
|
|
if err := db.DB.Where("appcustomerid = ? AND status = ?", customerID, "Active").
|
|
Order("isdefault DESC, appcustomerlocationid DESC").Find(&locations).Error; err != nil {
|
|
utils.Error("GetCustomerLocations: query failed", "customer_id", customerID, "error", err)
|
|
return utils.CxInternal(c)
|
|
}
|
|
|
|
out := make([]fiber.Map, 0, len(locations))
|
|
for i := range locations {
|
|
out = append(out, renderSavedAddress(&locations[i]))
|
|
}
|
|
return utils.CxList(c, out, len(out), nil)
|
|
}
|
|
|
|
func CreateCustomerLocation(c *fiber.Ctx) error {
|
|
customerID := c.Locals("userid").(int)
|
|
|
|
var count int64
|
|
db.DB.Model(&models.AppCustomerLocation{}).
|
|
Where("appcustomerid = ? AND status = ?", customerID, "Active").Count(&count)
|
|
if count >= 10 {
|
|
return utils.CxBadRequest(c, "You can save up to 10 addresses")
|
|
}
|
|
|
|
req := new(dto.LocationCreateRequest)
|
|
if err := c.BodyParser(req); err != nil {
|
|
return utils.CxBadRequest(c, "We could not read that request")
|
|
}
|
|
|
|
if req.Address == "" || req.Pincode == "" || req.Latitude == 0 || req.Longitude == 0 {
|
|
return utils.CxBadRequest(c, "An address needs a street, a pincode and a map location")
|
|
}
|
|
|
|
if req.Isdefault {
|
|
db.DB.Model(&models.AppCustomerLocation{}).
|
|
Where("appcustomerid = ?", customerID).Update("isdefault", false)
|
|
}
|
|
|
|
location := models.AppCustomerLocation{
|
|
Appcustomerid: customerID,
|
|
Label: req.Label,
|
|
Receivername: req.Receivername,
|
|
Receiverphone: req.Receiverphone,
|
|
Address: req.Address,
|
|
Landmark: req.Landmark,
|
|
City: req.City,
|
|
State: req.State,
|
|
Pincode: req.Pincode,
|
|
Latitude: req.Latitude,
|
|
Longitude: req.Longitude,
|
|
Isdefault: req.Isdefault,
|
|
Status: "Active",
|
|
}
|
|
|
|
if err := db.DB.Create(&location).Error; err != nil {
|
|
utils.Error("CreateCustomerLocation: insert failed", "customer_id", customerID, "error", err)
|
|
return utils.CxInternal(c)
|
|
}
|
|
|
|
return utils.CxCreated(c, renderSavedAddress(&location))
|
|
}
|
|
|
|
func UpdateCustomerLocation(c *fiber.Ctx) error {
|
|
customerID := c.Locals("userid").(int)
|
|
locationID, err := strconv.Atoi(c.Params("id"))
|
|
if err != nil {
|
|
return utils.CxBadRequest(c, "That address could not be found")
|
|
}
|
|
|
|
var location models.AppCustomerLocation
|
|
if err := db.DB.Where("appcustomerlocationid = ? AND appcustomerid = ?", locationID, customerID).
|
|
First(&location).Error; err != nil {
|
|
return utils.CxNotFound(c, "That address could not be found")
|
|
}
|
|
|
|
req := new(dto.LocationCreateRequest)
|
|
if err := c.BodyParser(req); err != nil {
|
|
return utils.CxBadRequest(c, "We could not read that request")
|
|
}
|
|
|
|
if req.Label != "" {
|
|
location.Label = req.Label
|
|
}
|
|
location.Receivername = req.Receivername
|
|
location.Receiverphone = req.Receiverphone
|
|
if req.Address != "" {
|
|
location.Address = req.Address
|
|
}
|
|
location.Landmark = req.Landmark
|
|
location.City = req.City
|
|
location.State = req.State
|
|
if req.Pincode != "" {
|
|
location.Pincode = req.Pincode
|
|
}
|
|
if req.Latitude != 0 {
|
|
location.Latitude = req.Latitude
|
|
}
|
|
if req.Longitude != 0 {
|
|
location.Longitude = req.Longitude
|
|
}
|
|
location.Isdefault = req.Isdefault
|
|
|
|
if req.Isdefault {
|
|
db.DB.Model(&models.AppCustomerLocation{}).
|
|
Where("appcustomerid = ?", customerID).Update("isdefault", false)
|
|
}
|
|
|
|
if err := db.DB.Save(&location).Error; err != nil {
|
|
utils.Error("UpdateCustomerLocation: save failed", "location_id", locationID, "error", err)
|
|
return utils.CxInternal(c)
|
|
}
|
|
|
|
return utils.CxOK(c, renderSavedAddress(&location))
|
|
}
|
|
|
|
func DeleteCustomerLocation(c *fiber.Ctx) error {
|
|
customerID := c.Locals("userid").(int)
|
|
locationID, err := strconv.Atoi(c.Params("id"))
|
|
if err != nil {
|
|
return utils.CxBadRequest(c, "That address could not be found")
|
|
}
|
|
|
|
var location models.AppCustomerLocation
|
|
if err := db.DB.Where("appcustomerlocationid = ? AND appcustomerid = ?", locationID, customerID).
|
|
First(&location).Error; err != nil {
|
|
return utils.CxNotFound(c, "That address could not be found")
|
|
}
|
|
|
|
location.Status = "InActive"
|
|
if err := db.DB.Save(&location).Error; err != nil {
|
|
utils.Error("DeleteCustomerLocation: save failed", "location_id", locationID, "error", err)
|
|
return utils.CxInternal(c)
|
|
}
|
|
|
|
return utils.CxOK(c, fiber.Map{"id": strconv.Itoa(location.Appcustomerlocationid), "deleted": true})
|
|
}
|
|
|
|
// renderSavedAddress is the one shape a saved address is returned in, matching
|
|
// the two-line title/sub the pickup search and the booking pickup block use —
|
|
// so an address picked from Saved and one picked from search are the same
|
|
// object to the client.
|
|
func renderSavedAddress(l *models.AppCustomerLocation) fiber.Map {
|
|
title := l.Label
|
|
if title == "" {
|
|
title = l.Address
|
|
}
|
|
return fiber.Map{
|
|
"id": strconv.Itoa(l.Appcustomerlocationid),
|
|
"label": l.Label,
|
|
"title": title,
|
|
"sub": joinNonEmpty(", ", l.Address, l.Landmark, l.City, l.Pincode),
|
|
"recipientName": l.Receivername,
|
|
"recipientPhone": l.Receiverphone,
|
|
"lat": l.Latitude,
|
|
"lng": l.Longitude,
|
|
"isDefault": l.Isdefault,
|
|
}
|
|
}
|