package handlers import ( "fmt" "net/http" "strconv" "time" "go-crm/internal/db" "go-crm/internal/templates" "github.com/go-chi/chi/v5" ) func (a *App) ListPayments(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) offset, _ := strconv.Atoi(r.URL.Query().Get("offset")) clientID, _ := strconv.ParseInt(r.URL.Query().Get("client_id"), 10, 64) if limit == 0 { limit = 20 } payments, err := db.ListPayments(a.DB, clientID, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } clients, err := db.ListClients(a.DB, accountID, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } customers, err := db.ListCustomers(a.DB, accountID, clientID, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var clientOptions, customerOptions string clientNames := make(map[int64]string) customerNames := make(map[int64]string) for _, c := range clients { clientOptions += fmt.Sprintf(``, c.ClientID, htmlEscape(c.Name)) clientNames[c.ClientID] = c.Name } for _, cu := range customers { customerOptions += fmt.Sprintf(``, cu.CustomerID, htmlEscape(cu.Name)) customerNames[cu.CustomerID] = cu.Name } buf := templates.BufRender() actions := `` fmt.Fprint(buf, templates.PageHeader("Payments", "Track revenue and transactions", actions)) fmt.Fprintf(buf, ` `, clientOptions, customerOptions) if len(payments) == 0 { fmt.Fprint(buf, templates.EmptyState(``, "No payments recorded yet.")) } else { fmt.Fprintf(buf, `
`) fmt.Fprint(buf, templates.TableStart([]string{"Client", "Customer", "Amount", "Paid", "Date", "Method", "Actions"})) for _, p := range payments { paidPill := `unpaid` if p.HasPaid { paidPill = `paid` } clientName := clientNames[p.ClientID] if clientName == "" { clientName = strconv.FormatInt(p.ClientID, 10) } customerName := customerNames[p.CustomerID] if customerName == "" { customerName = strconv.FormatInt(p.CustomerID, 10) } fmt.Fprintf(buf, ` %s %s R$ %.2f %s %s %s
View
`, htmlEscape(clientName), htmlEscape(customerName), p.Amount, paidPill, htmlEscape(p.PaymentDate), htmlEscape(p.PaymentMethod), p.PaymentID, p.PaymentID, p.PaymentID) } fmt.Fprint(buf, templates.TableEnd()) fmt.Fprintf(buf, `
`) for _, p := range payments { clientName := clientNames[p.ClientID] customerName := customerNames[p.CustomerID] fmt.Fprintf(buf, ` `, p.PaymentID, p.PaymentID, p.PaymentID, p.ClientID, htmlEscape(clientName), clientOptions, p.CustomerID, htmlEscape(customerName), customerOptions, p.Amount, htmlEscape(p.PaymentDate), p.PaymentMethod, p.PaymentMethod, p.PaymentID) } } w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.WritePage(w, buf, "Payments", "payments") } func (a *App) CreatePayment(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } r.ParseForm() clientID, _ := strconv.ParseInt(r.FormValue("client_id"), 10, 64) customerID, _ := strconv.ParseInt(r.FormValue("customer_id"), 10, 64) scheduleID, _ := strconv.ParseInt(r.FormValue("schedule_id"), 10, 64) amount, _ := strconv.ParseFloat(r.FormValue("amount"), 64) hasPaid := r.FormValue("has_paid") == "on" payment := db.Payment{ ClientID: clientID, CustomerID: customerID, ScheduleID: scheduleID, HasPaid: hasPaid, Amount: amount, PaymentDate: r.FormValue("payment_date"), PaymentMethod: r.FormValue("payment_method"), CreatedAt: time.Now().Unix(), } if err := payment.Create(a.DB); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("HX-Refresh", "true") } func (a *App) ViewPayment(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) var p db.Payment var hasPaid int err := a.DB.QueryRow("SELECT payment_id, client_id, customer_id, schedule_id, has_paid, amount, payment_date, payment_method, created_at FROM payments WHERE payment_id = ?", id).Scan(&p.PaymentID, &p.ClientID, &p.CustomerID, &p.ScheduleID, &hasPaid, &p.Amount, &p.PaymentDate, &p.PaymentMethod, &p.CreatedAt) if err != nil { http.Error(w, "Payment not found", http.StatusNotFound) return } p.HasPaid = hasPaid == 1 buf := templates.BufRender() fmt.Fprint(buf, templates.PageHeader("Payment", "Transaction details")) paidPill := `unpaid` if p.HasPaid { paidPill = `paid` } fmt.Fprintf(buf, `
%s
Amount R$ %.2f
Method %s
Date %s
`, paidPill, p.Amount, htmlEscape(p.PaymentMethod), htmlEscape(p.PaymentDate)) w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.WritePage(w, buf, "Payment", "payments") } func (a *App) UpdatePayment(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) r.ParseForm() clientID, _ := strconv.ParseInt(r.FormValue("client_id"), 10, 64) customerID, _ := strconv.ParseInt(r.FormValue("customer_id"), 10, 64) amount, _ := strconv.ParseFloat(r.FormValue("amount"), 64) hasPaid := r.FormValue("has_paid") == "on" _, err := a.DB.Exec("UPDATE payments SET client_id=?, customer_id=?, has_paid=?, amount=?, payment_date=?, payment_method=? WHERE payment_id=?", clientID, customerID, hasPaid, amount, r.FormValue("payment_date"), r.FormValue("payment_method"), id) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("HX-Refresh", "true") } func (a *App) DeletePayment(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) a.DB.Exec("DELETE FROM payments WHERE payment_id = ?", id) } // --- package-level shims --- func ListPayments(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).ListPayments(w, r) } func CreatePayment(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).CreatePayment(w, r) } func ViewPayment(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).ViewPayment(w, r) } func UpdatePayment(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).UpdatePayment(w, r) } func DeletePayment(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).DeletePayment(w, r) }