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:
@@ -61,6 +61,39 @@ func (b *Bot) handleCallback(callback *tgbotapi.CallbackQuery) {
|
||||
}
|
||||
|
||||
action := parts[0]
|
||||
|
||||
// Handle checkhabit with optional date (checkhabit_<id> or checkhabit_<id>_yesterday)
|
||||
if action == "checkhabit" {
|
||||
id, _ := strconv.ParseInt(parts[1], 10, 64)
|
||||
|
||||
logDate := time.Now()
|
||||
dateLabel := "сегодня"
|
||||
if len(parts) >= 3 && parts[2] == "yesterday" {
|
||||
logDate = time.Now().AddDate(0, 0, -1)
|
||||
dateLabel = "вчера"
|
||||
}
|
||||
|
||||
log := &model.HabitLog{
|
||||
HabitID: id,
|
||||
UserID: user.ID,
|
||||
Date: logDate,
|
||||
Count: 1,
|
||||
}
|
||||
err = b.habitRepo.CreateLog(log)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "duplicate") || strings.Contains(err.Error(), "already") {
|
||||
b.answerCallback(callback.ID, "⚠️ Уже отмечено за эту дату")
|
||||
} else {
|
||||
b.answerCallback(callback.ID, "❌ Ошибка")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
b.answerCallback(callback.ID, fmt.Sprintf("✅ Отмечено за %s!", dateLabel))
|
||||
b.refreshHabitsMessage(chatID, messageID, user.ID)
|
||||
return
|
||||
}
|
||||
|
||||
id, _ := strconv.ParseInt(parts[1], 10, 64)
|
||||
|
||||
switch action {
|
||||
@@ -81,21 +114,6 @@ func (b *Bot) handleCallback(callback *tgbotapi.CallbackQuery) {
|
||||
}
|
||||
b.answerCallback(callback.ID, "🗑 Задача удалена")
|
||||
b.refreshTasksMessage(chatID, messageID, user.ID)
|
||||
|
||||
case "checkhabit":
|
||||
log := &model.HabitLog{
|
||||
HabitID: id,
|
||||
UserID: user.ID,
|
||||
Date: time.Now(),
|
||||
Count: 1,
|
||||
}
|
||||
err = b.habitRepo.CreateLog(log)
|
||||
if err != nil {
|
||||
b.answerCallback(callback.ID, "❌ Ошибка")
|
||||
return
|
||||
}
|
||||
b.answerCallback(callback.ID, "✅ Привычка отмечена!")
|
||||
b.refreshHabitsMessage(chatID, messageID, user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,11 +222,15 @@ func (b *Bot) buildHabitsMessage(habits []model.Habit, userID int64) (string, *t
|
||||
|
||||
text := "🎯 <b>Привычки на сегодня:</b>\n\n"
|
||||
var rows [][]tgbotapi.InlineKeyboardButton
|
||||
|
||||
yesterday := time.Now().AddDate(0, 0, -1).Truncate(24 * time.Hour)
|
||||
|
||||
for _, habit := range todayHabits {
|
||||
completed, _ := b.habitRepo.IsHabitCompletedToday(habit.ID, userID)
|
||||
completedToday, _ := b.habitRepo.IsHabitCompletedToday(habit.ID, userID)
|
||||
completedYesterday, _ := b.habitRepo.IsHabitCompletedOnDate(habit.ID, userID, yesterday)
|
||||
|
||||
status := "⬜"
|
||||
if completed {
|
||||
if completedToday {
|
||||
status = "✅"
|
||||
}
|
||||
|
||||
@@ -218,15 +240,31 @@ func (b *Bot) buildHabitsMessage(habits []model.Habit, userID int64) (string, *t
|
||||
}
|
||||
text += "\n"
|
||||
|
||||
if !completed {
|
||||
row := []tgbotapi.InlineKeyboardButton{
|
||||
tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("✅ %s", habit.Name), fmt.Sprintf("checkhabit_%d", habit.ID)),
|
||||
}
|
||||
rows = append(rows, row)
|
||||
// Build buttons row
|
||||
var btnRow []tgbotapi.InlineKeyboardButton
|
||||
|
||||
if !completedToday {
|
||||
btnRow = append(btnRow, tgbotapi.NewInlineKeyboardButtonData(
|
||||
fmt.Sprintf("✅ %s", habit.Name),
|
||||
fmt.Sprintf("checkhabit_%d", habit.ID),
|
||||
))
|
||||
}
|
||||
|
||||
// Add "За вчера" button if not completed yesterday
|
||||
if !completedYesterday {
|
||||
btnRow = append(btnRow, tgbotapi.NewInlineKeyboardButtonData(
|
||||
"📅 За вчера",
|
||||
fmt.Sprintf("checkhabit_%d_yesterday", habit.ID),
|
||||
))
|
||||
}
|
||||
|
||||
if len(btnRow) > 0 {
|
||||
rows = append(rows, btnRow)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
text += "\n✨ Всё выполнено!"
|
||||
return text, nil
|
||||
}
|
||||
|
||||
@@ -235,28 +273,13 @@ func (b *Bot) buildHabitsMessage(habits []model.Habit, userID int64) (string, *t
|
||||
}
|
||||
|
||||
func (b *Bot) handleStart(msg *tgbotapi.Message) {
|
||||
text := fmt.Sprintf(`👋 Привет! Я бот Pulse.
|
||||
|
||||
Твой Chat ID: <code>%d</code>
|
||||
|
||||
Скопируй его и вставь в настройках Pulse для получения уведомлений.
|
||||
|
||||
Доступные команды:
|
||||
/tasks — задачи на сегодня
|
||||
/habits — привычки на сегодня
|
||||
/help — справка`, msg.Chat.ID)
|
||||
text := fmt.Sprintf("👋 Привет! Я бот Pulse.\n\nТвой Chat ID: <code>%d</code>\n\nСкопируй его и вставь в настройках Pulse для получения уведомлений.\n\nДоступные команды:\n/tasks — задачи на сегодня\n/habits — привычки на сегодня\n/help — справка", msg.Chat.ID)
|
||||
|
||||
b.SendMessage(msg.Chat.ID, text)
|
||||
}
|
||||
|
||||
func (b *Bot) handleHelp(msg *tgbotapi.Message) {
|
||||
text := `📚 <b>Справка по командам:</b>
|
||||
|
||||
/start — получить твой Chat ID
|
||||
/tasks — список задач на сегодня
|
||||
/habits — список привычек
|
||||
|
||||
💡 Чтобы получать уведомления, добавь свой Chat ID в настройках Pulse.`
|
||||
text := "📚 <b>Справка по командам:</b>\n\n/start — получить твой Chat ID\n/tasks — список задач на сегодня\n/habits — список привычек\n\n💡 Чтобы получать уведомления, добавь свой Chat ID в настройках Pulse."
|
||||
|
||||
b.SendMessage(msg.Chat.ID, text)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user