package handler import ( "context" "fmt" "time" "github.com/go-telegram/bot/models" "worktimeBot/internal/domain" ) // -- Command -- func (h *Handler) handleSalary(ctx context.Context, msg *models.Message, user *domain.User) { text, err := h.buildSalaryEstimate(user) if err != nil { h.sendText(ctx, msg.Chat.ID, err.Error()) return } h.sendWithKB(ctx, msg.Chat.ID, text, backKeyboard()) } // -- Callback -- func (h *Handler) salaryCallback(ctx context.Context, chatID int64, msgID int, cbID string, user *domain.User) { defer h.answerCb(ctx, cbID) text, err := h.buildSalaryEstimate(user) if err != nil { text = err.Error() } kb := backKeyboard() h.editText(ctx, chatID, msgID, text, &kb) } // -- Builders -- func (h *Handler) buildSalaryEstimate(user *domain.User) (string, error) { st, err := h.Repo.GetOrCreateSettings(user.ID) if err != nil { return "", err } if st.HourlyRate <= 0 { return "No hourly rate configured.\nUse /setrate to set your rate.", nil } loc := h.loadLocation(user.Timezone) now := time.Now().In(loc) calY, calM := now.Year(), int(now.Month()) switch user.Calendar { case "jalali": calY, calM, _ = domain.GregorianToJalali(calY, calM, now.Day()) case "hijri": calY, calM, _ = domain.GregorianToHijri(calY, calM, now.Day()) } startStr, endStr := domain.MonthGregorianRange(user.Calendar, calY, calM) days, err := h.Repo.GetUserDaysInRange(user.ID, startStr, endStr) if err != nil { return "", err } est := h.computeMonthlySalary(days, user.ID, st.HourlyRate, loc) est.Currency = st.Currency est.HourlyRate = st.HourlyRate title := domain.FormatMonthTitle(user.Calendar, calY, calM) return fmt.Sprintf("Salary estimate for %s\n%s\nRate: %s%.2f/hr\nDays worked: %d\nHours: %.1f", title, est.FormatSalary(st.Currency), domain.CurrencySymbol(st.Currency), st.HourlyRate, est.WorkDays, est.WorkHours), nil } func (h *Handler) computeMonthlySalary(days []domain.Day, userID int64, hourlyRate float64, loc *time.Location) domain.SalaryEstimate { var totalWork int64 workDayCount := 0 for _, day := range days { if day.IsDayOff { continue } events, err := h.Repo.EventsForDayByDayID(day.ID) if err != nil || len(events) == 0 { continue } y, m, d := domain.ParseGregorianDateKey(day.Date) dayStart := time.Date(y, time.Month(m), d, 0, 0, 0, 0, loc).Unix() dayEnd := time.Date(y, time.Month(m), d, 23, 59, 59, 0, loc).Unix() totals := domain.ComputeDailyTotals(events, day.MinBreakThreshold, dayStart, dayEnd) if totals.TotalSeconds > 0 { totalWork += totals.TotalSeconds workDayCount++ } } hours := float64(totalWork) / domain.SecondsPerHour gross := domain.ComputeDailySalary(totalWork, hourlyRate) return domain.SalaryEstimate{ WorkSeconds: totalWork, WorkHours: hours, GrossEarning: gross, WorkDays: workDayCount, } }