feat(dashboard): #163 first summary
All checks were successful
Build Docker Image / Build-Docker-Image (push) Successful in 5m11s
Build and Push Docker Image / Build-And-Push-Docker-Image (push) Successful in 5m6s

This commit was merged in pull request #181.
This commit is contained in:
2025-06-08 15:35:21 +02:00
parent 935019c1c4
commit 6b8059889d
15 changed files with 280 additions and 67 deletions

View File

@@ -0,0 +1,88 @@
package service
import (
"context"
"log/slog"
"spend-sparrow/internal/db"
"spend-sparrow/internal/types"
"time"
"github.com/jmoiron/sqlx"
)
type Dashboard struct {
db *sqlx.DB
}
func NewDashboard(db *sqlx.DB) *Dashboard {
return &Dashboard{
db: db,
}
}
func (s Dashboard) Summary(ctx context.Context, user *types.User, month time.Time) (*types.DashboardMonthlySummary, error) {
if user == nil {
return nil, ErrUnauthorized
}
var summary types.DashboardMonthlySummary
var value *int64
err := s.db.GetContext(ctx, &value, `
SELECT SUM(value)
FROM "transaction"
WHERE user_id = $1
AND treasure_chest_id IS NOT NULL
AND account_id IS NULL
AND error IS NULL
AND date(timestamp, 'start of month') = date($2, 'start of month')`,
user.Id, month)
err = db.TransformAndLogDbError("dashboard", nil, err)
if err != nil {
return nil, err
}
if value != nil {
summary.Savings = *value
}
err = s.db.GetContext(ctx, &value, `
SELECT SUM(value)
FROM "transaction"
WHERE user_id = $1
AND account_id IS NOT NULL
AND treasure_chest_id IS NULL
AND error IS NULL
AND date(timestamp, 'start of month') = date($2, 'start of month')`,
user.Id, month)
err = db.TransformAndLogDbError("dashboard", nil, err)
if err != nil {
return nil, err
}
if value != nil {
summary.Income = *value
}
err = s.db.GetContext(ctx, &value, `
SELECT SUM(value)
FROM "transaction"
WHERE user_id = $1
AND account_id IS NOT NULL
AND treasure_chest_id IS NOT NULL
AND error IS NULL
AND date(timestamp, 'start of month') = date($2, 'start of month')`,
user.Id, month)
err = db.TransformAndLogDbError("dashboard", nil, err)
if err != nil {
return nil, err
}
if value != nil {
summary.Expenses = *value
}
summary.Total = summary.Income + summary.Expenses
summary.Month = month
slog.Info("Dashboard summary", "summary", summary)
return &summary, nil
}

View File

@@ -512,27 +512,20 @@ func (s TransactionImpl) validateAndEnrichTransaction(ctx context.Context, tx *s
return &transaction, nil
}
func (s TransactionImpl) updateErrors(transaction *types.Transaction) {
func (s TransactionImpl) updateErrors(t *types.Transaction) {
errorStr := ""
switch {
case transaction.Value < 0:
if transaction.TreasureChestId == nil {
errorStr = "no treasure chest specified"
}
case transaction.Value > 0:
if transaction.AccountId == nil && transaction.TreasureChestId == nil {
errorStr = "either an account or a treasure chest needs to be specified"
} else if transaction.AccountId != nil && transaction.TreasureChestId != nil {
errorStr = "positive amounts can only be applied to either an account or a treasure chest"
}
default:
case (t.AccountId != nil && t.TreasureChestId != nil && t.Value > 0) ||
(t.AccountId == nil && t.TreasureChestId == nil):
errorStr = "either an account or a treasure chest needs to be specified"
case t.Value == 0:
errorStr = "\"value\" needs to be specified"
}
if errorStr == "" {
transaction.Error = nil
t.Error = nil
} else {
transaction.Error = &errorStr
t.Error = &errorStr
}
}