200 lines
6.5 KiB
Go
200 lines
6.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
func getEnv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
var ledgerBaseURL = getEnv("LEDGER_URL", "http://172.17.0.1:3250")
|
|
|
|
// LedgerClient handles communication with aamos-ledger
|
|
type LedgerClient struct {
|
|
BaseURL string
|
|
}
|
|
|
|
func NewLedgerClient() *LedgerClient {
|
|
return &LedgerClient{BaseURL: ledgerBaseURL}
|
|
}
|
|
|
|
func (c *LedgerClient) Get(path string) (*http.Response, error) {
|
|
return http.Get(c.BaseURL + path)
|
|
}
|
|
|
|
// LedgerFinanceHandler connects to aamos-ledger for financial data
|
|
type LedgerFinanceHandler struct {
|
|
Client *LedgerClient
|
|
}
|
|
|
|
func NewLedgerFinanceHandler() *LedgerFinanceHandler {
|
|
return &LedgerFinanceHandler{Client: NewLedgerClient()}
|
|
}
|
|
|
|
// GetBalanceSheet returns trial balance from ledger
|
|
func (h *LedgerFinanceHandler) GetBalanceSheet(w http.ResponseWriter, r *http.Request) {
|
|
period := r.URL.Query().Get("period")
|
|
if period == "" {
|
|
period = time.Now().Format("2006-01")
|
|
}
|
|
|
|
resp, err := h.Client.Get("/api/ledger/trial-balance?period=" + period)
|
|
if err != nil || resp.StatusCode != 200 {
|
|
// Fallback to mock data
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"assets": []map[string]interface{}{
|
|
{"account": "1930 - Checkkonto", "amount": 245000},
|
|
{"account": "1940 - Sparkonto", "amount": 500000},
|
|
{"account": "1510 - Kundfordringar", "amount": 125000},
|
|
},
|
|
"liabilities": []map[string]interface{}{
|
|
{"account": "2440 - Leverantörsskulder", "amount": 85000},
|
|
{"account": "2013 - Aktiekapital", "amount": 100000},
|
|
},
|
|
"equity": []map[string]interface{}{
|
|
{"account": "2091 - Balanserad vinst", "amount": 485000},
|
|
},
|
|
"total_assets": 870000,
|
|
"total_liabilities": 185000,
|
|
"total_equity": 685000,
|
|
"period": period,
|
|
})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var result map[string]interface{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
writeError(w, http.StatusInternalServerError, "decode error")
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(result)
|
|
}
|
|
|
|
// GetIncomeStatement returns income statement (not yet implemented in ledger)
|
|
func (h *LedgerFinanceHandler) GetIncomeStatement(w http.ResponseWriter, r *http.Request) {
|
|
// Return mock data until ledger implements this
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"revenue": 245000,
|
|
"expenses": 180000,
|
|
"net_income": 65000,
|
|
"period": time.Now().Format("2006-01"),
|
|
})
|
|
}
|
|
|
|
// GetMomsReport returns VAT report (not yet implemented in ledger)
|
|
func (h *LedgerFinanceHandler) GetMomsReport(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"moms_in": 61250,
|
|
"moms_ut": 35000,
|
|
"moms_att_betala": 26250,
|
|
"period": time.Now().Format("2006-01"),
|
|
})
|
|
}
|
|
|
|
// GetAccounts returns chart of accounts from ledger
|
|
func (h *LedgerFinanceHandler) GetAccounts(w http.ResponseWriter, r *http.Request) {
|
|
resp, err := h.Client.Get("/api/v1/accounts")
|
|
if err != nil || resp.StatusCode != 200 {
|
|
// Fallback to mock data
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"accounts": []map[string]interface{}{
|
|
{"id": "1930", "name": "Checkkonto", "type": "asset", "balance": 245000},
|
|
{"id": "1940", "name": "Sparkonto", "type": "asset", "balance": 500000},
|
|
{"id": "1510", "name": "Kundfordringar", "type": "asset", "balance": 125000},
|
|
{"id": "2440", "name": "Leverantörsskulder", "type": "liability", "balance": 85000},
|
|
{"id": "2013", "name": "Aktiekapital", "type": "equity", "balance": 100000},
|
|
{"id": "2091", "name": "Balanserad vinst", "type": "equity", "balance": 485000},
|
|
{"id": "3001", "name": "Försäljning tjänster", "type": "revenue", "balance": 450000},
|
|
{"id": "6100", "name": "Löner", "type": "expense", "balance": 180000},
|
|
},
|
|
})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var result map[string]interface{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
var accounts []map[string]interface{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&accounts); err != nil {
|
|
writeError(w, http.StatusInternalServerError, "decode error")
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{"accounts": accounts})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(result)
|
|
}
|
|
|
|
// GetInvoices returns invoices (mock until implemented)
|
|
func (h *LedgerFinanceHandler) GetInvoices(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"invoices": []map[string]interface{}{
|
|
{"id": "INV-001", "customer": "Test AB", "amount": 25000, "status": "paid", "due_date": "2026-07-30"},
|
|
{"id": "INV-002", "customer": "Acme Corp", "amount": 45000, "status": "pending", "due_date": "2026-08-15"},
|
|
},
|
|
"total": 2,
|
|
})
|
|
}
|
|
|
|
// GetCashflow returns cashflow (mock until implemented)
|
|
func (h *LedgerFinanceHandler) GetCashflow(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"inflow": 320000,
|
|
"outflow": 180000,
|
|
"net": 140000,
|
|
"period": time.Now().Format("2006-01"),
|
|
})
|
|
}
|
|
|
|
// GetBudget returns budget (mock until implemented)
|
|
func (h *LedgerFinanceHandler) GetBudget(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"budget": 500000,
|
|
"actual": 245000,
|
|
"remaining": 255000,
|
|
"period": time.Now().Format("2006-01"),
|
|
})
|
|
}
|
|
|
|
// CreateExpense creates an expense (mock until implemented)
|
|
func (h *LedgerFinanceHandler) CreateExpense(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"id": "EXP-001",
|
|
"status": "created",
|
|
})
|
|
}
|
|
|
|
// ListExpenses lists expenses (mock until implemented)
|
|
func (h *LedgerFinanceHandler) ListExpenses(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"expenses": []map[string]interface{}{
|
|
{"id": "EXP-001", "category": "Boende", "amount": 8500, "date": "2026-07-01"},
|
|
{"id": "EXP-002", "category": "Mat", "amount": 3200, "date": "2026-07-05"},
|
|
},
|
|
"total": 2,
|
|
})
|
|
}
|
|
|