feat(savings): Add savings module with categories, transactions, recurring plans
- Categories: regular, deposits, credits, recurring, multi-user, accounts - Transactions: deposits and withdrawals with user tracking - Recurring plans: monthly payment obligations per user - Stats: overdues calculation with allocation algorithm - Excludes is_account categories from total sums - Documentation: docs/SAVINGS.md
This commit is contained in:
@@ -13,20 +13,22 @@ import (
|
||||
)
|
||||
|
||||
type Scheduler struct {
|
||||
cron *cron.Cron
|
||||
bot *bot.Bot
|
||||
userRepo *repository.UserRepository
|
||||
taskRepo *repository.TaskRepository
|
||||
habitRepo *repository.HabitRepository
|
||||
cron *cron.Cron
|
||||
bot *bot.Bot
|
||||
userRepo *repository.UserRepository
|
||||
taskRepo *repository.TaskRepository
|
||||
habitRepo *repository.HabitRepository
|
||||
freezeRepo *repository.HabitFreezeRepository
|
||||
}
|
||||
|
||||
func New(b *bot.Bot, userRepo *repository.UserRepository, taskRepo *repository.TaskRepository, habitRepo *repository.HabitRepository) *Scheduler {
|
||||
func New(b *bot.Bot, userRepo *repository.UserRepository, taskRepo *repository.TaskRepository, habitRepo *repository.HabitRepository, freezeRepo *repository.HabitFreezeRepository) *Scheduler {
|
||||
return &Scheduler{
|
||||
cron: cron.New(),
|
||||
bot: b,
|
||||
userRepo: userRepo,
|
||||
taskRepo: taskRepo,
|
||||
habitRepo: habitRepo,
|
||||
cron: cron.New(),
|
||||
bot: b,
|
||||
userRepo: userRepo,
|
||||
taskRepo: taskRepo,
|
||||
habitRepo: habitRepo,
|
||||
freezeRepo: freezeRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,19 +119,13 @@ func (s *Scheduler) sendMorningBriefing(userID, chatID int64, loc *time.Location
|
||||
return
|
||||
}
|
||||
|
||||
// Filter habits for today
|
||||
// Filter habits for today (excluding frozen ones)
|
||||
weekday := int(time.Now().In(loc).Weekday())
|
||||
today := time.Now().In(loc).Truncate(24 * time.Hour)
|
||||
var todayHabits int
|
||||
for _, habit := range habits {
|
||||
if habit.Frequency == "daily" {
|
||||
if s.shouldShowHabitToday(habit, userID, weekday, today) {
|
||||
todayHabits++
|
||||
} else {
|
||||
for _, day := range habit.TargetDays {
|
||||
if day == weekday {
|
||||
todayHabits++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,25 +181,14 @@ func (s *Scheduler) sendEveningSummary(userID, chatID int64, loc *time.Location)
|
||||
return
|
||||
}
|
||||
|
||||
// Filter and count today's habits
|
||||
// Filter and count today's habits (excluding frozen ones)
|
||||
weekday := int(time.Now().In(loc).Weekday())
|
||||
today := time.Now().In(loc).Truncate(24 * time.Hour)
|
||||
var completedHabits, incompleteHabits int
|
||||
var incompleteHabitNames []string
|
||||
|
||||
for _, habit := range habits {
|
||||
isToday := false
|
||||
if habit.Frequency == "daily" {
|
||||
isToday = true
|
||||
} else {
|
||||
for _, day := range habit.TargetDays {
|
||||
if day == weekday {
|
||||
isToday = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isToday {
|
||||
if s.shouldShowHabitToday(habit, userID, weekday, today) {
|
||||
completed, _ := s.habitRepo.IsHabitCompletedToday(habit.ID, userID)
|
||||
if completed {
|
||||
completedHabits++
|
||||
@@ -313,17 +298,37 @@ func (s *Scheduler) checkHabitReminders(userID, chatID int64, currentTime string
|
||||
return
|
||||
}
|
||||
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
|
||||
for _, habit := range habits {
|
||||
if habit.UserID != userID {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if habit is frozen today
|
||||
frozen, err := s.freezeRepo.IsHabitFrozenOnDate(habit.ID, today)
|
||||
if err != nil {
|
||||
log.Printf("Scheduler: error checking freeze for habit %d: %v", habit.ID, err)
|
||||
continue
|
||||
}
|
||||
if frozen {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if already completed today
|
||||
completed, _ := s.habitRepo.IsHabitCompletedToday(habit.ID, userID)
|
||||
if completed {
|
||||
continue
|
||||
}
|
||||
|
||||
// For interval habits, check if it should be shown today
|
||||
if (habit.Frequency == "interval" || habit.Frequency == "custom") && habit.TargetCount > 0 {
|
||||
shouldShow, err := s.habitRepo.ShouldShowIntervalHabitToday(habit.ID, userID, habit.TargetCount, habit.StartDate)
|
||||
if err != nil || !shouldShow {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("⏰ <b>Напоминание о привычке:</b>\n\n%s <b>%s</b>", habit.Icon, habit.Name)
|
||||
if habit.Description != "" {
|
||||
text += fmt.Sprintf("\n<i>%s</i>", habit.Description)
|
||||
@@ -339,3 +344,40 @@ func (s *Scheduler) checkHabitReminders(userID, chatID int64, currentTime string
|
||||
s.bot.SendMessageWithKeyboard(chatID, text, &keyboard)
|
||||
}
|
||||
}
|
||||
|
||||
// shouldShowHabitToday checks if a habit should be shown today based on its frequency and freeze status
|
||||
func (s *Scheduler) shouldShowHabitToday(habit model.Habit, userID int64, weekday int, today time.Time) bool {
|
||||
// Check if habit is frozen today
|
||||
frozen, err := s.freezeRepo.IsHabitFrozenOnDate(habit.ID, today)
|
||||
if err != nil {
|
||||
log.Printf("Scheduler: error checking freeze for habit %d: %v", habit.ID, err)
|
||||
return false
|
||||
}
|
||||
if frozen {
|
||||
return false
|
||||
}
|
||||
|
||||
if habit.Frequency == "daily" {
|
||||
return true
|
||||
}
|
||||
|
||||
if habit.Frequency == "weekly" {
|
||||
for _, day := range habit.TargetDays {
|
||||
if day == weekday {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// For interval habits
|
||||
if (habit.Frequency == "interval" || habit.Frequency == "custom") && habit.TargetCount > 0 {
|
||||
shouldShow, err := s.habitRepo.ShouldShowIntervalHabitToday(habit.ID, userID, habit.TargetCount, habit.StartDate)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return shouldShow
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user