init
This commit is contained in:
583
internal/service/piggy_bank_service.go
Normal file
583
internal/service/piggy_bank_service.go
Normal file
@@ -0,0 +1,583 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user