594 lines
20 KiB
Go
594 lines
20 KiB
Go
package repositories
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"nearle/models"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type PartnerRepository interface {
|
|
GetActiveRiders(partnerid, aid, uid, tid int) ([]models.RiderInfo, error)
|
|
GetPartners(aid, pid, uid int) ([]models.Partnerinfo, error)
|
|
GetRiderShifts(aid int) ([]models.Ridershifts, error)
|
|
GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error)
|
|
GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error)
|
|
GetRiderInfo(userid int) (models.RiderInfo, error)
|
|
GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error)
|
|
CreateRider(rider models.NewRider) (int, error)
|
|
UpdateRider(rider models.NewRider) error
|
|
GetRiderRoster(tid, aid, pid int) ([]models.RiderRosterRow, error)
|
|
}
|
|
|
|
type partnerRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewPartnerRepository(db *gorm.DB) PartnerRepository {
|
|
return &partnerRepository{db: db}
|
|
}
|
|
|
|
func (r *partnerRepository) GetActiveRiders(partnerid, aid, uid, tid int) ([]models.RiderInfo, error) {
|
|
var data []models.RiderInfo
|
|
var q1 string
|
|
|
|
const riders = `SELECT DISTINCT b.poolid, a.userid, a.firstname, a.lastname,CONCAT(a.firstname, ' ', a.lastname) AS fullname,a.contactno, a.userfcmtoken, a.partnerid, a.applocationid,
|
|
c.identificationno, c.registrationno, c.licenseno, c.vehiclename, c.vehicleno,d.shiftid, d.starttime, d.endtime, d.shifthours, d.basefare, d.fuelcharge,e.logdate,
|
|
e.login, e.logout, e.workhours, e.shorthours, e.logstatus, a.status,f.locationname AS applocation
|
|
FROM app_users a
|
|
INNER JOIN app_userpools b ON a.userid = b.userid
|
|
INNER JOIN ridersettings c ON a.userid = c.userid
|
|
INNER JOIN ridershifts d ON c.shiftid = d.shiftid
|
|
INNER JOIN (
|
|
SELECT r1.*
|
|
FROM riderlogs r1
|
|
INNER JOIN (
|
|
SELECT userid, MAX(logdate) AS max_logdate
|
|
FROM riderlogs
|
|
WHERE logdate::date = CURRENT_DATE
|
|
GROUP BY userid
|
|
) r2 ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate
|
|
) e ON a.userid = e.userid
|
|
INNER JOIN app_location f ON a.applocationid = f.applocationid
|
|
INNER JOIN app_locationconfig g ON g.applocationid = f.applocationid`
|
|
|
|
if aid != 0 {
|
|
q1 = riders + ` WHERE a.configid = 6 AND a.status = 'Active' AND b.onduty = 1
|
|
AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.applocationid = ` + strconv.Itoa(aid)
|
|
} else if partnerid != 0 {
|
|
q1 = riders + ` WHERE a.configid = 6 AND a.status = 'Active' AND b.onduty = 1
|
|
AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.partnerid = ` + strconv.Itoa(partnerid)
|
|
} else if tid != 0 {
|
|
q1 = riders + ` WHERE a.configid = 6 AND a.status = 'Active' AND b.onduty = 1
|
|
AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.tenantid = ` + strconv.Itoa(tid)
|
|
} else {
|
|
q1 = riders + ` WHERE g.status = 'Active' AND a.configid = 6 AND a.status = 'Active'
|
|
AND b.onduty = 1 AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND g.userid = ` + strconv.Itoa(uid)
|
|
}
|
|
|
|
err := r.db.Raw(q1).Find(&data).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (r *partnerRepository) GetPartners(aid, pid, uid int) ([]models.Partnerinfo, error) {
|
|
var data []models.Partnerinfo
|
|
var q1 string
|
|
var args []interface{}
|
|
|
|
if pid != 0 {
|
|
q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
|
|
contactno,address,suburb,state,city,partnerimage
|
|
from partnerinfo where status='Active' and partnerid=?`
|
|
args = append(args, pid)
|
|
|
|
} else if aid != 0 {
|
|
q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
|
|
contactno,address,suburb,state,city,partnerimage
|
|
from partnerinfo where status='Active' and applocationid=?`
|
|
args = append(args, aid)
|
|
|
|
} else {
|
|
q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
|
|
contactno,address,suburb,state,city,partnerimage
|
|
from partnerinfo where status='Active'`
|
|
}
|
|
|
|
err := r.db.Raw(q1, args...).Find(&data).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
print(q1)
|
|
return data, nil
|
|
}
|
|
|
|
func (r *partnerRepository) GetRiderShifts(aid int) ([]models.Ridershifts, error) {
|
|
|
|
var data []models.Ridershifts
|
|
|
|
q1 := `Select a.*,concat(a.starttime,'-',a.endtime) as shiftname from ridershifts a where a.applocationid=` + strconv.Itoa(aid)
|
|
|
|
err := r.db.Raw(q1).Find(&data).Error
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
print(q1)
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (r *partnerRepository) GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) {
|
|
|
|
var data []models.Locationconfigs
|
|
|
|
q1 := fmt.Sprintf(`SELECT a.applocationid,a.locationname FROM app_location a
|
|
inner join app_locationconfig b ON a.applocationid=b.applocationid
|
|
WHERE b.status='Active' and b.userid=%d`, uid)
|
|
|
|
err := r.db.Raw(q1).Find(&data).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
print(q1)
|
|
return data, nil
|
|
}
|
|
|
|
func (r *partnerRepository) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) {
|
|
var data []models.RiderlogDetails
|
|
var args []interface{}
|
|
|
|
baseQuery := `
|
|
SELECT a.*, b.*, CONCAT(b.firstname,' ',b.lastname) AS username,
|
|
COALESCE(SUM(c.breakhours),0) AS breakhours
|
|
FROM riderlogs a
|
|
INNER JOIN app_users b ON a.userid = b.userid
|
|
LEFT JOIN riderbreaks c ON a.logid = c.logid AND a.userid = c.userid
|
|
WHERE 1=1
|
|
`
|
|
|
|
if pid != 0 {
|
|
baseQuery += " AND a.partnerid = ?"
|
|
args = append(args, pid)
|
|
}
|
|
|
|
if aid != 0 {
|
|
baseQuery += " AND b.applocationid = ?"
|
|
args = append(args, aid)
|
|
}
|
|
|
|
if fdate != "" && tdate != "" {
|
|
baseQuery += " AND logdate::date BETWEEN ? AND ?"
|
|
args = append(args, fdate, tdate)
|
|
} else {
|
|
baseQuery += " AND a.logdate::date = CURRENT_DATE"
|
|
}
|
|
|
|
// GROUP BY the two PRIMARY KEYS, not by a.userid.
|
|
//
|
|
// The select is "a.*, b.*" — every column of riderlogs and app_users — and
|
|
// grouping by a.userid leaves all of them unaggregated: userid is not
|
|
// riderlogs' key (logid is), so it does not functionally determine a.logid
|
|
// or anything else. Postgres refuses the whole statement, so this endpoint
|
|
// answered 500 "column a.logid must appear in the GROUP BY clause" on every
|
|
// call it has ever received. Verified on production 2026-09-04.
|
|
//
|
|
// Grouping by both primary keys is what the query means: one row per LOG
|
|
// (the model is RiderlogDetails and the sort is by logid), carrying that
|
|
// log's break hours summed. Postgres treats a PK in GROUP BY as determining
|
|
// the rest of its table's columns, so "a.*, b.*" is then legal.
|
|
baseQuery += " GROUP BY a.logid, b.userid ORDER BY a.logid ASC"
|
|
|
|
if err := r.db.Raw(baseQuery, args...).Find(&data).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (r *partnerRepository) GetRiderInfo(userid int) (models.RiderInfo, error) {
|
|
var data models.RiderInfo
|
|
|
|
q1 := `SELECT a.userid,a.firstname,a.lastname, CONCAT(a.firstname,' ',a.lastname) as fullname,
|
|
a.partnerid,a.configid,a.contactno,
|
|
a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,
|
|
b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
|
|
c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,c.fuelcharge,a.status,
|
|
a.applocationid,d.locationname as applocation,d.logseconds
|
|
FROM app_users a
|
|
INNER JOIN ridersettings b ON a.userid=b.userid
|
|
INNER JOIN ridershifts c ON b.shiftid=c.shiftid
|
|
INNER JOIN app_location d ON a.applocationid=d.applocationid
|
|
WHERE a.userid = ?`
|
|
|
|
if err := r.db.Raw(q1, userid).Scan(&data).Error; err != nil {
|
|
return models.RiderInfo{}, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (r *partnerRepository) GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error) {
|
|
var summary models.FleetSummary
|
|
|
|
// --- Fleet membership filter (app_users aliased as a) ---
|
|
memberWhere := "a.configid = 6"
|
|
var memberArgs []interface{}
|
|
if aid != 0 {
|
|
memberWhere += " AND a.applocationid = ?"
|
|
memberArgs = append(memberArgs, aid)
|
|
}
|
|
if tid != 0 {
|
|
memberWhere += " AND a.tenantid = ?"
|
|
memberArgs = append(memberArgs, tid)
|
|
}
|
|
|
|
countsQuery := `
|
|
SELECT
|
|
COUNT(*) AS totalriders,
|
|
COUNT(*) FILTER (WHERE a.status = 'Active') AS activeriders
|
|
FROM app_users a
|
|
WHERE ` + memberWhere
|
|
|
|
if err := r.db.Raw(countsQuery, memberArgs...).Scan(&summary.Counts).Error; err != nil {
|
|
return models.FleetSummary{}, err
|
|
}
|
|
|
|
// --- Log-range filter (app_users aliased as b, riderlogs as a) ---
|
|
logWhere := "b.configid = 6"
|
|
var logArgs []interface{}
|
|
if aid != 0 {
|
|
logWhere += " AND b.applocationid = ?"
|
|
logArgs = append(logArgs, aid)
|
|
}
|
|
if tid != 0 {
|
|
logWhere += " AND b.tenantid = ?"
|
|
logArgs = append(logArgs, tid)
|
|
}
|
|
if fdate != "" && tdate != "" {
|
|
logWhere += " AND a.logdate::date BETWEEN ? AND ?"
|
|
logArgs = append(logArgs, fdate, tdate)
|
|
} else {
|
|
logWhere += " AND a.logdate::date = CURRENT_DATE"
|
|
}
|
|
|
|
var worked struct {
|
|
Ridersworked int `json:"ridersworked"`
|
|
Totallogins int `json:"totallogins"`
|
|
}
|
|
|
|
workedQuery := `
|
|
SELECT
|
|
COUNT(DISTINCT a.userid) AS ridersworked,
|
|
COUNT(*) AS totallogins
|
|
FROM riderlogs a
|
|
INNER JOIN app_users b ON a.userid = b.userid
|
|
WHERE ` + logWhere
|
|
|
|
if err := r.db.Raw(workedQuery, logArgs...).Scan(&worked).Error; err != nil {
|
|
return models.FleetSummary{}, err
|
|
}
|
|
summary.Counts.Ridersworked = worked.Ridersworked
|
|
summary.Counts.Totallogins = worked.Totallogins
|
|
|
|
// --- Per-rider breakdown over the range ---
|
|
// Breaks are pre-aggregated per log so the join cannot inflate the work/short-hour sums.
|
|
ridersQuery := `
|
|
SELECT
|
|
b.userid,
|
|
CONCAT(b.firstname, ' ', b.lastname) AS fullname,
|
|
b.contactno,
|
|
b.partnerid,
|
|
b.status,
|
|
rs.vehicleno,
|
|
rs.vehiclename,
|
|
COUNT(DISTINCT a.logdate::date) AS dayslogged,
|
|
COALESCE(SUM(a.workhours), 0) AS totalworkhours,
|
|
COALESCE(SUM(a.shorthours), 0) AS totalshorthours,
|
|
COALESCE(SUM(bk.breakhours), 0) AS totalbreakhours
|
|
FROM riderlogs a
|
|
INNER JOIN app_users b ON a.userid = b.userid
|
|
LEFT JOIN ridersettings rs ON b.userid = rs.userid
|
|
LEFT JOIN (
|
|
SELECT logid, userid, SUM(breakhours) AS breakhours
|
|
FROM riderbreaks
|
|
GROUP BY logid, userid
|
|
) bk ON a.logid = bk.logid AND a.userid = bk.userid
|
|
WHERE ` + logWhere + `
|
|
GROUP BY b.userid, b.firstname, b.lastname, b.contactno, b.partnerid, b.status, rs.vehicleno, rs.vehiclename
|
|
ORDER BY fullname ASC`
|
|
|
|
if err := r.db.Raw(ridersQuery, logArgs...).Find(&summary.Riders).Error; err != nil {
|
|
return models.FleetSummary{}, err
|
|
}
|
|
|
|
return summary, nil
|
|
}
|
|
|
|
// CreateRider hires one rider, in one transaction.
|
|
//
|
|
// Three tables, all or nothing. Before this existed the only way to make a
|
|
// rider was POST /users/create, which writes `app_users` and stops — so it
|
|
// answered 201 Created and produced somebody every rider query ignored forever,
|
|
// because `getriders` INNER JOINs `ridersettings` and `app_userpools` as well.
|
|
// The old console papered over that by generating an SQL script for an operator
|
|
// to run by hand; the script named three columns that do not exist, so it never
|
|
// worked either.
|
|
//
|
|
// The guards below all catch the same class of fault: a write that succeeds and
|
|
// then cannot be seen. Postgres will not complain about any of them — a
|
|
// `shiftid` pointing nowhere is a perfectly good integer — but each one produces
|
|
// a rider who is invisible the moment the transaction commits.
|
|
func (r *partnerRepository) CreateRider(rider models.NewRider) (int, error) {
|
|
if strings.TrimSpace(rider.Firstname) == "" {
|
|
return 0, errors.New("the rider needs a name")
|
|
}
|
|
if strings.TrimSpace(rider.Contactno) == "" {
|
|
return 0, errors.New("the rider needs a contact number")
|
|
}
|
|
if rider.Applocationid == 0 {
|
|
return 0, errors.New("the rider needs a delivery region")
|
|
}
|
|
if rider.Shiftid == 0 {
|
|
return 0, errors.New("the rider needs a shift")
|
|
}
|
|
|
|
// A shift that does not exist takes the rider out of every listing:
|
|
// `getriders` joins ridershifts through ridersettings.shiftid.
|
|
var shifts int64
|
|
if err := r.db.Table("ridershifts").Where("shiftid = ?", rider.Shiftid).Count(&shifts).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
if shifts == 0 {
|
|
return 0, fmt.Errorf("shift %d does not exist", rider.Shiftid)
|
|
}
|
|
|
|
// Same again for the region, which is joined twice — app_location AND
|
|
// app_locationconfig. A region with no config row hides every rider in it.
|
|
var configs int64
|
|
if err := r.db.Table("app_locationconfig").Where("applocationid = ?", rider.Applocationid).Count(&configs).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
if configs == 0 {
|
|
return 0, fmt.Errorf("delivery region %d is not configured, so a rider added to it would not appear anywhere", rider.Applocationid)
|
|
}
|
|
|
|
// Riders are looked up by phone more than by anything else, and two accounts
|
|
// on one number is how the wrong person gets the job.
|
|
var clash int64
|
|
if err := r.db.Table("app_users").
|
|
Where("contactno = ? AND configid = 6", strings.TrimSpace(rider.Contactno)).
|
|
Count(&clash).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
if clash > 0 {
|
|
return 0, fmt.Errorf("a rider already uses %s", strings.TrimSpace(rider.Contactno))
|
|
}
|
|
|
|
status := strings.TrimSpace(rider.Status)
|
|
if status == "" {
|
|
status = "Active"
|
|
}
|
|
|
|
tx := r.db.Begin()
|
|
if tx.Error != nil {
|
|
return 0, tx.Error
|
|
}
|
|
|
|
// configid 6 is what identifies a rider — there is no Rider row in
|
|
// app_roles, and `getriders` keys on the configid rather than on a role.
|
|
user := models.User{
|
|
Firstname: strings.TrimSpace(rider.Firstname),
|
|
Lastname: strings.TrimSpace(rider.Lastname),
|
|
Contactno: strings.TrimSpace(rider.Contactno),
|
|
Email: strings.TrimSpace(rider.Email),
|
|
Password: rider.Password,
|
|
Address: rider.Address,
|
|
Suburb: rider.Suburb,
|
|
City: rider.City,
|
|
State: rider.State,
|
|
Postcode: rider.Postcode,
|
|
Configid: 6,
|
|
Tenantid: rider.Tenantid,
|
|
Applocationid: rider.Applocationid,
|
|
Partnerid: rider.Partnerid,
|
|
Shiftid: rider.Shiftid,
|
|
Status: status,
|
|
}
|
|
|
|
if err := tx.Table("app_users").Create(&user).Error; err != nil {
|
|
tx.Rollback()
|
|
return 0, err
|
|
}
|
|
if user.Userid == 0 {
|
|
tx.Rollback()
|
|
return 0, errors.New("the rider account was written without an id")
|
|
}
|
|
|
|
settings := models.Ridersettings{
|
|
Userid: user.Userid,
|
|
Partnerid: rider.Partnerid,
|
|
Shiftid: rider.Shiftid,
|
|
Identificationno: rider.Identificationno,
|
|
Vehiclename: rider.Vehiclename,
|
|
Vehicleno: rider.Vehicleno,
|
|
Licenseno: rider.Licenseno,
|
|
Registrationno: rider.Registrationno,
|
|
}
|
|
if err := tx.Table("ridersettings").Create(&settings).Error; err != nil {
|
|
tx.Rollback()
|
|
return 0, err
|
|
}
|
|
|
|
// onduty 1 means "available for work", not "on shift now" — that second
|
|
// question is answered by riderlogs, which the rider's own app writes when
|
|
// they clock on. A rider created here is therefore correctly absent from
|
|
// getriders until they start a shift.
|
|
pool := models.Appuserpools{
|
|
Userid: user.Userid,
|
|
Partnerid: rider.Partnerid,
|
|
Onduty: 1,
|
|
Status: status,
|
|
}
|
|
if err := tx.Table("app_userpools").Create(&pool).Error; err != nil {
|
|
tx.Rollback()
|
|
return 0, err
|
|
}
|
|
|
|
if err := tx.Commit().Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return user.Userid, nil
|
|
}
|
|
|
|
// GetRiderRoster lists every rider, on duty or not.
|
|
//
|
|
// Distinct from GetActiveRiders, and the difference is the whole point. That
|
|
// one INNER JOINs a riderlog dated today with logstatus 0 — it answers "who can
|
|
// I give this delivery to right now", which is correct for an assignment picker
|
|
// and useless for a staff directory: somebody hired this morning, or simply not
|
|
// working today, is absent from it. A directory that hides the person you just
|
|
// created reads as a failed save.
|
|
//
|
|
// So every join here is LEFT except the rider's own settings, and the duty
|
|
// state is returned as facts — `onduty`, the last log date, and whether that
|
|
// log is today — rather than used as a filter.
|
|
func (r *partnerRepository) GetRiderRoster(tid, aid, pid int) ([]models.RiderRosterRow, error) {
|
|
var data []models.RiderRosterRow
|
|
|
|
q1 := `SELECT a.userid, a.firstname, a.lastname,
|
|
CONCAT(a.firstname, ' ', a.lastname) AS fullname,
|
|
a.contactno, a.email, a.tenantid, a.applocationid, a.partnerid, a.status,
|
|
f.locationname AS applocation,
|
|
p.partnername,
|
|
c.identificationno, c.vehiclename, c.vehicleno, c.licenseno, c.registrationno,
|
|
c.shiftid, CONCAT(d.starttime, ' - ', d.endtime) AS shiftname,
|
|
COALESCE(b.onduty, 0) AS onduty,
|
|
e.logdate AS lastlogdate,
|
|
COALESCE(e.logdate::date = CURRENT_DATE AND e.logstatus = 0, false) AS isonduty
|
|
FROM app_users a
|
|
INNER JOIN ridersettings c ON a.userid = c.userid
|
|
LEFT JOIN app_userpools b ON a.userid = b.userid
|
|
LEFT JOIN ridershifts d ON c.shiftid = d.shiftid
|
|
LEFT JOIN app_location f ON a.applocationid = f.applocationid
|
|
LEFT JOIN partnerinfo p ON a.partnerid = p.partnerid
|
|
LEFT JOIN (
|
|
SELECT r1.userid, r1.logdate, r1.logstatus
|
|
FROM riderlogs r1
|
|
INNER JOIN (
|
|
SELECT userid, MAX(logdate) AS max_logdate FROM riderlogs GROUP BY userid
|
|
) r2 ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate
|
|
) e ON a.userid = e.userid
|
|
WHERE a.configid = 6`
|
|
|
|
var args []interface{}
|
|
// Scoped by whichever id the caller has. Tenant first: it is the narrowest,
|
|
// and it is the scope a merchant's own directory wants.
|
|
if tid != 0 {
|
|
q1 += ` AND a.tenantid = ?`
|
|
args = append(args, tid)
|
|
} else if aid != 0 {
|
|
q1 += ` AND a.applocationid = ?`
|
|
args = append(args, aid)
|
|
} else if pid != 0 {
|
|
q1 += ` AND a.partnerid = ?`
|
|
args = append(args, pid)
|
|
}
|
|
|
|
q1 += ` ORDER BY a.firstname, a.lastname`
|
|
|
|
if err := r.db.Raw(q1, args...).Scan(&data).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// UpdateRider edits a rider across both of their tables.
|
|
//
|
|
// Only the fields a person can change from the console: their contact details,
|
|
// their vehicle and licence, their shift, and their status. The identity
|
|
// columns and the tenant are not editable — moving a rider between merchants is
|
|
// not an edit, and doing it silently through a profile form is how a rider ends
|
|
// up on somebody else's books.
|
|
func (r *partnerRepository) UpdateRider(rider models.NewRider) error {
|
|
if rider.Userid == 0 {
|
|
return errors.New("userid is required")
|
|
}
|
|
|
|
if rider.Shiftid != 0 {
|
|
var shifts int64
|
|
if err := r.db.Table("ridershifts").Where("shiftid = ?", rider.Shiftid).Count(&shifts).Error; err != nil {
|
|
return err
|
|
}
|
|
if shifts == 0 {
|
|
return fmt.Errorf("shift %d does not exist", rider.Shiftid)
|
|
}
|
|
}
|
|
|
|
tx := r.db.Begin()
|
|
if tx.Error != nil {
|
|
return tx.Error
|
|
}
|
|
|
|
// Built as a map rather than a struct: Updates() with a struct skips every
|
|
// zero value, so clearing a licence number or blanking an email would
|
|
// silently do nothing. A map says exactly what to write.
|
|
user := map[string]interface{}{
|
|
"firstname": strings.TrimSpace(rider.Firstname),
|
|
"lastname": strings.TrimSpace(rider.Lastname),
|
|
"contactno": strings.TrimSpace(rider.Contactno),
|
|
"email": strings.TrimSpace(rider.Email),
|
|
"address": rider.Address,
|
|
"suburb": rider.Suburb,
|
|
"city": rider.City,
|
|
"state": rider.State,
|
|
"postcode": rider.Postcode,
|
|
}
|
|
if rider.Shiftid != 0 {
|
|
user["shiftid"] = rider.Shiftid
|
|
}
|
|
if strings.TrimSpace(rider.Status) != "" {
|
|
user["status"] = strings.TrimSpace(rider.Status)
|
|
}
|
|
|
|
res := tx.Table("app_users").Where("userid = ? AND configid = 6", rider.Userid).Updates(user)
|
|
if res.Error != nil {
|
|
tx.Rollback()
|
|
return res.Error
|
|
}
|
|
// An UPDATE matching no rows is not an SQL error, so editing a userid that
|
|
// is not a rider would report success and change nothing.
|
|
if res.RowsAffected == 0 {
|
|
tx.Rollback()
|
|
return fmt.Errorf("rider %d not found", rider.Userid)
|
|
}
|
|
|
|
settings := map[string]interface{}{
|
|
"identificationno": rider.Identificationno,
|
|
"vehiclename": rider.Vehiclename,
|
|
"vehicleno": rider.Vehicleno,
|
|
"licenseno": rider.Licenseno,
|
|
"registrationno": rider.Registrationno,
|
|
}
|
|
if rider.Shiftid != 0 {
|
|
settings["shiftid"] = rider.Shiftid
|
|
}
|
|
if err := tx.Table("ridersettings").Where("userid = ?", rider.Userid).Updates(settings).Error; err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
return tx.Commit().Error
|
|
}
|