package handlers import ( "database/sql" "encoding/json" "net/http" "time" "github.com/go-chi/chi/v5" ) type LegalHandler struct { DB *sql.DB } func NewLegalHandler(db *sql.DB) *LegalHandler { return &LegalHandler{DB: db} } type Contract struct { ID string `json:"id"` Title string `json:"title"` Counterparty string `json:"counterparty"` Type string `json:"type"` Status string `json:"status"` Value float64 `json:"value"` Currency string `json:"currency"` StartDate *time.Time `json:"start_date"` EndDate *time.Time `json:"end_date"` RenewalDate *time.Time `json:"renewal_date"` DocumentURL string `json:"document_url"` CreatedAt time.Time `json:"created_at"` } type ContractReminder struct { ID string `json:"id"` ContractID string `json:"contract_id"` Type string `json:"type"` DueDate time.Time `json:"due_date"` Status string `json:"status"` } func (h *LegalHandler) ListContracts(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") if status == "" { status = "active" } rows, err := h.DB.Query(` SELECT id, title, counterparty, type, status, value, currency, start_date, end_date, renewal_date, document_url, created_at FROM boc_contracts WHERE status = $1 ORDER BY renewal_date ASC NULLS LAST `, status) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() contracts := []Contract{} for rows.Next() { var c Contract if err := rows.Scan(&c.ID, &c.Title, &c.Counterparty, &c.Type, &c.Status, &c.Value, &c.Currency, &c.StartDate, &c.EndDate, &c.RenewalDate, &c.DocumentURL, &c.CreatedAt); err != nil { continue } contracts = append(contracts, c) } writeJSON(w, http.StatusOK, map[string]interface{}{ "contracts": contracts, "total": len(contracts), }) } func (h *LegalHandler) CreateContract(w http.ResponseWriter, r *http.Request) { var req Contract if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } var id string err := h.DB.QueryRow(` INSERT INTO boc_contracts (title, counterparty, type, status, value, currency, start_date, end_date, renewal_date, document_url) VALUES ($1, $2, $3, 'draft', $4, $5, $6, $7, $8, $9) RETURNING id `, req.Title, req.Counterparty, req.Type, req.Value, req.Currency, req.StartDate, req.EndDate, req.RenewalDate, req.DocumentURL).Scan(&id) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create contract") return } // Create reminder if renewal date is set if req.RenewalDate != nil { reminderDate := req.RenewalDate.AddDate(0, 0, -30) // 30 days before h.DB.Exec(` INSERT INTO boc_contract_reminders (contract_id, type, due_date, status) VALUES ($1, 'renewal', $2, 'pending') `, id, reminderDate) } writeJSON(w, http.StatusCreated, map[string]interface{}{ "id": id, "message": "Contract created", }) } func (h *LegalHandler) GetContract(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var c Contract err := h.DB.QueryRow(` SELECT id, title, counterparty, type, status, value, currency, start_date, end_date, renewal_date, document_url, created_at FROM boc_contracts WHERE id = $1 `, id).Scan(&c.ID, &c.Title, &c.Counterparty, &c.Type, &c.Status, &c.Value, &c.Currency, &c.StartDate, &c.EndDate, &c.RenewalDate, &c.DocumentURL, &c.CreatedAt) if err == sql.ErrNoRows { writeError(w, http.StatusNotFound, "contract not found") return } if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } writeJSON(w, http.StatusOK, c) } func (h *LegalHandler) UpdateContract(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var req Contract if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request") return } _, err := h.DB.Exec(` UPDATE boc_contracts SET title = $1, counterparty = $2, type = $3, status = $4, value = $5, currency = $6, start_date = $7, end_date = $8, renewal_date = $9, document_url = $10 WHERE id = $11 `, req.Title, req.Counterparty, req.Type, req.Status, req.Value, req.Currency, req.StartDate, req.EndDate, req.RenewalDate, req.DocumentURL, id) if err != nil { writeError(w, http.StatusInternalServerError, "failed to update contract") return } writeJSON(w, http.StatusOK, map[string]interface{}{ "message": "Contract updated", }) } func (h *LegalHandler) ListReminders(w http.ResponseWriter, r *http.Request) { rows, err := h.DB.Query(` SELECT r.id, r.contract_id, r.type, r.due_date, r.status, c.title as contract_title FROM boc_contract_reminders r JOIN boc_contracts c ON r.contract_id = c.id WHERE r.status = 'pending' ORDER BY r.due_date ASC LIMIT 50 `) if err != nil { writeError(w, http.StatusInternalServerError, "database error") return } defer rows.Close() reminders := []map[string]interface{}{} for rows.Next() { var id, contractID, reminderType, status, contractTitle string var dueDate time.Time if err := rows.Scan(&id, &contractID, &reminderType, &dueDate, &status, &contractTitle); err != nil { continue } reminders = append(reminders, map[string]interface{}{ "id": id, "contract_id": contractID, "contract_title": contractTitle, "type": reminderType, "due_date": dueDate, "status": status, }) } writeJSON(w, http.StatusOK, map[string]interface{}{ "reminders": reminders, "total": len(reminders), }) }