new revenue getters
This commit is contained in:
BIN
Portifolio
BIN
Portifolio
Binary file not shown.
@@ -55,7 +55,8 @@ func InitDB(db *sql.DB) {
|
|||||||
FOREIGN KEY (company_id) REFERENCES companies(id),
|
FOREIGN KEY (company_id) REFERENCES companies(id),
|
||||||
FOREIGN KEY (currency_id) REFERENCES currencies(id),
|
FOREIGN KEY (currency_id) REFERENCES currencies(id),
|
||||||
FOREIGN KEY (category_id) REFERENCES category(id),
|
FOREIGN KEY (category_id) REFERENCES category(id),
|
||||||
FOREIGN KEY (period_id) REFERENCES periods(id)
|
FOREIGN KEY (period_id) REFERENCES periods(id),
|
||||||
|
UNIQUE(company_id, category_id, period_id)
|
||||||
);`
|
);`
|
||||||
|
|
||||||
if _, err := db.Exec(schema); err != nil {
|
if _, err := db.Exec(schema); err != nil {
|
||||||
@@ -63,3 +64,37 @@ func InitDB(db *sql.DB) {
|
|||||||
}
|
}
|
||||||
fmt.Println("Tables ready")
|
fmt.Println("Tables ready")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MigrateAddUniqueToRevenueEntries(db *sql.DB) error {
|
||||||
|
steps := []string{
|
||||||
|
// 1. copy existing data into a temp table with the new constraint
|
||||||
|
`CREATE TABLE IF NOT EXISTS revenue_entries_new (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
company_id INTEGER NOT NULL,
|
||||||
|
currency_id INTEGER NOT NULL,
|
||||||
|
category_id INTEGER NOT NULL,
|
||||||
|
period_id INTEGER NOT NULL,
|
||||||
|
value REAL NOT NULL,
|
||||||
|
FOREIGN KEY (company_id) REFERENCES companies(id),
|
||||||
|
FOREIGN KEY (currency_id) REFERENCES currencies(id),
|
||||||
|
FOREIGN KEY (category_id) REFERENCES category(id),
|
||||||
|
FOREIGN KEY (period_id) REFERENCES periods(id),
|
||||||
|
UNIQUE(company_id, category_id, period_id)
|
||||||
|
)`,
|
||||||
|
// 2. copy data over
|
||||||
|
`INSERT OR IGNORE INTO revenue_entries_new
|
||||||
|
SELECT id, company_id, currency_id, category_id, period_id, value
|
||||||
|
FROM revenue_entries`,
|
||||||
|
// 3. drop old table
|
||||||
|
`DROP TABLE revenue_entries`,
|
||||||
|
// 4. rename new table
|
||||||
|
`ALTER TABLE revenue_entries_new RENAME TO revenue_entries`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, step := range steps {
|
||||||
|
if _, err := db.Exec(step); err != nil {
|
||||||
|
return fmt.Errorf("migration failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,3 +57,59 @@ func GetPeriodByID(db *sql.DB, periodID int) (model.Period, error) {
|
|||||||
p.End, _ = time.Parse("2006-01-02", end)
|
p.End, _ = time.Parse("2006-01-02", end)
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetRevenueByPeriod(db *sql.DB, companyID int, periodID int) ([]model.Revenue, error) {
|
||||||
|
rows, err := db.Query(
|
||||||
|
`SELECT id, company_id, currency_id, period_id, value FROM revenue_entries WHERE company_id = ? AND period_id = ?`,
|
||||||
|
companyID, periodID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("query revenue by period: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var revenues []model.Revenue
|
||||||
|
for rows.Next() {
|
||||||
|
var rc model.Revenue
|
||||||
|
if err := rows.Scan(&rc.ID, &rc.Company, &rc.Currency, &rc.Period, &rc.Value); err != nil {
|
||||||
|
return nil, fmt.Errorf("scan revenue row: %w", err)
|
||||||
|
}
|
||||||
|
revenues = append(revenues, rc)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("iterate revenue rows: %w", err)
|
||||||
|
}
|
||||||
|
if len(revenues) == 0 {
|
||||||
|
return nil, fmt.Errorf("revenue by company %d and period_id %d not found", companyID, periodID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return revenues, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRevenueByCategory(db *sql.DB, companyID int, categoryID int) ([]model.Revenue, error) {
|
||||||
|
rows, err := db.Query(
|
||||||
|
`SELECT id, company_id, currency_id, category_id, value FROM revenue_entries WHERE company_id = ? AND category_id = ?`,
|
||||||
|
companyID, categoryID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("query revenue by category: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var revenues []model.Revenue
|
||||||
|
for rows.Next() {
|
||||||
|
var rc model.Revenue
|
||||||
|
if err := rows.Scan(&rc.ID, &rc.Company, &rc.Currency, &rc.Category, &rc.Value); err != nil {
|
||||||
|
return nil, fmt.Errorf("scan revenue row: %w", err)
|
||||||
|
}
|
||||||
|
revenues = append(revenues, rc)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("iterate revenue rows: %w", err)
|
||||||
|
}
|
||||||
|
if len(revenues) == 0 {
|
||||||
|
return nil, fmt.Errorf("revenue by company %d and category_id %d not found", companyID, categoryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return revenues, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
@@ -51,6 +50,7 @@ func AddRevenueEntryHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func GetRevenueReportHandler(db *sql.DB) http.HandlerFunc {
|
func GetRevenueReportHandler(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
companyID, _ := strconv.Atoi(r.URL.Query().Get("company_id"))
|
companyID, _ := strconv.Atoi(r.URL.Query().Get("company_id"))
|
||||||
@@ -58,7 +58,7 @@ func GetRevenueReportHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
year, _ := strconv.Atoi(r.URL.Query().Get("year"))
|
year, _ := strconv.Atoi(r.URL.Query().Get("year"))
|
||||||
idx, _ := strconv.Atoi(r.URL.Query().Get("index"))
|
idx, _ := strconv.Atoi(r.URL.Query().Get("index"))
|
||||||
|
|
||||||
entries, err := service.GetRevenue(db, companyID, model.PeriodType(periodType), year, idx)
|
entries, err := database.GetRevenueByPeriod(db, companyID, )
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -68,3 +68,4 @@ func GetRevenueReportHandler(db *sql.DB) http.HandlerFunc {
|
|||||||
json.NewEncoder(w).Encode(entries)
|
json.NewEncoder(w).Encode(entries)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"Portifolio/internal/model"
|
"Portifolio/internal/model"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
@@ -54,46 +53,3 @@ func InsertRevenue(db *sql.DB, companyID int, currencyID int, categoryName strin
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRevenue(db *sql.DB, companyID int, periodType model.PeriodType, year, idx int) ([]model.Revenue, error) {
|
|
||||||
rows, err := db.Query(`
|
|
||||||
WITH RECURSIVE tree AS (
|
|
||||||
SELECT id, name, parent_id
|
|
||||||
FROM category
|
|
||||||
WHERE company_id = ? AND parent_id IS NULL
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT c.id, c.name, c.parent_id
|
|
||||||
FROM category c
|
|
||||||
JOIN tree t ON c.parent_id = t.id
|
|
||||||
)
|
|
||||||
SELECT e.id, t.name, e.value,
|
|
||||||
p.type, p.year, p.idx, p.start_date, p.end_date
|
|
||||||
FROM revenue_entries e
|
|
||||||
JOIN tree t ON e.category_id = t.id
|
|
||||||
JOIN periods p ON e.period_id = p.id
|
|
||||||
WHERE e.company_id = ? AND p.type = ? AND p.year = ? AND p.idx = ?`,
|
|
||||||
companyID, companyID, string(periodType), year, idx,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("query revenue: %w", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var entries []model.Revenue
|
|
||||||
for rows.Next() {
|
|
||||||
var e model.Revenue
|
|
||||||
var p model.Period
|
|
||||||
var start, end string
|
|
||||||
if err := rows.Scan(&e.ID, &e.Category, &e.Value,
|
|
||||||
&p.Type, &p.Year, &p.Index, &start, &end); err != nil {
|
|
||||||
return nil, fmt.Errorf("scan revenue row: %w", err)
|
|
||||||
}
|
|
||||||
p.Start, _ = time.Parse("2006-01-02", start)
|
|
||||||
p.End, _ = time.Parse("2006-01-02", end)
|
|
||||||
e.Period = &p
|
|
||||||
entries = append(entries, e)
|
|
||||||
}
|
|
||||||
return entries, rows.Err()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ func ListRevenue(scanner *bufio.Scanner, db *sql.DB) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := service.GetRevenue(db, companyID, period.Type, period.Year, period.Index)
|
entries, err := database.GetRevenueByPeriod(db, companyID, period.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(" ✗ Error:", err)
|
fmt.Println(" ✗ Error:", err)
|
||||||
return
|
return
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -30,6 +30,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
database.InitDB(db)
|
database.InitDB(db)
|
||||||
|
database.MigrateAddUniqueToRevenueEntries(db)
|
||||||
fmt.Println("Connected to SQLite database")
|
fmt.Println("Connected to SQLite database")
|
||||||
|
|
||||||
http.HandleFunc("/health", handlers.HealthHandler(db))
|
http.HandleFunc("/health", handlers.HealthHandler(db))
|
||||||
@@ -43,7 +44,7 @@ func main() {
|
|||||||
|
|
||||||
// Revenue
|
// Revenue
|
||||||
http.HandleFunc("POST /add/revenue/entry", handlers.AddRevenueEntryHandler(db))
|
http.HandleFunc("POST /add/revenue/entry", handlers.AddRevenueEntryHandler(db))
|
||||||
http.HandleFunc("GET /revenue/report", handlers.GetRevenueReportHandler(db))
|
//http.HandleFunc("GET /revenue/report", handlers.GetRevenueReportHandler(db))
|
||||||
|
|
||||||
fmt.Println("Server running on :8080")
|
fmt.Println("Server running on :8080")
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user