init
This commit is contained in:
169
internal/repository/budget_repository.go
Normal file
169
internal/repository/budget_repository.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"accounting-app/internal/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Common repository errors
|
||||
var (
|
||||
ErrBudgetNotFound = errors.New("budget not found")
|
||||
ErrBudgetInUse = errors.New("budget is in use and cannot be deleted")
|
||||
)
|
||||
|
||||
// BudgetRepository handles database operations for budgets
|
||||
type BudgetRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewBudgetRepository creates a new BudgetRepository instance
|
||||
func NewBudgetRepository(db *gorm.DB) *BudgetRepository {
|
||||
return &BudgetRepository{db: db}
|
||||
}
|
||||
|
||||
// Create creates a new budget in the database
|
||||
func (r *BudgetRepository) Create(budget *models.Budget) error {
|
||||
if err := r.db.Create(budget).Error; err != nil {
|
||||
return fmt.Errorf("failed to create budget: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByID retrieves a budget by its ID
|
||||
func (r *BudgetRepository) GetByID(userID uint, id uint) (*models.Budget, error) {
|
||||
var budget models.Budget
|
||||
if err := r.db.Preload("Category").Preload("Account").Where("user_id = ?", userID).First(&budget, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrBudgetNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get budget: %w", err)
|
||||
}
|
||||
return &budget, nil
|
||||
}
|
||||
|
||||
// GetAll retrieves all budgets for a user
|
||||
func (r *BudgetRepository) GetAll(userID uint) ([]models.Budget, error) {
|
||||
var budgets []models.Budget
|
||||
if err := r.db.Preload("Category").Preload("Account").Where("user_id = ?", userID).Order("created_at DESC").Find(&budgets).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get budgets: %w", err)
|
||||
}
|
||||
return budgets, nil
|
||||
}
|
||||
|
||||
// Update updates an existing budget in the database
|
||||
func (r *BudgetRepository) Update(budget *models.Budget) error {
|
||||
// First check if the budget exists
|
||||
var existing models.Budget
|
||||
if err := r.db.Where("user_id = ?", budget.UserID).First(&existing, budget.ID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrBudgetNotFound
|
||||
}
|
||||
return fmt.Errorf("failed to check budget existence: %w", err)
|
||||
}
|
||||
|
||||
// Update the budget
|
||||
if err := r.db.Save(budget).Error; err != nil {
|
||||
return fmt.Errorf("failed to update budget: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes a budget by its ID
|
||||
func (r *BudgetRepository) Delete(userID uint, id uint) error {
|
||||
// First check if the budget exists
|
||||
var budget models.Budget
|
||||
if err := r.db.Where("user_id = ?", userID).First(&budget, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrBudgetNotFound
|
||||
}
|
||||
return fmt.Errorf("failed to check budget existence: %w", err)
|
||||
}
|
||||
|
||||
// Delete the budget (soft delete due to gorm.DeletedAt field)
|
||||
if err := r.db.Delete(&budget).Error; err != nil {
|
||||
return fmt.Errorf("failed to delete budget: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByCategoryID retrieves all budgets for a specific category and user
|
||||
func (r *BudgetRepository) GetByCategoryID(userID, categoryID uint) ([]models.Budget, error) {
|
||||
var budgets []models.Budget
|
||||
if err := r.db.Preload("Category").Preload("Account").Where("user_id = ? AND category_id = ?", userID, categoryID).Order("created_at DESC").Find(&budgets).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get budgets by category: %w", err)
|
||||
}
|
||||
return budgets, nil
|
||||
}
|
||||
|
||||
// GetByAccountID retrieves all budgets for a specific account and user
|
||||
func (r *BudgetRepository) GetByAccountID(userID, accountID uint) ([]models.Budget, error) {
|
||||
var budgets []models.Budget
|
||||
if err := r.db.Preload("Category").Preload("Account").Where("user_id = ? AND account_id = ?", userID, accountID).Order("created_at DESC").Find(&budgets).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get budgets by account: %w", err)
|
||||
}
|
||||
return budgets, nil
|
||||
}
|
||||
|
||||
// GetByPeriodType retrieves all budgets of a specific period type for a user
|
||||
func (r *BudgetRepository) GetByPeriodType(userID uint, periodType models.PeriodType) ([]models.Budget, error) {
|
||||
var budgets []models.Budget
|
||||
if err := r.db.Preload("Category").Preload("Account").Where("user_id = ? AND period_type = ?", userID, periodType).Order("created_at DESC").Find(&budgets).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get budgets by period type: %w", err)
|
||||
}
|
||||
return budgets, nil
|
||||
}
|
||||
|
||||
// GetActiveBudgets retrieves all budgets that are currently active for a user
|
||||
func (r *BudgetRepository) GetActiveBudgets(userID uint, currentDate time.Time) ([]models.Budget, error) {
|
||||
var budgets []models.Budget
|
||||
query := r.db.Preload("Category").Preload("Account").
|
||||
Where("user_id = ?", userID).
|
||||
Where("start_date <= ?", currentDate).
|
||||
Where("end_date IS NULL OR end_date >= ?", currentDate).
|
||||
Order("created_at DESC")
|
||||
|
||||
if err := query.Find(&budgets).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get active budgets: %w", err)
|
||||
}
|
||||
return budgets, nil
|
||||
}
|
||||
|
||||
// GetSpentAmount calculates the total spent amount for a budget within a specific date range
|
||||
func (r *BudgetRepository) GetSpentAmount(budget *models.Budget, startDate, endDate time.Time) (float64, error) {
|
||||
var totalSpent float64
|
||||
|
||||
query := r.db.Model(&models.Transaction{}).
|
||||
Where("user_id = ?", budget.UserID).
|
||||
Where("type = ?", models.TransactionTypeExpense).
|
||||
Where("transaction_date >= ? AND transaction_date <= ?", startDate, endDate)
|
||||
|
||||
// Filter by category if specified
|
||||
if budget.CategoryID != nil {
|
||||
query = query.Where("category_id = ?", *budget.CategoryID)
|
||||
}
|
||||
|
||||
// Filter by account if specified
|
||||
if budget.AccountID != nil {
|
||||
query = query.Where("account_id = ?", *budget.AccountID)
|
||||
}
|
||||
|
||||
if err := query.Select("COALESCE(SUM(amount), 0)").Scan(&totalSpent).Error; err != nil {
|
||||
return 0, fmt.Errorf("failed to calculate spent amount: %w", err)
|
||||
}
|
||||
|
||||
return totalSpent, nil
|
||||
}
|
||||
|
||||
// ExistsByID checks if a budget with the given ID exists for a user
|
||||
func (r *BudgetRepository) ExistsByID(userID, id uint) (bool, error) {
|
||||
var count int64
|
||||
if err := r.db.Model(&models.Budget{}).Where("user_id = ? AND id = ?", userID, id).Count(&count).Error; err != nil {
|
||||
return false, fmt.Errorf("failed to check budget existence: %w", err)
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
Reference in New Issue
Block a user