143 lines
3.6 KiB
Go
143 lines
3.6 KiB
Go
|
|
// 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
|
||
|
|
}
|