package handlers import ( "database/sql" "encoding/json" "net/http" "boc/legal" "github.com/go-chi/chi/v5" ) // LegalHandler hanterar legal/contract endpoints type LegalHandler struct { DB *sql.DB } func NewLegalHandler(db *sql.DB) *LegalHandler { return &LegalHandler{DB: db} } // Contract representerar ett avtal i systemet type Contract struct { ID string `json:"id"` TemplateType string `json:"template_type"` Name string `json:"name"` Counterparty string `json:"counterparty"` CounterpartyOrg string `json:"counterparty_org,omitempty"` Status string `json:"status"` // draft, pending, active, expired, terminated Value float64 `json:"value,omitempty"` Currency string `json:"currency,omitempty"` StartDate string `json:"start_date,omitempty"` EndDate string `json:"end_date,omitempty"` RenewalDate string `json:"renewal_date,omitempty"` Responsible string `json:"responsible,omitempty"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } // ListContracts returnerar alla avtal func (h *LegalHandler) ListContracts(w http.ResponseWriter, r *http.Request) { rows, err := h.DB.Query(` SELECT id, template_type, name, counterparty, counterparty_org, status, value, currency, start_date, end_date, renewal_date, responsible, created_at, updated_at FROM boc_contracts ORDER BY created_at DESC `) if err != nil { http.Error(w, `{"error":"database error"}`, http.StatusInternalServerError) return } defer rows.Close() contracts := []Contract{} for rows.Next() { var c Contract var value sql.NullFloat64 var currency, startDate, endDate, renewalDate, responsible sql.NullString rows.Scan(&c.ID, &c.TemplateType, &c.Name, &c.Counterparty, &c.CounterpartyOrg, &c.Status, &value, ¤cy, &startDate, &endDate, &renewalDate, &responsible, &c.CreatedAt, &c.UpdatedAt) if value.Valid { c.Value = value.Float64 } if currency.Valid { c.Currency = currency.String } if startDate.Valid { c.StartDate = startDate.String } if endDate.Valid { c.EndDate = endDate.String } if renewalDate.Valid { c.RenewalDate = renewalDate.String } if responsible.Valid { c.Responsible = responsible.String } contracts = append(contracts, c) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "contracts": contracts, "total": len(contracts), }) } // GetContract returnerar ett specifikt avtal func (h *LegalHandler) GetContract(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var c Contract var value sql.NullFloat64 var currency, startDate, endDate, renewalDate, responsible sql.NullString err := h.DB.QueryRow(` SELECT id, template_type, name, counterparty, counterparty_org, status, value, currency, start_date, end_date, renewal_date, responsible, created_at, updated_at FROM boc_contracts WHERE id = $1 `, id).Scan(&c.ID, &c.TemplateType, &c.Name, &c.Counterparty, &c.CounterpartyOrg, &c.Status, &value, ¤cy, &startDate, &endDate, &renewalDate, &responsible, &c.CreatedAt, &c.UpdatedAt) if err == sql.ErrNoRows { http.Error(w, `{"error":"contract not found"}`, http.StatusNotFound) return } if err != nil { http.Error(w, `{"error":"database error"}`, http.StatusInternalServerError) return } if value.Valid { c.Value = value.Float64 } if currency.Valid { c.Currency = currency.String } if startDate.Valid { c.StartDate = startDate.String } if endDate.Valid { c.EndDate = endDate.String } if renewalDate.Valid { c.RenewalDate = renewalDate.String } if responsible.Valid { c.Responsible = responsible.String } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(c) } // GetContractTemplates returnerar alla standardavtal func (h *LegalHandler) GetContractTemplates(w http.ResponseWriter, r *http.Request) { templates := legal.GetStandardTemplates() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "templates": templates, "total": len(templates), }) } // GetContractTemplate returnerar ett specifikt template func (h *LegalHandler) GetContractTemplate(w http.ResponseWriter, r *http.Request) { templateType := chi.URLParam(r, "type") templates := legal.GetStandardTemplates() for _, t := range templates { if string(t.Type) == templateType { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(t) return } } http.Error(w, `{"error":"template not found"}`, http.StatusNotFound) } // GetProductContractLinks returnerar produkt-avtal kopplingar func (h *LegalHandler) GetProductContractLinks(w http.ResponseWriter, r *http.Request) { links := legal.GetProductContractLinks() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "links": links, "total": len(links), }) } // CreateContract skapar ett nytt avtal från template func (h *LegalHandler) CreateContract(w http.ResponseWriter, r *http.Request) { var req struct { TemplateType string `json:"template_type"` Counterparty string `json:"counterparty"` CounterpartyOrg string `json:"counterparty_org,omitempty"` Variables map[string]string `json:"variables,omitempty"` Terms legal.ContractTerms `json:"terms,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) return } // Hitta template var template *legal.ContractTemplate for _, t := range legal.GetStandardTemplates() { if string(t.Type) == req.TemplateType { template = &t break } } if template == nil { http.Error(w, `{"error":"template not found"}`, http.StatusNotFound) return } // Skapa avtal i databas var id string err := h.DB.QueryRow(` INSERT INTO boc_contracts (template_type, name, counterparty, counterparty_org, status, currency) VALUES ($1, $2, $3, $4, 'draft', $5) RETURNING id `, req.TemplateType, template.Name, req.Counterparty, req.CounterpartyOrg, template.DefaultTerms.Currency).Scan(&id) if err != nil { http.Error(w, `{"error":"failed to create contract"}`, http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "id": id, "message": "Contract created", "template": template, }) } // UpdateContract uppdaterar ett avtal func (h *LegalHandler) UpdateContract(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var req struct { Status string `json:"status,omitempty"` Value float64 `json:"value,omitempty"` StartDate string `json:"start_date,omitempty"` EndDate string `json:"end_date,omitempty"` Responsible string `json:"responsible,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) return } _, err := h.DB.Exec(` UPDATE boc_contracts SET status = COALESCE(NULLIF($1, ''), status), value = COALESCE($2, value), start_date = COALESCE(NULLIF($3, ''), start_date), end_date = COALESCE(NULLIF($4, ''), end_date), responsible = COALESCE(NULLIF($5, ''), responsible), updated_at = NOW() WHERE id = $6 `, req.Status, req.Value, req.StartDate, req.EndDate, req.Responsible, id) if err != nil { http.Error(w, `{"error":"failed to update contract"}`, http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "message": "Contract updated", }) }