pos login edited with phone number

This commit is contained in:
2026-08-11 11:08:43 +05:30
parent 3531c656d4
commit d35caf34d3
12 changed files with 768 additions and 25 deletions

View File

@@ -0,0 +1,170 @@
package repositories
import (
"fmt"
"regexp"
"strings"
"nearle/models"
)
// Shift windows for till staff.
//
// Scoped by tenant *and* outlet in every statement rather than checked first,
// the same shape the till-user queries use: a console naming somebody else's
// shift id updates no rows and is told so, instead of quietly editing another
// shop's hours.
var posTimeOfDay = regexp.MustCompile(`^([01]\d|2[0-3]):[0-5]\d$`)
// normaliseShiftTime accepts what a time input actually sends.
//
// `<input type="time">` gives "07:00", some browsers and most hand-typed values
// give "07:00:00", and `ridershifts` already stores the seconds form. Both are
// reduced to `HH:MM` so a shift written by one client reads the same to another.
func normaliseShiftTime(raw string) (string, error) {
t := strings.TrimSpace(raw)
if t == "" {
return "", fmt.Errorf("a start and end time are required")
}
if len(t) == 8 && strings.Count(t, ":") == 2 {
t = t[:5]
}
if !posTimeOfDay.MatchString(t) {
return "", fmt.Errorf("time must be 24-hour HH:MM; got %q", raw)
}
return t, nil
}
// ListStaffShifts returns an outlet's shifts, newest last so a picker reads in
// the order they were created rather than alphabetically by name.
func (r *posRepository) ListStaffShifts(tenantID, locationID int, includeInactive bool) ([]models.StaffShifts, error) {
if tenantID <= 0 || locationID <= 0 {
return nil, fmt.Errorf("tenantid and locationid are required")
}
shifts := make([]models.StaffShifts, 0)
query := `SELECT * FROM staffshifts WHERE tenantid = ? AND locationid = ?`
if !includeInactive {
query += ` AND LOWER(COALESCE(status,'active')) <> 'inactive'`
}
query += ` ORDER BY staffshiftid`
if err := r.db.Raw(query, tenantID, locationID).Scan(&shifts).Error; err != nil {
return nil, err
}
return shifts, nil
}
// CreateStaffShift adds a window at one outlet.
func (r *posRepository) CreateStaffShift(tenantID, locationID int, req models.StaffShifts) (*models.StaffShifts, error) {
if tenantID <= 0 || locationID <= 0 {
return nil, fmt.Errorf("tenantid and locationid are required")
}
name := strings.TrimSpace(req.Name)
if name == "" {
return nil, fmt.Errorf("a shift name is required")
}
start, err := normaliseShiftTime(req.Starttime)
if err != nil {
return nil, err
}
end, err := normaliseShiftTime(req.Endtime)
if err != nil {
return nil, err
}
// An end before a start is allowed on purpose — a night shift runs 22:00 to
// 06:00 and wrapping midnight is ordinary in retail. Only the equal case is
// refused, because a zero-length window cannot be what anyone meant.
if start == end {
return nil, fmt.Errorf("a shift cannot start and end at the same time")
}
weekdays := strings.TrimSpace(req.Weekdays)
if weekdays != "" && !regexp.MustCompile(`^[01]{7}$`).MatchString(weekdays) {
return nil, fmt.Errorf("weekdays must be seven 0/1 characters starting Monday, e.g. 1111100")
}
shift := models.StaffShifts{
Tenantid: tenantID,
Locationid: locationID,
Name: name,
Starttime: start,
Endtime: end,
Weekdays: weekdays,
Status: "Active",
}
if err := r.db.Table("staffshifts").Create(&shift).Error; err != nil {
return nil, err
}
return &shift, nil
}
// UpdateStaffShift edits a window. Every field is optional; only the ones sent
// are written, matching how the till-user update behaves.
func (r *posRepository) UpdateStaffShift(tenantID, locationID int, req models.StaffShifts) (*models.StaffShifts, error) {
if req.Staffshiftid <= 0 {
return nil, fmt.Errorf("staff_shift_id is required")
}
sets := []string{}
args := []interface{}{}
if name := strings.TrimSpace(req.Name); name != "" {
sets = append(sets, "name = ?")
args = append(args, name)
}
if strings.TrimSpace(req.Starttime) != "" {
t, err := normaliseShiftTime(req.Starttime)
if err != nil {
return nil, err
}
sets = append(sets, "starttime = ?")
args = append(args, t)
}
if strings.TrimSpace(req.Endtime) != "" {
t, err := normaliseShiftTime(req.Endtime)
if err != nil {
return nil, err
}
sets = append(sets, "endtime = ?")
args = append(args, t)
}
if w := strings.TrimSpace(req.Weekdays); w != "" {
if !regexp.MustCompile(`^[01]{7}$`).MatchString(w) {
return nil, fmt.Errorf("weekdays must be seven 0/1 characters starting Monday")
}
sets = append(sets, "weekdays = ?")
args = append(args, w)
}
if s := strings.TrimSpace(req.Status); s != "" {
sets = append(sets, "status = ?")
args = append(args, s)
}
if len(sets) == 0 {
return nil, fmt.Errorf("nothing to change")
}
sets = append(sets, "updated = NOW()")
args = append(args, req.Staffshiftid, tenantID, locationID)
res := r.db.Exec(
fmt.Sprintf(`UPDATE staffshifts SET %s WHERE staffshiftid = ? AND tenantid = ? AND locationid = ?`,
strings.Join(sets, ", ")), args...)
if res.Error != nil {
return nil, res.Error
}
if res.RowsAffected == 0 {
return nil, fmt.Errorf("no shift %d at this outlet", req.Staffshiftid)
}
var out models.StaffShifts
if err := r.db.Raw(`SELECT * FROM staffshifts WHERE staffshiftid = ?`, req.Staffshiftid).Scan(&out).Error; err != nil {
return nil, err
}
return &out, nil
}