package service import ( "database/sql" "time" "github.com/daniil/homelab-api/internal/model" "github.com/daniil/homelab-api/internal/repository" ) type FinanceService struct { repo *repository.FinanceRepository } func NewFinanceService(repo *repository.FinanceRepository) *FinanceService { return &FinanceService{repo: repo} } // --- Categories --- func (s *FinanceService) ListCategories(userID int64) ([]model.FinanceCategory, error) { // Auto-seed if user has no categories has, err := s.repo.HasCategories(userID) if err != nil { return nil, err } if !has { if err := s.repo.SeedDefaultCategories(userID); err != nil { return nil, err } } cats, err := s.repo.ListCategories(userID) if err != nil { return nil, err } if cats == nil { cats = []model.FinanceCategory{} } return cats, nil } func (s *FinanceService) CreateCategory(userID int64, req *model.CreateFinanceCategoryRequest) (*model.FinanceCategory, error) { cat := &model.FinanceCategory{ UserID: userID, Name: req.Name, Emoji: req.Emoji, Type: req.Type, Color: defaultString(req.Color, "#6366f1"), SortOrder: req.SortOrder, } if req.Budget != nil { cat.Budget = sql.NullFloat64{Float64: *req.Budget, Valid: true} } if err := s.repo.CreateCategory(cat); err != nil { return nil, err } cat.ProcessForJSON() return cat, nil } func (s *FinanceService) UpdateCategory(id, userID int64, req *model.UpdateFinanceCategoryRequest) (*model.FinanceCategory, error) { cat, err := s.repo.GetCategory(id, userID) if err != nil { return nil, err } if req.Name != nil { cat.Name = *req.Name } if req.Emoji != nil { cat.Emoji = *req.Emoji } if req.Type != nil { cat.Type = *req.Type } if req.Budget != nil { cat.Budget = sql.NullFloat64{Float64: *req.Budget, Valid: true} } if req.Color != nil { cat.Color = *req.Color } if req.SortOrder != nil { cat.SortOrder = *req.SortOrder } if err := s.repo.UpdateCategory(cat); err != nil { return nil, err } cat.ProcessForJSON() return cat, nil } func (s *FinanceService) DeleteCategory(id, userID int64) error { return s.repo.DeleteCategory(id, userID) } // --- Transactions --- func (s *FinanceService) ListTransactions(userID int64, month, year int, categoryID *int64, txType, search string, limit, offset int) ([]model.FinanceTransaction, error) { txs, err := s.repo.ListTransactions(userID, month, year, categoryID, txType, search, limit, offset) if err != nil { return nil, err } if txs == nil { txs = []model.FinanceTransaction{} } return txs, nil } func (s *FinanceService) CreateTransaction(userID int64, req *model.CreateFinanceTransactionRequest) (*model.FinanceTransaction, error) { date, err := time.Parse("2006-01-02", req.Date) if err != nil { date = time.Now() } tx := &model.FinanceTransaction{ UserID: userID, CategoryID: req.CategoryID, Type: req.Type, Amount: req.Amount, Description: req.Description, Date: date, } if err := s.repo.CreateTransaction(tx); err != nil { return nil, err } return tx, nil } func (s *FinanceService) UpdateTransaction(id, userID int64, req *model.UpdateFinanceTransactionRequest) (*model.FinanceTransaction, error) { tx, err := s.repo.GetTransaction(id, userID) if err != nil { return nil, err } if req.CategoryID != nil { tx.CategoryID = *req.CategoryID } if req.Type != nil { tx.Type = *req.Type } if req.Amount != nil { tx.Amount = *req.Amount } if req.Description != nil { tx.Description = *req.Description } if req.Date != nil { parsed, err := time.Parse("2006-01-02", *req.Date) if err == nil { tx.Date = parsed } } if err := s.repo.UpdateTransaction(tx); err != nil { return nil, err } return tx, nil } func (s *FinanceService) DeleteTransaction(id, userID int64) error { return s.repo.DeleteTransaction(id, userID) } func (s *FinanceService) GetSummary(userID int64, month, year int) (*model.FinanceSummary, error) { return s.repo.GetSummary(userID, month, year) } func (s *FinanceService) GetAnalytics(userID int64, months int) (*model.FinanceAnalytics, error) { return s.repo.GetAnalytics(userID, months) }