Files
boc/backend/handlers/ai_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

191 lines
5.6 KiB
Go

package handlers
import (
"encoding/json"
"net/http"
"strings"
)
// AIAccountingHandler hanterar AI-driven bokföringsförslag
type AIAccountingHandler struct{}
func NewAIAccountingHandler() *AIAccountingHandler {
return &AIAccountingHandler{}
}
// AISuggestion representerar ett AI-förslag
type AISuggestion struct {
ID string `json:"id"`
Description string `json:"description"`
SuggestedAccount string `json:"suggested_account"`
AccountName string `json:"account_name"`
Confidence float64 `json:"confidence"`
Debit float64 `json:"debit"`
Credit float64 `json:"credit"`
Reason string `json:"reason"`
}
// GetSuggestions returnerar AI-förslag för en transaktion
func (h *AIAccountingHandler) GetSuggestions(w http.ResponseWriter, r *http.Request) {
var req struct {
Description string `json:"description"`
Amount float64 `json:"amount"`
Counterparty string `json:"counterparty"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
suggestions := h.analyzeTransaction(req.Description, req.Amount, req.Counterparty)
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"suggestions": suggestions,
"input": req,
})
}
// analyzeTransaction analyserar en transaktion och ger förslag
func (h *AIAccountingHandler) analyzeTransaction(description string, amount float64, counterparty string) []AISuggestion {
desc := strings.ToLower(description)
counter := strings.ToLower(counterparty)
var suggestions []AISuggestion
// Regelbaserad AI (kan bytas mot ML-modell)
switch {
case containsAny(desc, []string{"faktura", "inbetalning", "betalning"}) && amount > 0:
suggestions = append(suggestions, AISuggestion{
ID: "ai-001",
Description: description,
SuggestedAccount: "1510",
AccountName: "Kundfordringar",
Confidence: 0.92,
Debit: amount,
Credit: 0,
Reason: "Positivt belopp med fakturareferens = kundfordran",
})
suggestions = append(suggestions, AISuggestion{
ID: "ai-002",
Description: description,
SuggestedAccount: "3010",
AccountName: "Försäljning tjänster",
Confidence: 0.88,
Debit: 0,
Credit: amount,
Reason: "Motkonto till kundfordran = försäljning",
})
case containsAny(desc, []string{"lön", "salary", "löneutbetalning"}):
suggestions = append(suggestions, AISuggestion{
ID: "ai-003",
Description: description,
SuggestedAccount: "7210",
AccountName: "Löner",
Confidence: 0.95,
Debit: amount,
Credit: 0,
Reason: "Löneutbetalning = lönekonto",
})
suggestions = append(suggestions, AISuggestion{
ID: "ai-004",
Description: description,
SuggestedAccount: "1930",
AccountName: "Företagskonto",
Confidence: 0.95,
Debit: 0,
Credit: amount,
Reason: "Lön betalas från företagskonto",
})
case containsAny(desc, []string{"aws", "hosting", "server", "cloud"}):
suggestions = append(suggestions, AISuggestion{
ID: "ai-005",
Description: description,
SuggestedAccount: "6540",
AccountName: "IT-kostnader",
Confidence: 0.89,
Debit: amount,
Credit: 0,
Reason: "AWS/hosting = IT-kostnader",
})
case containsAny(desc, []string{"zoomer", "quixzoom", "fältarbetare"}):
suggestions = append(suggestions, AISuggestion{
ID: "ai-006",
Description: description,
SuggestedAccount: "7690",
AccountName: "Övriga personalkostnader",
Confidence: 0.87,
Debit: amount,
Credit: 0,
Reason: "Zoomer-utbetalning = personalkostnad",
})
case containsAny(desc, []string{"försäkring", "insurance"}):
suggestions = append(suggestions, AISuggestion{
ID: "ai-007",
Description: description,
SuggestedAccount: "6310",
AccountName: "Försäkringspremier",
Confidence: 0.91,
Debit: amount,
Credit: 0,
Reason: "Försäkringsbetalning = försäkringspremie",
})
case containsAny(counter, []string{"skatteverket", "skatt"}):
suggestions = append(suggestions, AISuggestion{
ID: "ai-008",
Description: description,
SuggestedAccount: "2012",
AccountName: "Skatter",
Confidence: 0.94,
Debit: amount,
Credit: 0,
Reason: "Skatteverket = skattebetalning",
})
default:
// Generiskt förslag baserat på belopp
if amount > 0 {
suggestions = append(suggestions, AISuggestion{
ID: "ai-099",
Description: description,
SuggestedAccount: "1930",
AccountName: "Företagskonto",
Confidence: 0.45,
Debit: 0,
Credit: amount,
Reason: "Kunde inte identifiera — granska manuellt",
})
} else {
suggestions = append(suggestions, AISuggestion{
ID: "ai-099",
Description: description,
SuggestedAccount: "6991",
AccountName: "Övriga externa kostnader",
Confidence: 0.45,
Debit: -amount,
Credit: 0,
Reason: "Kunde inte identifiera — granska manuellt",
})
}
}
return suggestions
}
func containsAny(s string, substrs []string) bool {
for _, substr := range substrs {
if strings.Contains(s, substr) {
return true
}
}
return false
}