Files
Portfolio-Engine/internal/model/revenue.go
2026-03-24 19:42:49 +01:00

102 lines
2.3 KiB
Go

package model
import (
"database/sql"
"fmt"
"time"
)
// PeriodType defines the granularity of the revenue entry
type PeriodType string
const (
PeriodQuarter PeriodType = "Q"
PeriodHalfYear PeriodType = "H"
PeriodYear PeriodType = "Y"
)
// Period holds the actual time range for a revenue entry
type Period struct {
ID int
Type PeriodType
Year int
Index int // Q1=1 Q2=2 Q3=3 Q4=4 | H1=1 H2=2 | FY=1
Start time.Time
End time.Time
}
func (p *Period) Insert(db *sql.DB) error {
_, err := db.Exec(
`INSERT INTO periods (type, year, idx, start_date, end_date) VALUES (?, ?, ?, ?, ?)
ON CONFLICT(type, year, idx) DO UPDATE SET start_date=excluded.start_date, end_date=excluded.end_date`,
string(p.Type), p.Year, p.Index, p.Start.Format("2006-01-02"), p.End.Format("2006-01-02"),
)
if err != nil {
return fmt.Errorf("upsert period: %w", err)
}
var id int
err = db.QueryRow(
`SELECT id FROM periods WHERE type = ? AND year = ? AND idx = ?`,
string(p.Type), p.Year, p.Index,
).Scan(&id)
if err != nil {
return fmt.Errorf("select period: %w", err)
}
p.ID = id
return nil
}
type RevenueCategory struct {
ID int
CompanyID int
ParentID *int
Name string // e.g. "product", "location", "segment"
}
func (rc *RevenueCategory) Validate() error {
if rc.ID == 0 {
return fmt.Errorf("No ID Set")
} else if rc.Name == "" {
return fmt.Errorf("No Name found")
} else if rc.CompanyID == 0 {
return fmt.Errorf("No Company Set")
}
return nil
}
func (rc *RevenueCategory) Insert(db *sql.DB) error {
if err := rc.Validate(); err != nil {
return fmt.Errorf("failed to insert: %w", err)
}
_, err := db.Exec(
`INSERT INTO category (company_id, parent_id, name) VALUES (?, ?, ?)
ON CONFLICT(company_id, name) DO UPDATE SET parent_id=excluded.parent_id`,
rc.CompanyID, rc.ParentID, rc.Name,
)
if err != nil {
return fmt.Errorf("upsert category: %w", err)
}
err = db.QueryRow(
`SELECT id FROM category WHERE company_id = ? AND name = ?`,
rc.CompanyID, rc.Name,
).Scan(&rc.ID)
if err != nil {
return fmt.Errorf("select category id: %w", err)
}
return nil
}
// Revenue is a single line in a financial report
type Revenue struct {
ID int
Company *Company
Currency *Currency
Category *RevenueCategory
Period *Period
Value float64
}