lat and long

This commit is contained in:
2026-08-27 13:59:16 +05:30
parent dc9c049bb9
commit edb9c2803f
2 changed files with 332 additions and 1 deletions

View File

@@ -2,9 +2,12 @@ package services
import (
"errors"
"net/http"
"strings"
"nearle/models"
"nearle/repositories"
"net/http"
"nearle/utils"
)
type TenantService interface {
@@ -107,6 +110,14 @@ func (s *tenantService) UpdateStaff(user models.User) error {
}
func (s *tenantService) CreateTenantLocation(data models.Tenantlocations) map[string]interface{} {
// A branch needs a location for the same reasons a tenant does — delivery
// radius is stored on this very row. Same rule: only when the caller sent
// nothing, and never fatal.
if strings.TrimSpace(data.Latitude) == "" && strings.TrimSpace(data.Longitude) == "" {
data.Latitude, data.Longitude = utils.GeocodeAddress(
data.Address, data.Suburb, data.City, data.State, data.Postcode)
}
created, err := s.repo.CreateTenantLocation(data)
if err != nil {
return map[string]interface{}{
@@ -151,6 +162,18 @@ func (s *tenantService) CreateTenantUser(data models.Tenants) (models.UserInfo,
return models.UserInfo{}, errors.New("Tenant Already Exists")
}
// Coordinates from the address, for the tenant and its primary outlet.
//
// `tenants.latitude` and `tenants.longitude` have existed since the schema
// was written and no caller has ever filled them, so every shop on the
// platform sits at no location at all. Resolved here rather than in the
// console because the key stays server-side and because a merchant created
// by any other client — the mobile app, a script — gets the same treatment.
//
// Only when the caller sent nothing: a console that lets an operator drag a
// pin is stating something a geocoder cannot know, and must win.
fillTenantCoordinates(&data)
// ✅ Create Tenant User
status, err := s.repo.CreateTenantUser(data)
if err != nil || !status {
@@ -169,3 +192,40 @@ func (s *tenantService) GetTenantByID(tid int, locationid int, userid int) (mode
func (s *tenantService) GetTenantByKeyword(keyword string) ([]models.TenantSearch, error) {
return s.repo.GetTenantByKeyword(keyword)
}
// fillTenantCoordinates geocodes a tenant and its nested primary outlet.
//
// Never returns an error and never blocks creation. A geocoder that is down,
// slow or rate-limited leaves the columns exactly as they are today — empty —
// and a merchant is still onboarded. The alternative, refusing to create a shop
// because a third party did not answer, trades a fixable blank field for a
// person stuck on the phone.
func fillTenantCoordinates(data *models.Tenants) {
if strings.TrimSpace(data.Latitude) == "" && strings.TrimSpace(data.Longitude) == "" {
data.Latitude, data.Longitude = utils.GeocodeAddress(
data.Address, data.Suburb, data.City, data.State, data.Postcode)
}
outlet := &data.Tenantlocations
if strings.TrimSpace(outlet.Latitude) != "" || strings.TrimSpace(outlet.Longitude) != "" {
return
}
// The first outlet is almost always at the address just typed, and the
// console already copies it across. Reuse the tenant's answer rather than
// asking twice for the same string — that is a second network round trip
// inside a request someone is waiting on, for a guaranteed identical result.
if sameAddress(*data) {
outlet.Latitude, outlet.Longitude = data.Latitude, data.Longitude
return
}
outlet.Latitude, outlet.Longitude = utils.GeocodeAddress(
outlet.Address, outlet.Suburb, outlet.City, outlet.State, outlet.Postcode)
}
func sameAddress(data models.Tenants) bool {
outlet := data.Tenantlocations
return strings.EqualFold(strings.TrimSpace(outlet.Address), strings.TrimSpace(data.Address)) &&
strings.EqualFold(strings.TrimSpace(outlet.City), strings.TrimSpace(data.City)) &&
strings.EqualFold(strings.TrimSpace(outlet.Postcode), strings.TrimSpace(data.Postcode))
}