94 lines
3.4 KiB
Go
94 lines
3.4 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"botpontos/internal/domain"
|
|
)
|
|
|
|
func scheduleCols(p string) string {
|
|
return fmt.Sprintf(`%[1]suser_id, %[1]slunch_minutes, %[1]sdaily_work_minutes, %[1]sentrada2_lead_minutes,
|
|
%[1]ssaida2_lead_minutes,
|
|
to_char(%[1]squery_window_start, 'HH24:MI'), to_char(%[1]squery_window_end, 'HH24:MI'),
|
|
to_char(%[1]sday_check_deadline, 'HH24:MI'), to_char(%[1]sno_points_recheck_until, 'HH24:MI')`, p)
|
|
}
|
|
|
|
type scheduleScan struct {
|
|
s domain.WorkSchedule
|
|
start, end, deadline, recheckUntil *string
|
|
}
|
|
|
|
func (s *scheduleScan) dest() []any {
|
|
return []any{&s.s.UserID, &s.s.LunchMinutes, &s.s.DailyWorkMinutes, &s.s.Entrada2LeadMinutes,
|
|
&s.s.Saida2LeadMinutes, &s.start, &s.end, &s.deadline, &s.recheckUntil}
|
|
}
|
|
|
|
func (s *scheduleScan) result() (domain.WorkSchedule, error) {
|
|
out := s.s
|
|
if s.start == nil || s.end == nil || s.deadline == nil || s.recheckUntil == nil {
|
|
return out, nil
|
|
}
|
|
var w domain.QueryWindow
|
|
var err error
|
|
if w.Start, err = domain.ParseClock(*s.start); err != nil {
|
|
return out, err
|
|
}
|
|
if w.End, err = domain.ParseClock(*s.end); err != nil {
|
|
return out, err
|
|
}
|
|
if w.DayCheckDeadline, err = domain.ParseClock(*s.deadline); err != nil {
|
|
return out, err
|
|
}
|
|
if w.NoPointsRecheckUntil, err = domain.ParseClock(*s.recheckUntil); err != nil {
|
|
return out, err
|
|
}
|
|
out.Window = &w
|
|
return out, nil
|
|
}
|
|
|
|
// GetSchedule devolve a jornada do usuário ou a padrão, se ainda não houver linha.
|
|
func (s *Store) GetSchedule(ctx context.Context, userID int64) (domain.WorkSchedule, error) {
|
|
var ss scheduleScan
|
|
err := s.pool.QueryRow(ctx, `SELECT `+scheduleCols("")+` FROM work_schedules WHERE user_id = $1`, userID).Scan(ss.dest()...)
|
|
if err != nil {
|
|
if mapErr(err) == domain.ErrNotFound {
|
|
return domain.DefaultSchedule(userID), nil
|
|
}
|
|
return domain.WorkSchedule{}, err
|
|
}
|
|
return ss.result()
|
|
}
|
|
|
|
func (s *Store) SaveSchedule(ctx context.Context, sch domain.WorkSchedule) error {
|
|
var start, end, deadline, recheck *string
|
|
if w := sch.Window; w != nil {
|
|
start, end = strPtr(w.Start.String()), strPtr(w.End.String())
|
|
deadline, recheck = strPtr(w.DayCheckDeadline.String()), strPtr(w.NoPointsRecheckUntil.String())
|
|
}
|
|
_, err := s.pool.Exec(ctx, `
|
|
INSERT INTO work_schedules (user_id, lunch_minutes, daily_work_minutes, entrada2_lead_minutes, saida2_lead_minutes,
|
|
query_window_start, query_window_end, day_check_deadline, no_points_recheck_until)
|
|
VALUES ($1, $2, $3, $4, $5, $6::text::time, $7::text::time, $8::text::time, $9::text::time)
|
|
ON CONFLICT (user_id) DO UPDATE SET
|
|
lunch_minutes = EXCLUDED.lunch_minutes,
|
|
daily_work_minutes = EXCLUDED.daily_work_minutes,
|
|
entrada2_lead_minutes = EXCLUDED.entrada2_lead_minutes,
|
|
saida2_lead_minutes = EXCLUDED.saida2_lead_minutes,
|
|
query_window_start = EXCLUDED.query_window_start,
|
|
query_window_end = EXCLUDED.query_window_end,
|
|
day_check_deadline = EXCLUDED.day_check_deadline,
|
|
no_points_recheck_until = EXCLUDED.no_points_recheck_until`,
|
|
sch.UserID, sch.LunchMinutes, sch.DailyWorkMinutes, sch.Entrada2LeadMinutes, sch.Saida2LeadMinutes,
|
|
start, end, deadline, recheck)
|
|
return mapErr(err)
|
|
}
|
|
|
|
func (s *Store) CountCustomWindows(ctx context.Context) (int, error) {
|
|
var n int
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT count(*) FROM work_schedules ws JOIN users u ON u.id = ws.user_id
|
|
WHERE ws.query_window_start IS NOT NULL AND u.status = 'active'`).Scan(&n)
|
|
return n, err
|
|
}
|