feat: BOC ledger-integration frontend + backend fixes
- Uppdatera robust_client.go med rätt tabellnamn (boc_chart_of_accounts, boc_journal_lines, boc_journal_entries) - Uppdatera kolumnnamn (entry_id istället för journal_entry_id, account_code istället för code) - Hantera både stora och små bokstäver för account_type - Lägg till ledgerApi i frontend med BalanceSheet, IncomeStatement, MomsReport - Uppdatera DashboardPage med Ledger KPI-kort - Lägg till LEDGER_DB_URL i docker-compose.yml - Uppdatera Dockerfile till golang:1.25-alpine
This commit is contained in:
@@ -104,6 +104,40 @@ export const financeApi = {
|
||||
cashflow: () => api.get<{ cashflow: unknown[] }>('/finance/cashflow'),
|
||||
}
|
||||
|
||||
// Ledger (aamos-ledger integration)
|
||||
export interface LedgerAccount {
|
||||
code: string
|
||||
name: string
|
||||
account_type: string
|
||||
balance: number
|
||||
}
|
||||
|
||||
export interface BalanceSheet {
|
||||
assets: LedgerAccount[]
|
||||
liabilities: LedgerAccount[]
|
||||
equity: LedgerAccount[]
|
||||
total_assets: number
|
||||
total_liabilities: number
|
||||
total_equity: number
|
||||
period: string
|
||||
}
|
||||
|
||||
export interface IncomeStatement {
|
||||
revenues: LedgerAccount[]
|
||||
expenses: LedgerAccount[]
|
||||
total_revenue: number
|
||||
total_expense: number
|
||||
net_income: number
|
||||
period: string
|
||||
}
|
||||
|
||||
export const ledgerApi = {
|
||||
accounts: () => api.get<{ accounts: LedgerAccount[] }>('/ledger/accounts'),
|
||||
balanceSheet: (period?: string) => api.get<BalanceSheet>(`/ledger/balance-sheet?period=${period || ''}`),
|
||||
incomeStatement: (period?: string) => api.get<IncomeStatement>(`/ledger/income-statement?period=${period || ''}`),
|
||||
momsReport: (period?: string) => api.get<unknown>(`/ledger/moms?period=${period || ''}`),
|
||||
}
|
||||
|
||||
// HR
|
||||
export const hrApi = {
|
||||
employees: () => api.get<{ employees: unknown[] }>('/hr/employees'),
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from '@/components/ui/Table'
|
||||
import { Badge } from '@/components/ui/Badge'
|
||||
import { formatCurrency, formatDate } from '@/lib/utils'
|
||||
import { financeApi, salesApi, crmApi } from '@/lib/api'
|
||||
import { financeApi, salesApi, crmApi, ledgerApi } from '@/lib/api'
|
||||
import {
|
||||
DollarSign,
|
||||
Users,
|
||||
@@ -60,17 +60,25 @@ export function DashboardPage() {
|
||||
const [customerCount, setCustomerCount] = useState(0)
|
||||
const [deals, setDeals] = useState<Deal[]>([])
|
||||
const [dealTotal, setDealTotal] = useState(0)
|
||||
|
||||
// Ledger state
|
||||
const [ledgerAssets, setLedgerAssets] = useState(0)
|
||||
const [ledgerLiabilities, setLedgerLiabilities] = useState(0)
|
||||
const [ledgerEquity, setLedgerEquity] = useState(0)
|
||||
const [netIncome, setNetIncome] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
setLoading(true)
|
||||
setError('')
|
||||
try {
|
||||
const [balanceRes, mrrRes, customersRes, dealsRes] = await Promise.all([
|
||||
const [balanceRes, mrrRes, customersRes, dealsRes, ledgerBs, ledgerIs] = await Promise.all([
|
||||
financeApi.balance(),
|
||||
salesApi.mrr(),
|
||||
crmApi.customers(),
|
||||
salesApi.deals(),
|
||||
ledgerApi.balanceSheet('2026-01').catch(() => null),
|
||||
ledgerApi.incomeStatement('2026-01').catch(() => null),
|
||||
])
|
||||
|
||||
const b = balanceRes as { total_assets?: number }
|
||||
@@ -84,6 +92,16 @@ export function DashboardPage() {
|
||||
const d = dealsRes as { deals: Deal[]; total: number }
|
||||
setDeals(d.deals || [])
|
||||
setDealTotal(d.total || 0)
|
||||
|
||||
// Ledger data
|
||||
if (ledgerBs) {
|
||||
setLedgerAssets(ledgerBs.total_assets || 0)
|
||||
setLedgerLiabilities(ledgerBs.total_liabilities || 0)
|
||||
setLedgerEquity(ledgerBs.total_equity || 0)
|
||||
}
|
||||
if (ledgerIs) {
|
||||
setNetIncome(ledgerIs.net_income || 0)
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load dashboard data')
|
||||
} finally {
|
||||
@@ -124,6 +142,37 @@ export function DashboardPage() {
|
||||
icon: <ShoppingCart size={18} />,
|
||||
},
|
||||
]
|
||||
|
||||
const ledgerKpis = [
|
||||
{
|
||||
label: 'Ledger Assets',
|
||||
value: formatCurrency(ledgerAssets),
|
||||
change: 0,
|
||||
changeLabel: 'per balansräkning',
|
||||
icon: <DollarSign size={18} />,
|
||||
},
|
||||
{
|
||||
label: 'Ledger Liabilities',
|
||||
value: formatCurrency(ledgerLiabilities),
|
||||
change: 0,
|
||||
changeLabel: 'per balansräkning',
|
||||
icon: <TrendingUp size={18} />,
|
||||
},
|
||||
{
|
||||
label: 'Net Income',
|
||||
value: formatCurrency(netIncome),
|
||||
change: 0,
|
||||
changeLabel: 'per resultaträkning',
|
||||
icon: <TrendingUp size={18} />,
|
||||
},
|
||||
{
|
||||
label: 'Equity',
|
||||
value: formatCurrency(ledgerEquity),
|
||||
change: 0,
|
||||
changeLabel: 'per balansräkning',
|
||||
icon: <DollarSign size={18} />,
|
||||
},
|
||||
]
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -158,6 +207,16 @@ export function DashboardPage() {
|
||||
<KPICard key={kpi.label} {...kpi} index={i} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Ledger KPI Cards */}
|
||||
<div className="mt-8">
|
||||
<h2 className="text-lg font-semibold mb-4">Ledger (Bokföring)</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-5">
|
||||
{ledgerKpis.map((kpi, i) => (
|
||||
<KPICard key={kpi.label} {...kpi} index={i} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Infrastructure Health — Grafana Integration */}
|
||||
<Card>
|
||||
|
||||
Reference in New Issue
Block a user