BOC v1.0: RS256 auth, Ledger integration, Prometheus metrics, CI/CD, backup
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
// Package ledger - Real client for aamos-ledger integration
|
||||
// Uses direct DB connection for reliability
|
||||
package ledger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// RealClient connects directly to aamos-ledger database
|
||||
type RealClient struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewRealClient creates a client connected to ledger DB
|
||||
func NewRealClient(dbURL string) (*RealClient, error) {
|
||||
db, err := sql.Open("postgres", dbURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to ledger DB: %w", err)
|
||||
}
|
||||
|
||||
db.SetMaxOpenConns(10)
|
||||
db.SetMaxIdleConns(5)
|
||||
db.SetConnMaxLifetime(5 * time.Minute)
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
return nil, fmt.Errorf("failed to ping ledger DB: %w", err)
|
||||
}
|
||||
|
||||
return &RealClient{db: db}, nil
|
||||
}
|
||||
|
||||
// Account represents a BAS account
|
||||
type Account struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
AccountType string `json:"account_type"`
|
||||
Balance float64 `json:"balance"`
|
||||
}
|
||||
|
||||
// GetAccounts returns all BAS accounts
|
||||
func (c *RealClient) GetAccounts(ctx context.Context) ([]Account, error) {
|
||||
rows, err := c.db.QueryContext(ctx, `
|
||||
SELECT a.code, a.name, a.account_type,
|
||||
COALESCE(SUM(CASE WHEN jl.debit IS NOT NULL THEN jl.debit ELSE 0 END) -
|
||||
SUM(CASE WHEN jl.credit IS NOT NULL THEN jl.credit ELSE 0 END), 0) as balance
|
||||
FROM accounts a
|
||||
LEFT JOIN journal_lines jl ON a.id = jl.account_id
|
||||
GROUP BY a.id, a.code, a.name, a.account_type
|
||||
ORDER BY a.code
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query accounts: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []Account
|
||||
for rows.Next() {
|
||||
var a Account
|
||||
if err := rows.Scan(&a.Code, &a.Name, &a.AccountType, &a.Balance); err != nil {
|
||||
return nil, fmt.Errorf("scan account: %w", err)
|
||||
}
|
||||
accounts = append(accounts, a)
|
||||
}
|
||||
|
||||
return accounts, rows.Err()
|
||||
}
|
||||
|
||||
// GetBalanceSheet returns assets, liabilities, equity
|
||||
func (c *RealClient) GetBalanceSheet(ctx context.Context) (map[string]interface{}, error) {
|
||||
accounts, err := c.GetAccounts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var assets, liabilities, equity []Account
|
||||
var totalAssets, totalLiabilities, totalEquity float64
|
||||
|
||||
for _, a := range accounts {
|
||||
switch a.AccountType {
|
||||
case "Asset":
|
||||
assets = append(assets, a)
|
||||
totalAssets += a.Balance
|
||||
case "Liability":
|
||||
liabilities = append(liabilities, a)
|
||||
totalLiabilities += a.Balance
|
||||
case "Equity":
|
||||
equity = append(equity, a)
|
||||
totalEquity += a.Balance
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"assets": accountsToMaps(assets),
|
||||
"liabilities": accountsToMaps(liabilities),
|
||||
"equity": accountsToMaps(equity),
|
||||
"total_assets": totalAssets,
|
||||
"total_liabilities": totalLiabilities,
|
||||
"total_equity": totalEquity,
|
||||
"period": time.Now().Format("2006-01"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetTrialBalance returns trial balance
|
||||
func (c *RealClient) GetTrialBalance(ctx context.Context) ([]map[string]interface{}, error) {
|
||||
accounts, err := c.GetAccounts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []map[string]interface{}
|
||||
for _, a := range accounts {
|
||||
result = append(result, map[string]interface{}{
|
||||
"account_number": a.Code,
|
||||
"account_name": a.Name,
|
||||
"balance": a.Balance,
|
||||
"account_type": a.AccountType,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Close closes the database connection
|
||||
func (c *RealClient) Close() error {
|
||||
return c.db.Close()
|
||||
}
|
||||
|
||||
func accountsToMaps(accounts []Account) []map[string]interface{} {
|
||||
var result []map[string]interface{}
|
||||
for _, a := range accounts {
|
||||
result = append(result, map[string]interface{}{
|
||||
"account": a.Code + " - " + a.Name,
|
||||
"amount": a.Balance,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user