added dividend db table and insert by trade

This commit is contained in:
samantha42
2026-04-06 07:36:45 +02:00
parent 31108a16d0
commit 57ae3cfb06
7 changed files with 120 additions and 48 deletions

View File

@@ -17,14 +17,26 @@ func InitDB(db *sql.DB) {
);
CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
currency_code TEXT NOT NULL,
shares INTEGER NOT NULL,
product INTEGER NOT NULL CHECK(product IN (0, 1, 2, 3)),
type INTEGER NOT NULL CHECK(type IN (0, 1)),
price REAL NOT NULL,
traded_at DATETIME NOT NULL
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
currency_code TEXT NOT NULL,
shares INTEGER NOT NULL,
product INTEGER NOT NULL CHECK(product IN (0, 1, 2, 3, 4)), -- added 4 for BondTrade
type INTEGER NOT NULL CHECK(type IN (0, 1)), -- Buy=0, Sell=1 only; Dividend has its own table
price REAL NOT NULL,
traded_at DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS dividends (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
currency_code TEXT NOT NULL,
product INTEGER NOT NULL CHECK(product IN (0, 1, 2, 3, 4)),
value REAL NOT NULL,
tax_amount REAL NOT NULL DEFAULT 0,
tax_rate REAL NOT NULL DEFAULT 0,
net_value REAL NOT NULL,
payment_date DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS position (

View File

@@ -25,14 +25,7 @@ func GetTrades(db *sql.DB) ([]model.Trade, error) {
return nil, err
}
switch typeInt {
case 0:
t.Type = model.TradeType(false)
case 1:
t.Type = model.TradeType(true)
default:
return nil, fmt.Errorf("failed to convert given Type int to bool of trade type.")
}
t.Type = model.TradeType(typeInt)
trades = append(trades, t)
}
@@ -81,6 +74,21 @@ func InsertTrade(db *sql.DB, trade model.Trade) error {
return err
}
func InsertDividend(db *sql.DB, div model.Dividend) error {
_, err := db.Exec(
"INSERT INTO trades (symbol, currency_code, shares, product, value, tax_amount, tax_rate, net_value, payment_date) VALUES (?, ?, ?, ?, ?, ?, ?)",
div.Symbol,
div.CurrencyCode,
div.Product,
div.Value,
div.TaxAmount,
div.TaxRate,
div.NetValue,
div.PaymentDate,
)
return err
}
func UpdatePositions(db *sql.DB, positions []model.Position) error {
// Complete overwrite of the db positions
_, err := db.Exec("DELETE FROM position")

View File

@@ -20,45 +20,57 @@ func AddTradeHandler(db *sql.DB) http.HandlerFunc {
return
}
err := req.Validate()
if err != nil {
http.Error(w, fmt.Sprintf("failed to validate trade: %s", err), http.StatusInternalServerError)
if err := req.Validate(); err != nil {
http.Error(w, fmt.Sprintf("failed to validate trade: %s", err), http.StatusBadRequest)
return
}
// check if currency is in the db.
currency, err := database.GetCurrencyByCode(db, req.CurrencyCode)
if err != nil {
http.Error(w, fmt.Sprintf("failed to find currency: %s", err), http.StatusInternalServerError)
return
}
trade := model.Trade{
Symbol: req.Symbol,
Shares: req.Shares,
Product: model.TradeProduct(req.Product),
Type: model.TradeType(req.Type),
Price: req.Price,
CurrencyCode: currency.Code,
Date: req.Date,
}
switch model.TradeType(req.Type) {
case model.DividendType:
dividend, err := req.ToDividend()
if err != nil {
http.Error(w, fmt.Sprintf("failed to build dividend: %s", err), http.StatusBadRequest)
return
}
dividend.CurrencyCode = currency.Code
err = database.InsertTrade(db, trade)
if err != nil {
http.Error(w, fmt.Sprintf("failed to insert trade into db: %s", err), http.StatusInternalServerError)
return
}
if err := database.InsertDividend(db, dividend); err != nil {
http.Error(w, fmt.Sprintf("failed to insert dividend: %s", err), http.StatusInternalServerError)
return
}
err = service.UpdatePositionByTradeList(db)
update := true
if err != nil {
update = false
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{"success": true})
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]any{"success": true, "position update": update}); err != nil {
http.Error(w, fmt.Sprintf("failed to encode trades: %s", err), http.StatusInternalServerError)
return
case model.BuyType, model.SellType:
trade, err := req.ToTrade()
if err != nil {
http.Error(w, fmt.Sprintf("failed to build trade: %s", err), http.StatusBadRequest)
return
}
trade.CurrencyCode = currency.Code
if err := database.InsertTrade(db, trade); err != nil {
http.Error(w, fmt.Sprintf("failed to insert trade: %s", err), http.StatusInternalServerError)
return
}
update := true
if err := service.UpdatePositionByTradeList(db); err != nil {
update = false
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{"success": true, "position_update": update})
default:
http.Error(w, fmt.Sprintf("unknown trade type: %d", req.Type), http.StatusBadRequest)
}
}
}

View File

@@ -2,6 +2,7 @@ package model
import (
"errors"
"fmt"
"time"
)
@@ -55,21 +56,28 @@ const (
BondTrade
)
type TradeType bool
type TradeType int
const (
Buy TradeType = true
Sell TradeType = false
BuyType TradeType = iota // 0
SellType // 1
DividendType // 2
)
type AddTradeRequest struct {
Symbol string `json:"symbol"`
Shares int `json:"shares"`
Product int `json:"product"`
Type bool `json:"type"`
Type int `json:"type"` // was bool, now int
Price float64 `json:"price"`
CurrencyCode string `json:"currency_code"`
Date time.Time `json:"date"`
// Dividend-specific fields (only populated when Type == 2)
TaxAmount float64 `json:"tax_amount,omitempty"`
TaxRate float64 `json:"tax_rate,omitempty"`
NetValue float64 `json:"net_value,omitempty"`
PaymentDate time.Time `json:"payment_date,omitempty"`
}
type Trade struct {
@@ -107,6 +115,38 @@ func (r *AddTradeRequest) Validate() error {
return nil
}
func (r AddTradeRequest) ToDividend() (Dividend, error) {
if TradeType(r.Type) != DividendType {
return Dividend{}, fmt.Errorf("trade type is not a dividend")
}
return Dividend{
Symbol: r.Symbol,
CurrencyCode: r.CurrencyCode,
Product: TradeProduct(r.Product),
Value: r.Price, // gross value
PaymentDate: r.PaymentDate,
TaxAmount: r.TaxAmount,
TaxRate: r.TaxRate,
NetValue: r.NetValue,
}, nil
}
func (r AddTradeRequest) ToTrade() (Trade, error) {
t := TradeType(r.Type)
if t != BuyType && t != SellType {
return Trade{}, fmt.Errorf("trade type is not buy or sell")
}
return Trade{
Symbol: r.Symbol,
CurrencyCode: r.CurrencyCode,
Shares: r.Shares,
Product: TradeProduct(r.Product),
Type: t,
Price: r.Price,
Date: r.Date,
}, nil
}
// for now trades and none stock position will not be supported.
type Portifolio struct {
Positions []Position

View File

@@ -19,7 +19,7 @@ func UpdatePositionByTradeList(db *sql.DB) error {
TradeSum := make(map[string]model.Position)
for _, trade := range trades {
if trade.Type == model.Buy {
if trade.Type == model.BuyType {
TradeSum[trade.Symbol] = model.Position{
Symbol: trade.Symbol,
CurrencyCode: trade.CurrencyCode,