Files
boc/backend/handlers/accounting.go
T
Bernt 78b57273e2 security: Add proper authentication, RBAC, and tenant isolation
- Add password hashing with bcrypt
- Add AuthService with proper login
- Add password strength validation
- Add RBAC middleware (AdminOnly, ManagerOrAdmin)
- Add tenant isolation middleware
- Update CRM handler with tenant filtering
- Add JWT fallback for development mode
- Add user context helpers
- Build successful
2026-08-10 12:52:48 +00:00

242 lines
6.9 KiB
Go

package handlers
import (
"net/http"
"time"
)
// AccountingHandler hanterar dubbel bokföring — egen ledger + Visma
type AccountingHandler struct{}
func NewAccountingHandler() *AccountingHandler {
return &AccountingHandler{}
}
// LedgerEntry representerar en bokföringspost
type LedgerEntry struct {
ID string `json:"id"`
Date string `json:"date"`
VoucherNo string `json:"voucher_no"`
Description string `json:"description"`
Account string `json:"account"`
AccountName string `json:"account_name"`
Debit float64 `json:"debit"`
Credit float64 `json:"credit"`
Balance float64 `json:"balance"`
Source string `json:"source"`
Synced bool `json:"synced"`
VismaID *string `json:"visma_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
// AccountBalance representerar kontosaldo
type AccountBalance struct {
Code string `json:"code"`
Name string `json:"name"`
Type string `json:"type"`
Balance float64 `json:"balance"`
LastUpdated string `json:"last_updated"`
}
// VismaConnection representerar Visma-koppling
type VismaConnection struct {
Connected bool `json:"connected"`
Company string `json:"company"`
OrgNumber string `json:"org_number"`
LastSync time.Time `json:"last_sync"`
SyncStatus string `json:"sync_status"`
PendingSync int `json:"pending_sync"`
}
// GetLedger returnerar egen ledger
type GetLedger struct{}
func (h *AccountingHandler) GetLedger(w http.ResponseWriter, r *http.Request) {
entries := []LedgerEntry{
{
ID: "le-001",
Date: "2026-08-01",
VoucherNo: "V-2026-081",
Description: "Faktura #1001 — Kundtjänst AB",
Account: "1510",
AccountName: "Kundfordringar",
Debit: 25000,
Credit: 0,
Balance: 25000,
Source: "internal",
Synced: true,
VismaID: strPtr("visma-88123"),
CreatedAt: time.Now().Add(-96 * time.Hour),
},
{
ID: "le-002",
Date: "2026-08-01",
VoucherNo: "V-2026-081",
Description: "Faktura #1001 — Kundtjänst AB",
Account: "3010",
AccountName: "Försäljning tjänster",
Debit: 0,
Credit: 25000,
Balance: -25000,
Source: "internal",
Synced: true,
VismaID: strPtr("visma-88124"),
CreatedAt: time.Now().Add(-96 * time.Hour),
},
{
ID: "le-003",
Date: "2026-08-02",
VoucherNo: "V-2026-082",
Description: "Leverantörsfaktura #L-442 — AWS",
Account: "2440",
AccountName: "Leverantörsskulder",
Debit: 0,
Credit: 8500,
Balance: -8500,
Source: "visma",
Synced: true,
VismaID: strPtr("visma-88125"),
CreatedAt: time.Now().Add(-72 * time.Hour),
},
{
ID: "le-004",
Date: "2026-08-02",
VoucherNo: "V-2026-082",
Description: "Leverantörsfaktura #L-442 — AWS",
Account: "6540",
AccountName: "IT-kostnader",
Debit: 8500,
Credit: 0,
Balance: 8500,
Source: "visma",
Synced: true,
VismaID: strPtr("visma-88126"),
CreatedAt: time.Now().Add(-72 * time.Hour),
},
{
ID: "le-005",
Date: "2026-08-03",
VoucherNo: "V-2026-083",
Description: "Lön — Erik Svensson",
Account: "7210",
AccountName: "Löner",
Debit: 45000,
Credit: 0,
Balance: 45000,
Source: "internal",
Synced: false,
VismaID: nil,
CreatedAt: time.Now().Add(-48 * time.Hour),
},
{
ID: "le-006",
Date: "2026-08-03",
VoucherNo: "V-2026-083",
Description: "Lön — Erik Svensson",
Account: "1930",
AccountName: "Företagskonto/checkkonto/räkning",
Debit: 0,
Credit: 45000,
Balance: -45000,
Source: "internal",
Synced: false,
VismaID: nil,
CreatedAt: time.Now().Add(-48 * time.Hour),
},
{
ID: "le-007",
Date: "2026-08-04",
VoucherNo: "V-2026-084",
Description: "Zoomer-utbetalning — Anna Lindqvist",
Account: "7690",
AccountName: "Övriga personalkostnader",
Debit: 5000,
Credit: 0,
Balance: 5000,
Source: "quixzoom",
Synced: false,
VismaID: nil,
CreatedAt: time.Now().Add(-24 * time.Hour),
},
{
ID: "le-008",
Date: "2026-08-04",
VoucherNo: "V-2026-084",
Description: "Zoomer-utbetalning — Anna Lindqvist",
Account: "1930",
AccountName: "Företagskonto/checkkonto/räkning",
Debit: 0,
Credit: 5000,
Balance: -5000,
Source: "quixzoom",
Synced: false,
VismaID: nil,
CreatedAt: time.Now().Add(-24 * time.Hour),
},
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"entries": entries,
"summary": map[string]interface{}{
"total": len(entries),
"synced": 4,
"pending_sync": 4,
"sources": []string{"internal", "visma", "quixzoom"},
},
})
}
// GetAccounts returnerar kontoplan
type GetAccounts struct{}
func (h *AccountingHandler) GetAccounts(w http.ResponseWriter, r *http.Request) {
accounts := []AccountBalance{
{Code: "1510", Name: "Kundfordringar", Type: "asset", Balance: 25000, LastUpdated: "2026-08-05"},
{Code: "1930", Name: "Företagskonto", Type: "asset", Balance: -78500, LastUpdated: "2026-08-05"},
{Code: "2010", Name: "Eget kapital", Type: "equity", Balance: 50000, LastUpdated: "2026-08-01"},
{Code: "2440", Name: "Leverantörsskulder", Type: "liability", Balance: -8500, LastUpdated: "2026-08-02"},
{Code: "2610", Name: "Utgående moms", Type: "liability", Balance: 6250, LastUpdated: "2026-08-01"},
{Code: "3010", Name: "Försäljning tjänster", Type: "revenue", Balance: -25000, LastUpdated: "2026-08-01"},
{Code: "6540", Name: "IT-kostnader", Type: "expense", Balance: 8500, LastUpdated: "2026-08-02"},
{Code: "7210", Name: "Löner", Type: "expense", Balance: 45000, LastUpdated: "2026-08-03"},
{Code: "7690", Name: "Övriga personalkostnader", Type: "expense", Balance: 5000, LastUpdated: "2026-08-04"},
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"accounts": accounts,
})
}
// GetVismaStatus returnerar Visma-kopplingsstatus
type GetVismaStatus struct{}
func (h *AccountingHandler) GetVismaStatus(w http.ResponseWriter, r *http.Request) {
status := VismaConnection{
Connected: true,
Company: "Landvex AB",
OrgNumber: "559141-7042",
LastSync: time.Now().Add(-2 * time.Hour),
SyncStatus: "partial",
PendingSync: 4,
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"visma": status,
})
}
// SyncVisma triggar synk till Visma
type SyncVisma struct{}
func (h *AccountingHandler) SyncVisma(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"message": "Sync initiated",
"status": "syncing",
"pending": 4,
})
}