package web import ( "bytes" "encoding/json" "fmt" "io" "log" root "marmic/servicetrade-toolbox" "marmic/servicetrade-toolbox/internal/api" "net/http" "strings" ) type StatusButton struct { Status string Label string Class string ConfirmText string } var statusButtons = []StatusButton{ {"draft", "Draft Invoice", "success-button", "Are you sure you want to draft this invoice?"}, {"ok", "Ok Invoice", "success-button", "Are you sure you want to mark this invoice as OK?"}, {"failed", "Fail Invoice", "caution-button", "Are you sure you want to fail this invoice?"}, {"pending_accounting", "Pending Invoice", "caution-button", "Are you sure you want to mark this invoice as pending?"}, {"processed", "Process Invoice", "warning-button", "Are you sure you want to process this invoice?"}, {"void", "Void Invoice", "warning-button", "Are you sure you want to void this invoice?"}, } func InvoicesHandler(w http.ResponseWriter, r *http.Request) { session, ok := r.Context().Value("session").(*api.Session) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if r.Method == "GET" { handleInvoiceSearch(w, r, session) return } tmpl := root.WebTemplates data := map[string]interface{}{ "Title": "Invoices", } var err error if r.Header.Get("HX-Request") == "true" { err = tmpl.ExecuteTemplate(w, "content", data) } else { err = tmpl.ExecuteTemplate(w, "layout.html", data) } if err != nil { log.Printf("Template execution error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } } func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Session) { tmpl := root.WebTemplates searchTerm := strings.TrimSpace(r.URL.Query().Get("search")) if searchTerm == "" { log.Println("Empty search term, returning empty response") w.WriteHeader(http.StatusOK) return } log.Printf("Searching for invoice with term: %s", searchTerm) invoice, err := session.GetInvoice(searchTerm) // log.Printf("GetInvoice result - invoice: %+v, err: %v", invoice, err) if err != nil { log.Printf("Error fetching invoice: %v", err) w.WriteHeader(http.StatusOK) errorMsg := fmt.Sprintf("No invoice found for: %s", searchTerm) if strings.Contains(err.Error(), "access forbidden") { errorMsg = "You do not have permission to view this invoice." } tmpl.ExecuteTemplate(w, "invoice_search_results", map[string]interface{}{ "Error": true, "ErrorMsg": errorMsg, "SearchTerm": searchTerm, }) return } if invoice == nil { log.Printf("No invoice found for: %s", searchTerm) w.WriteHeader(http.StatusOK) tmpl.ExecuteTemplate(w, "invoice_search_results", map[string]interface{}{ "NotFound": true, "ErrorMsg": fmt.Sprintf("No invoice found for: %s", searchTerm), "SearchTerm": searchTerm, }) return } if id, ok := invoice["id"].(float64); ok { invoice["id"] = fmt.Sprintf("%.0f", id) } // Add the buttons to display invoice["buttons"] = getInvoiceStatusButtons(invoice["id"].(string), invoice["status"].(string)) err = tmpl.ExecuteTemplate(w, "invoice_search_results", invoice) if err != nil { log.Printf("Error executing template: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } func getInvoiceStatusButtons(invoiceID, currentStatus string) []map[string]string { var buttons []map[string]string switch currentStatus { case "processed": // Only allow voiding for processed invoices for _, button := range statusButtons { if button.Status == "void" { buttons = append(buttons, map[string]string{ "Action": fmt.Sprintf("/%s-invoice/%s", button.Status, invoiceID), "Label": button.Label, "Class": button.Class, "ConfirmText": button.ConfirmText, }) break } } case "void": // No buttons for voided invoices return buttons default: // For all other statuses, show all buttons except the current status for _, button := range statusButtons { fmt.Printf("btn status: %s, curr status: %s", button.Status, currentStatus) if button.Status != currentStatus { buttons = append(buttons, map[string]string{ "Action": fmt.Sprintf("/%s-invoice/%s", button.Status, invoiceID), "Label": button.Label, "Class": button.Class, "ConfirmText": button.ConfirmText, }) } } } return buttons } func UpdateInvoiceStatusHandler(w http.ResponseWriter, r *http.Request) { session, ok := r.Context().Value("session").(*api.Session) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if r.Method != http.MethodPut { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Extract the invoice ID and status from the URL parts := strings.Split(r.URL.Path, "/") if len(parts) < 3 { http.Error(w, "Invalid request", http.StatusBadRequest) return } invoiceID := parts[len(parts)-1] statusPart := parts[len(parts)-2] // Extract the status from the statusPart status := strings.TrimSuffix(statusPart, "-invoice") if invoiceID == "" { http.Error(w, "Invalid invoice ID", http.StatusBadRequest) return } // Validate the status validStatuses := map[string]bool{ "draft": true, "failed": true, "ok": true, "pending_accounting": true, "processed": true, "void": true, } if !validStatuses[status] { http.Error(w, "Invalid status", http.StatusBadRequest) return } // Prepare the request body requestBody := map[string]string{"status": status} jsonBody, err := json.Marshal(requestBody) if err != nil { http.Error(w, "Error preparing request", http.StatusInternalServerError) return } // Send the PUT request to update the invoice status endpoint := fmt.Sprintf("/invoice/%s", invoiceID) resp, err := session.DoRequest("PUT", endpoint, bytes.NewBuffer(jsonBody)) if err != nil { log.Printf("Error updating invoice status: %v", err) http.Error(w, fmt.Sprintf("Error updating invoice status: %v", err), http.StatusInternalServerError) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) log.Printf("Failed to update invoice status: %s", body) http.Error(w, fmt.Sprintf("Failed to update invoice status: %s", body), resp.StatusCode) return } // If successful, fetch the updated invoice details invoice, err := session.GetInvoice(invoiceID) if err != nil { log.Printf("Error fetching updated invoice: %v", err) http.Error(w, fmt.Sprintf("Error fetching updated invoice: %v", err), http.StatusInternalServerError) return } log.Printf("Updated invoice after status change to %s: %+v", status, invoice) // Render the updated invoice details err = root.WebTemplates.ExecuteTemplate(w, "invoice_search_results", invoice) if err != nil { log.Printf("Error executing template: %v", err) http.Error(w, "Error rendering template", http.StatusInternalServerError) return } }