227 lines
5.6 KiB
Go
227 lines
5.6 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
models "vibeStonk/server/models/v1"
|
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
var (
|
|
ErrPurchaseNotFound = errors.New("purchase not found")
|
|
)
|
|
|
|
func newSqlitePurchaseRepo(db *sql.DB) (PurchaseRepo, error) {
|
|
repo := &sqlitePurchaseRepo{db: db}
|
|
if err := repo.initialize(); err != nil {
|
|
return nil, err
|
|
}
|
|
return repo, nil
|
|
}
|
|
|
|
type sqlitePurchaseRepo struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// initialize creates the purchases table if it doesn't exist
|
|
func (s *sqlitePurchaseRepo) initialize() error {
|
|
query := `
|
|
CREATE TABLE IF NOT EXISTS purchases (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
purchase_date TIMESTAMP NOT NULL,
|
|
stock_id INTEGER NOT NULL,
|
|
qty REAL NOT NULL,
|
|
price REAL NOT NULL,
|
|
FOREIGN KEY (stock_id) REFERENCES stocks(id)
|
|
);
|
|
`
|
|
_, err := s.db.Exec(query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create purchases table: %w", err)
|
|
}
|
|
|
|
query = `
|
|
CREATE INDEX IF NOT EXISTS purchases_stock_id
|
|
ON purchases(stock_id);
|
|
`
|
|
_, err = s.db.Exec(query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create stock_id index for purchases table: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *sqlitePurchaseRepo) Create(purchase *models.Purchase) (*models.Purchase, error) {
|
|
query := `
|
|
INSERT INTO purchases (purchase_date, stock_id, qty, price)
|
|
VALUES (?, ?, ?, ?)
|
|
`
|
|
// Convert protobuf timestamp to time.Time for SQLite
|
|
var purchaseDate time.Time
|
|
if purchase.PurchaseDate != nil {
|
|
purchaseDate = purchase.PurchaseDate.AsTime()
|
|
} else {
|
|
purchaseDate = time.Now()
|
|
purchase.PurchaseDate = timestamppb.New(purchaseDate)
|
|
}
|
|
|
|
result, err := s.db.Exec(query, purchaseDate, purchase.StockID, purchase.Qty, purchase.Price)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create purchase: %w", err)
|
|
}
|
|
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get last insert ID: %w", err)
|
|
}
|
|
|
|
purchase.Id = id
|
|
return purchase, nil
|
|
}
|
|
|
|
func (s *sqlitePurchaseRepo) Get(id int64) (*models.Purchase, error) {
|
|
query := `
|
|
SELECT id, purchase_date, stock_id, qty, price
|
|
FROM purchases
|
|
WHERE id = ?
|
|
`
|
|
row := s.db.QueryRow(query, id)
|
|
|
|
purchase := &models.Purchase{}
|
|
var purchaseDate time.Time
|
|
err := row.Scan(&purchase.Id, &purchaseDate, &purchase.StockID, &purchase.Qty, &purchase.Price)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrPurchaseNotFound
|
|
}
|
|
return nil, fmt.Errorf("failed to get purchase: %w", err)
|
|
}
|
|
|
|
// Convert time.Time to protobuf timestamp
|
|
purchase.PurchaseDate = timestamppb.New(purchaseDate)
|
|
|
|
return purchase, nil
|
|
}
|
|
|
|
func (s *sqlitePurchaseRepo) GetByStockID(stockID int64) ([]*models.Purchase, error) {
|
|
query := `
|
|
SELECT id, purchase_date, stock_id, qty, price
|
|
FROM purchases
|
|
WHERE stock_id = ?
|
|
`
|
|
rows, err := s.db.Query(query, stockID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get purchases by stock ID: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var purchases []*models.Purchase
|
|
for rows.Next() {
|
|
purchase := &models.Purchase{}
|
|
var purchaseDate time.Time
|
|
err := rows.Scan(&purchase.Id, &purchaseDate, &purchase.StockID, &purchase.Qty, &purchase.Price)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan purchase row: %w", err)
|
|
}
|
|
// Convert time.Time to protobuf timestamp
|
|
purchase.PurchaseDate = timestamppb.New(purchaseDate)
|
|
purchases = append(purchases, purchase)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating purchase rows: %w", err)
|
|
}
|
|
|
|
return purchases, nil
|
|
}
|
|
|
|
func (s *sqlitePurchaseRepo) Update(purchase *models.Purchase) error {
|
|
query := `
|
|
UPDATE purchases
|
|
SET purchase_date = ?, stock_id = ?, qty = ?, price = ?
|
|
WHERE id = ?
|
|
`
|
|
// Convert protobuf timestamp to time.Time for SQLite
|
|
var purchaseDate time.Time
|
|
if purchase.PurchaseDate != nil {
|
|
purchaseDate = purchase.PurchaseDate.AsTime()
|
|
} else {
|
|
purchaseDate = time.Now()
|
|
purchase.PurchaseDate = timestamppb.New(purchaseDate)
|
|
}
|
|
|
|
result, err := s.db.Exec(query, purchaseDate, purchase.StockID, purchase.Qty, purchase.Price, purchase.Id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update purchase: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return ErrPurchaseNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *sqlitePurchaseRepo) Delete(purchase *models.Purchase) error {
|
|
query := `
|
|
DELETE FROM purchases
|
|
WHERE id = ?
|
|
`
|
|
result, err := s.db.Exec(query, purchase.Id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete purchase: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return ErrPurchaseNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *sqlitePurchaseRepo) List() ([]*models.Purchase, error) {
|
|
query := `
|
|
SELECT id, purchase_date, stock_id, qty, price
|
|
FROM purchases
|
|
ORDER BY purchase_date DESC
|
|
`
|
|
rows, err := s.db.Query(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list purchases: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var purchases []*models.Purchase
|
|
for rows.Next() {
|
|
purchase := &models.Purchase{}
|
|
var purchaseDate time.Time
|
|
err := rows.Scan(&purchase.Id, &purchaseDate, &purchase.StockID, &purchase.Qty, &purchase.Price)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan purchase row: %w", err)
|
|
}
|
|
// Convert time.Time to protobuf timestamp
|
|
purchase.PurchaseDate = timestamppb.New(purchaseDate)
|
|
purchases = append(purchases, purchase)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating purchase rows: %w", err)
|
|
}
|
|
|
|
return purchases, nil
|
|
}
|