584 lines
18 KiB
Go
584 lines
18 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"accounting-app/internal/models"
|
|
"accounting-app/internal/repository"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Service layer errors for piggy banks
|
|
var (
|
|
ErrPiggyBankNotFound = errors.New("piggy bank not found")
|
|
ErrPiggyBankInUse = errors.New("piggy bank is in use and cannot be deleted")
|
|
ErrInvalidTargetAmount = errors.New("target amount must be positive")
|
|
ErrInvalidDepositAmount = errors.New("deposit amount must be positive")
|
|
ErrInvalidWithdrawAmount = errors.New("withdraw amount must be positive")
|
|
ErrInvalidPiggyBankType = errors.New("invalid piggy bank type")
|
|
ErrInvalidAutoRule = errors.New("invalid auto rule format")
|
|
ErrInsufficientAccountFunds = errors.New("insufficient funds in linked account")
|
|
)
|
|
|
|
// PiggyBankInput represents the input data for creating or updating a piggy bank
|
|
type PiggyBankInput struct {
|
|
UserID uint `json:"user_id"`
|
|
Name string `json:"name" binding:"required"`
|
|
TargetAmount float64 `json:"target_amount" binding:"required,gt=0"`
|
|
Type models.PiggyBankType `json:"type" binding:"required"`
|
|
TargetDate *time.Time `json:"target_date,omitempty"`
|
|
LinkedAccountID *uint `json:"linked_account_id,omitempty"`
|
|
AutoRule *AutoDepositRule `json:"auto_rule,omitempty"`
|
|
}
|
|
|
|
// AutoDepositRule represents the automatic deposit rule for auto piggy banks
|
|
type AutoDepositRule struct {
|
|
Frequency string `json:"frequency"` // daily, weekly, monthly
|
|
Amount float64 `json:"amount"`
|
|
DayOfWeek *int `json:"day_of_week,omitempty"` // 0-6 for weekly
|
|
DayOfMonth *int `json:"day_of_month,omitempty"` // 1-31 for monthly
|
|
}
|
|
|
|
// DepositInput represents the input for depositing to a piggy bank
|
|
type DepositInput struct {
|
|
Amount float64 `json:"amount" binding:"required,gt=0"`
|
|
FromAccountID *uint `json:"from_account_id,omitempty"`
|
|
Note string `json:"note,omitempty"`
|
|
}
|
|
|
|
// WithdrawInput represents the input for withdrawing from a piggy bank
|
|
type WithdrawInput struct {
|
|
Amount float64 `json:"amount" binding:"required,gt=0"`
|
|
ToAccountID *uint `json:"to_account_id,omitempty"`
|
|
Note string `json:"note,omitempty"`
|
|
}
|
|
|
|
// PiggyBankProgress represents the progress of a piggy bank
|
|
type PiggyBankProgress struct {
|
|
PiggyBankID uint `json:"piggy_bank_id"`
|
|
Name string `json:"name"`
|
|
TargetAmount float64 `json:"target_amount"`
|
|
CurrentAmount float64 `json:"current_amount"`
|
|
Remaining float64 `json:"remaining"`
|
|
Progress float64 `json:"progress"` // Percentage (0-100)
|
|
Type models.PiggyBankType `json:"type"`
|
|
TargetDate *time.Time `json:"target_date,omitempty"`
|
|
IsCompleted bool `json:"is_completed"`
|
|
DaysRemaining *int `json:"days_remaining,omitempty"`
|
|
LinkedAccountID *uint `json:"linked_account_id,omitempty"`
|
|
}
|
|
|
|
// PiggyBankService handles business logic for piggy banks
|
|
type PiggyBankService struct {
|
|
repo *repository.PiggyBankRepository
|
|
accountRepo *repository.AccountRepository
|
|
db *gorm.DB
|
|
}
|
|
|
|
// NewPiggyBankService creates a new PiggyBankService instance
|
|
func NewPiggyBankService(repo *repository.PiggyBankRepository, accountRepo *repository.AccountRepository, db *gorm.DB) *PiggyBankService {
|
|
return &PiggyBankService{
|
|
repo: repo,
|
|
accountRepo: accountRepo,
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// CreatePiggyBank creates a new piggy bank with business logic validation
|
|
func (s *PiggyBankService) CreatePiggyBank(input PiggyBankInput) (*models.PiggyBank, error) {
|
|
// Validate target amount
|
|
if input.TargetAmount <= 0 {
|
|
return nil, ErrInvalidTargetAmount
|
|
}
|
|
|
|
// Validate piggy bank type
|
|
if !isValidPiggyBankType(input.Type) {
|
|
return nil, ErrInvalidPiggyBankType
|
|
}
|
|
|
|
// Validate linked account if specified
|
|
if input.LinkedAccountID != nil {
|
|
exists, err := s.accountRepo.ExistsByID(input.UserID, *input.LinkedAccountID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to check account existence: %w", err)
|
|
}
|
|
if !exists {
|
|
return nil, ErrAccountNotFound
|
|
}
|
|
}
|
|
|
|
// For auto piggy banks, validate auto rule
|
|
var autoRuleJSON string
|
|
if input.Type == models.PiggyBankTypeAuto || input.Type == models.PiggyBankTypeFixedDeposit || input.Type == models.PiggyBankTypeWeek52 {
|
|
if input.AutoRule != nil {
|
|
ruleBytes, err := json.Marshal(input.AutoRule)
|
|
if err != nil {
|
|
return nil, ErrInvalidAutoRule
|
|
}
|
|
autoRuleJSON = string(ruleBytes)
|
|
} else if input.Type == models.PiggyBankTypeAuto || input.Type == models.PiggyBankTypeFixedDeposit {
|
|
// Auto and fixed deposit types require auto rule
|
|
return nil, ErrInvalidAutoRule
|
|
}
|
|
}
|
|
|
|
// Create the piggy bank model
|
|
piggyBank := &models.PiggyBank{
|
|
UserID: input.UserID,
|
|
Name: input.Name,
|
|
TargetAmount: input.TargetAmount,
|
|
CurrentAmount: 0,
|
|
Type: input.Type,
|
|
TargetDate: input.TargetDate,
|
|
LinkedAccountID: input.LinkedAccountID,
|
|
AutoRule: autoRuleJSON,
|
|
}
|
|
|
|
// Save to database
|
|
if err := s.repo.Create(piggyBank); err != nil {
|
|
return nil, fmt.Errorf("failed to create piggy bank: %w", err)
|
|
}
|
|
|
|
return piggyBank, nil
|
|
}
|
|
|
|
// GetPiggyBank retrieves a piggy bank by ID and verifies ownership
|
|
func (s *PiggyBankService) GetPiggyBank(userID, id uint) (*models.PiggyBank, error) {
|
|
piggyBank, err := s.repo.GetByID(userID, id)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrPiggyBankNotFound) {
|
|
return nil, ErrPiggyBankNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get piggy bank: %w", err)
|
|
}
|
|
// userID check handled by repo
|
|
return piggyBank, nil
|
|
}
|
|
|
|
// GetAllPiggyBanks retrieves all piggy banks for a user
|
|
func (s *PiggyBankService) GetAllPiggyBanks(userID uint) ([]models.PiggyBank, error) {
|
|
piggyBanks, err := s.repo.GetAll(userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get piggy banks: %w", err)
|
|
}
|
|
return piggyBanks, nil
|
|
}
|
|
|
|
// UpdatePiggyBank updates an existing piggy bank after verifying ownership
|
|
func (s *PiggyBankService) UpdatePiggyBank(userID, id uint, input PiggyBankInput) (*models.PiggyBank, error) {
|
|
// Get existing piggy bank
|
|
piggyBank, err := s.repo.GetByID(userID, id)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrPiggyBankNotFound) {
|
|
return nil, ErrPiggyBankNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get piggy bank: %w", err)
|
|
}
|
|
// userID check handled by repo
|
|
|
|
// Validate target amount
|
|
if input.TargetAmount <= 0 {
|
|
return nil, ErrInvalidTargetAmount
|
|
}
|
|
|
|
// Validate piggy bank type
|
|
if !isValidPiggyBankType(input.Type) {
|
|
return nil, ErrInvalidPiggyBankType
|
|
}
|
|
|
|
// Validate linked account if specified
|
|
if input.LinkedAccountID != nil {
|
|
exists, err := s.accountRepo.ExistsByID(userID, *input.LinkedAccountID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to check account existence: %w", err)
|
|
}
|
|
if !exists {
|
|
return nil, ErrAccountNotFound
|
|
}
|
|
}
|
|
|
|
// For auto piggy banks, validate auto rule
|
|
var autoRuleJSON string
|
|
if input.Type == models.PiggyBankTypeAuto || input.Type == models.PiggyBankTypeFixedDeposit || input.Type == models.PiggyBankTypeWeek52 {
|
|
if input.AutoRule != nil {
|
|
ruleBytes, err := json.Marshal(input.AutoRule)
|
|
if err != nil {
|
|
return nil, ErrInvalidAutoRule
|
|
}
|
|
autoRuleJSON = string(ruleBytes)
|
|
} else if input.Type == models.PiggyBankTypeAuto || input.Type == models.PiggyBankTypeFixedDeposit {
|
|
// Auto and fixed deposit types require auto rule
|
|
return nil, ErrInvalidAutoRule
|
|
}
|
|
}
|
|
|
|
// Update fields
|
|
piggyBank.Name = input.Name
|
|
piggyBank.TargetAmount = input.TargetAmount
|
|
piggyBank.Type = input.Type
|
|
piggyBank.TargetDate = input.TargetDate
|
|
piggyBank.LinkedAccountID = input.LinkedAccountID
|
|
piggyBank.AutoRule = autoRuleJSON
|
|
|
|
// Save to database
|
|
if err := s.repo.Update(piggyBank); err != nil {
|
|
return nil, fmt.Errorf("failed to update piggy bank: %w", err)
|
|
}
|
|
|
|
return piggyBank, nil
|
|
}
|
|
|
|
// DeletePiggyBank deletes a piggy bank by ID after verifying ownership
|
|
func (s *PiggyBankService) DeletePiggyBank(userID, id uint) error {
|
|
_, err := s.repo.GetByID(userID, id)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrPiggyBankNotFound) {
|
|
return ErrPiggyBankNotFound
|
|
}
|
|
return fmt.Errorf("failed to check piggy bank existence: %w", err)
|
|
}
|
|
// userID check handled by repo
|
|
|
|
err = s.repo.Delete(userID, id)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrPiggyBankNotFound) {
|
|
return ErrPiggyBankNotFound
|
|
}
|
|
if errors.Is(err, repository.ErrPiggyBankInUse) {
|
|
return ErrPiggyBankInUse
|
|
}
|
|
return fmt.Errorf("failed to delete piggy bank: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Deposit adds money to a piggy bank
|
|
// If fromAccountID is provided, it will deduct from that account
|
|
func (s *PiggyBankService) Deposit(userID, id uint, input DepositInput) (*models.PiggyBank, error) {
|
|
// Validate deposit amount
|
|
if input.Amount <= 0 {
|
|
return nil, ErrInvalidDepositAmount
|
|
}
|
|
|
|
// Start a transaction
|
|
tx := s.db.Begin()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
// Get the piggy bank using the transaction
|
|
var piggyBank models.PiggyBank
|
|
if err := tx.Preload("LinkedAccount").First(&piggyBank, id).Error; err != nil {
|
|
tx.Rollback()
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrPiggyBankNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get piggy bank: %w", err)
|
|
}
|
|
|
|
// If fromAccountID is provided, deduct from that account
|
|
if input.FromAccountID != nil {
|
|
var account models.Account
|
|
if err := tx.Where("user_id = ?", userID).First(&account, *input.FromAccountID).Error; err != nil {
|
|
tx.Rollback()
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrAccountNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get account: %w", err)
|
|
}
|
|
|
|
// Check if account has sufficient funds (only for non-credit accounts)
|
|
if !account.IsCredit && account.Balance < input.Amount {
|
|
tx.Rollback()
|
|
return nil, ErrInsufficientAccountFunds
|
|
}
|
|
|
|
// Deduct from account
|
|
account.Balance -= input.Amount
|
|
if err := tx.Save(&account).Error; err != nil {
|
|
tx.Rollback()
|
|
return nil, fmt.Errorf("failed to update account balance: %w", err)
|
|
}
|
|
}
|
|
|
|
// Add to piggy bank
|
|
piggyBank.CurrentAmount += input.Amount
|
|
|
|
// Update piggy bank
|
|
if err := tx.Save(&piggyBank).Error; err != nil {
|
|
tx.Rollback()
|
|
return nil, fmt.Errorf("failed to update piggy bank: %w", err)
|
|
}
|
|
|
|
// Commit transaction
|
|
if err := tx.Commit().Error; err != nil {
|
|
return nil, fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
return &piggyBank, nil
|
|
}
|
|
|
|
// Withdraw removes money from a piggy bank (breaking the piggy bank)
|
|
// If toAccountID is provided, it will add to that account
|
|
func (s *PiggyBankService) Withdraw(userID, id uint, input WithdrawInput) (*models.PiggyBank, error) {
|
|
// Validate withdraw amount
|
|
if input.Amount <= 0 {
|
|
return nil, ErrInvalidWithdrawAmount
|
|
}
|
|
|
|
// Start a transaction
|
|
tx := s.db.Begin()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
// Get the piggy bank using the transaction
|
|
var piggyBank models.PiggyBank
|
|
if err := tx.Preload("LinkedAccount").Where("user_id = ?", userID).First(&piggyBank, id).Error; err != nil {
|
|
tx.Rollback()
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrPiggyBankNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get piggy bank: %w", err)
|
|
}
|
|
|
|
// Check if piggy bank has sufficient balance
|
|
if piggyBank.CurrentAmount < input.Amount {
|
|
tx.Rollback()
|
|
return nil, ErrInsufficientBalance
|
|
}
|
|
|
|
// Deduct from piggy bank
|
|
piggyBank.CurrentAmount -= input.Amount
|
|
|
|
// Update piggy bank
|
|
if err := tx.Save(&piggyBank).Error; err != nil {
|
|
tx.Rollback()
|
|
return nil, fmt.Errorf("failed to update piggy bank: %w", err)
|
|
}
|
|
|
|
// If toAccountID is provided, add to that account
|
|
if input.ToAccountID != nil {
|
|
var account models.Account
|
|
if err := tx.Where("user_id = ?", userID).First(&account, *input.ToAccountID).Error; err != nil {
|
|
tx.Rollback()
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrAccountNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get account: %w", err)
|
|
}
|
|
|
|
// Add to account
|
|
account.Balance += input.Amount
|
|
if err := tx.Save(&account).Error; err != nil {
|
|
tx.Rollback()
|
|
return nil, fmt.Errorf("failed to update account balance: %w", err)
|
|
}
|
|
}
|
|
|
|
// Commit transaction
|
|
if err := tx.Commit().Error; err != nil {
|
|
return nil, fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
return &piggyBank, nil
|
|
}
|
|
|
|
// GetPiggyBankProgress calculates and returns the progress of a piggy bank for a user
|
|
func (s *PiggyBankService) GetPiggyBankProgress(userID, id uint) (*PiggyBankProgress, error) {
|
|
// Get the piggy bank
|
|
piggyBank, err := s.repo.GetByID(userID, id)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrPiggyBankNotFound) {
|
|
return nil, ErrPiggyBankNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get piggy bank: %w", err)
|
|
}
|
|
// userID check handled by repo
|
|
|
|
// Calculate progress metrics
|
|
remaining := piggyBank.TargetAmount - piggyBank.CurrentAmount
|
|
if remaining < 0 {
|
|
remaining = 0
|
|
}
|
|
|
|
progress := 0.0
|
|
if piggyBank.TargetAmount > 0 {
|
|
progress = (piggyBank.CurrentAmount / piggyBank.TargetAmount) * 100
|
|
if progress > 100 {
|
|
progress = 100
|
|
}
|
|
}
|
|
|
|
isCompleted := piggyBank.CurrentAmount >= piggyBank.TargetAmount
|
|
|
|
// Calculate days remaining if target date is set
|
|
var daysRemaining *int
|
|
if piggyBank.TargetDate != nil {
|
|
days := int(time.Until(*piggyBank.TargetDate).Hours() / 24)
|
|
daysRemaining = &days
|
|
}
|
|
|
|
return &PiggyBankProgress{
|
|
PiggyBankID: piggyBank.ID,
|
|
Name: piggyBank.Name,
|
|
TargetAmount: piggyBank.TargetAmount,
|
|
CurrentAmount: piggyBank.CurrentAmount,
|
|
Remaining: remaining,
|
|
Progress: progress,
|
|
Type: piggyBank.Type,
|
|
TargetDate: piggyBank.TargetDate,
|
|
IsCompleted: isCompleted,
|
|
DaysRemaining: daysRemaining,
|
|
LinkedAccountID: piggyBank.LinkedAccountID,
|
|
}, nil
|
|
}
|
|
|
|
// GetAllPiggyBankProgress returns progress for all piggy banks for a user
|
|
func (s *PiggyBankService) GetAllPiggyBankProgress(userID uint) ([]PiggyBankProgress, error) {
|
|
piggyBanks, err := s.repo.GetAll(userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get piggy banks: %w", err)
|
|
}
|
|
|
|
var progressList []PiggyBankProgress
|
|
for _, piggyBank := range piggyBanks {
|
|
progress, err := s.GetPiggyBankProgress(userID, piggyBank.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to calculate progress for piggy bank %d: %w", piggyBank.ID, err)
|
|
}
|
|
progressList = append(progressList, *progress)
|
|
}
|
|
|
|
return progressList, nil
|
|
}
|
|
|
|
// GetActivePiggyBanks retrieves all piggy banks that haven't reached their target yet for a user
|
|
func (s *PiggyBankService) GetActivePiggyBanks(userID uint) ([]models.PiggyBank, error) {
|
|
piggyBanks, err := s.repo.GetActiveGoals(userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get active piggy banks: %w", err)
|
|
}
|
|
return piggyBanks, nil
|
|
}
|
|
|
|
// GetCompletedPiggyBanks retrieves all piggy banks that have reached their target for a user
|
|
func (s *PiggyBankService) GetCompletedPiggyBanks(userID uint) ([]models.PiggyBank, error) {
|
|
piggyBanks, err := s.repo.GetCompletedGoals(userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get completed piggy banks: %w", err)
|
|
}
|
|
return piggyBanks, nil
|
|
}
|
|
|
|
// GetPiggyBanksByType retrieves all piggy banks of a specific type for a user
|
|
func (s *PiggyBankService) GetPiggyBanksByType(userID uint, piggyBankType models.PiggyBankType) ([]models.PiggyBank, error) {
|
|
piggyBanks, err := s.repo.GetByType(userID, piggyBankType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get piggy banks by type: %w", err)
|
|
}
|
|
return piggyBanks, nil
|
|
}
|
|
|
|
// ProcessAutoDeposits processes automatic deposits for all auto piggy banks of a user
|
|
// This should be called by a scheduled job
|
|
func (s *PiggyBankService) ProcessAutoDeposits(userID uint) error {
|
|
// Get all auto piggy banks
|
|
autoPiggyBanks, err := s.repo.GetByType(userID, models.PiggyBankTypeAuto)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get auto piggy banks: %w", err)
|
|
}
|
|
|
|
fixedDepositPiggyBanks, err := s.repo.GetByType(userID, models.PiggyBankTypeFixedDeposit)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get fixed deposit piggy banks: %w", err)
|
|
}
|
|
|
|
week52PiggyBanks, err := s.repo.GetByType(userID, models.PiggyBankTypeWeek52)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get week 52 piggy banks: %w", err)
|
|
}
|
|
|
|
allAutoPiggyBanks := append(autoPiggyBanks, fixedDepositPiggyBanks...)
|
|
allAutoPiggyBanks = append(allAutoPiggyBanks, week52PiggyBanks...)
|
|
|
|
now := time.Now()
|
|
|
|
for _, piggyBank := range allAutoPiggyBanks {
|
|
// Skip if already completed
|
|
if piggyBank.CurrentAmount >= piggyBank.TargetAmount {
|
|
continue
|
|
}
|
|
|
|
// Parse auto rule
|
|
if piggyBank.AutoRule == "" {
|
|
continue
|
|
}
|
|
|
|
var rule AutoDepositRule
|
|
if err := json.Unmarshal([]byte(piggyBank.AutoRule), &rule); err != nil {
|
|
continue
|
|
}
|
|
|
|
// Check if deposit should be made based on frequency
|
|
shouldDeposit := false
|
|
depositAmount := rule.Amount
|
|
|
|
switch rule.Frequency {
|
|
case "daily":
|
|
shouldDeposit = true
|
|
case "weekly":
|
|
if rule.DayOfWeek != nil && int(now.Weekday()) == *rule.DayOfWeek {
|
|
shouldDeposit = true
|
|
}
|
|
case "monthly":
|
|
if rule.DayOfMonth != nil && now.Day() == *rule.DayOfMonth {
|
|
shouldDeposit = true
|
|
}
|
|
}
|
|
|
|
// For Week 52 type, calculate the week number and deposit amount
|
|
if piggyBank.Type == models.PiggyBankTypeWeek52 {
|
|
// Calculate week number since creation
|
|
weeksSinceCreation := int(time.Since(piggyBank.CreatedAt).Hours() / 24 / 7)
|
|
if weeksSinceCreation < 52 {
|
|
depositAmount = float64(weeksSinceCreation + 1) // Week 1: $1, Week 2: $2, etc.
|
|
shouldDeposit = int(now.Weekday()) == 1 // Monday
|
|
}
|
|
}
|
|
|
|
if shouldDeposit && piggyBank.LinkedAccountID != nil {
|
|
// Make the deposit
|
|
_, err := s.Deposit(userID, piggyBank.ID, DepositInput{
|
|
Amount: depositAmount,
|
|
FromAccountID: piggyBank.LinkedAccountID,
|
|
Note: "Automatic deposit",
|
|
})
|
|
if err != nil {
|
|
// Log error but continue with other piggy banks
|
|
fmt.Printf("Failed to process auto deposit for piggy bank %d: %v\n", piggyBank.ID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// isValidPiggyBankType checks if a piggy bank type is valid
|
|
func isValidPiggyBankType(piggyBankType models.PiggyBankType) bool {
|
|
switch piggyBankType {
|
|
case models.PiggyBankTypeManual, models.PiggyBankTypeAuto, models.PiggyBankTypeFixedDeposit, models.PiggyBankTypeWeek52:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|