package main import ( "encoding/json" "fmt" "log" "net/http" "time" ) type PDRequest struct { TicketID string `json:"ticket_id"` CustomerID string `json:"customer_id"` CustomerName string `json:"customer_name"` Phone string `json:"phone"` Address string `json:"address"` PickupDate string `json:"pickup_date"` DeliveryDate string `json:"delivery_date"` Make string `json:"make"` Model string `json:"model"` WorkDesc string `json:"work_desc"` } type PDUpdateRequest struct { TicketID string `json:"ticket_id"` Status string `json:"status"` DeliveryDate string `json:"delivery_date"` } // FIX: P&D Save handler securely binds delivery_date func handlePDSave(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method != http.MethodPost { http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed) return } var req PDRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, `{"error": "Invalid payload"}`, http.StatusBadRequest) return } if req.TicketID != "" { _, err := db.Exec(`UPDATE pd_tickets SET customer_id = ?, customer_name = ?, phone = ?, address = ?, pickup_date = ?, delivery_date = ?, make = ?, model = ?, work_desc = ? WHERE ticket_id = ?`, req.CustomerID, req.CustomerName, req.Phone, req.Address, req.PickupDate, req.DeliveryDate, req.Make, req.Model, req.WorkDesc, req.TicketID) if err != nil { log.Printf(">> PD TICKET UPDATE ERROR: %v", err) http.Error(w, `{"error": "Failed to update ticket"}`, http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]interface{}{"success": true, "ticket_id": req.TicketID}) return } ticketID := fmt.Sprintf("PD-%05d", time.Now().Unix()%100000) createdAt := time.Now().Format("01/02/2006 15:04:05") _, err := db.Exec(`INSERT INTO pd_tickets (ticket_id, customer_id, customer_name, phone, address, pickup_date, delivery_date, make, model, work_desc, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'PENDING', ?)`, ticketID, req.CustomerID, req.CustomerName, req.Phone, req.Address, req.PickupDate, req.DeliveryDate, req.Make, req.Model, req.WorkDesc, createdAt) if err != nil { log.Printf(">> PD TICKET SAVE ERROR: %v", err) http.Error(w, `{"error": "Failed to save ticket"}`, http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]interface{}{"success": true, "ticket_id": ticketID}) } // FIX: P&D List safely fetches delivery_date instead of old return_date func handlePDList(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") query := ` SELECT ticket_id, customer_id, customer_name, phone, address, pickup_date, COALESCE(delivery_date, ''), make, model, work_desc, status, created_at FROM pd_tickets ORDER BY CASE WHEN status='PENDING' THEN 1 WHEN status='PICKED UP' THEN 2 ELSE 3 END, created_at DESC ` rows, err := db.Query(query) if err != nil { log.Printf(">> PD LIST ERROR: %v", err) w.Write([]byte(`[]`)) return } defer rows.Close() var results []map[string]interface{} for rows.Next() { var tID, cID, cName, phone, addr, pDate, dDate, make, mod, desc, status, cAt string if err := rows.Scan(&tID, &cID, &cName, &phone, &addr, &pDate, &dDate, &make, &mod, &desc, &status, &cAt); err == nil { results = append(results, map[string]interface{}{ "ticket_id": tID, "customer_id": cID, "customer_name": cName, "phone": phone, "address": addr, "pickup_date": pDate, "delivery_date": dDate, "make": make, "model": mod, "work_desc": desc, "status": status, "created_at": cAt, }) } } if results == nil { results = []map[string]interface{}{} } json.NewEncoder(w).Encode(results) } func handlePDUpdate(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method != http.MethodPost { return } var req PDUpdateRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, `{"error": "Invalid request"}`, http.StatusBadRequest) return } _, err := db.Exec("UPDATE pd_tickets SET status = ?, delivery_date = ? WHERE ticket_id = ?", req.Status, req.DeliveryDate, req.TicketID) if err != nil { log.Printf(">> PD UPDATE ERROR: %v", err) http.Error(w, `{"error": "Failed to update"}`, http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]interface{}{"success": true}) } func handlePDDelete(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method != http.MethodDelete { return } tID := r.URL.Query().Get("ticket_id") var status string err := db.QueryRow("SELECT status FROM pd_tickets WHERE ticket_id = ?", tID).Scan(&status) if err != nil || status != "CLOSED" { http.Error(w, `{"error": "Ticket must be CLOSED to delete"}`, http.StatusBadRequest) return } _, err = db.Exec("DELETE FROM pd_tickets WHERE ticket_id = ?", tID) if err != nil { log.Printf(">> PD DELETE ERROR: %v", err) http.Error(w, `{"error": "Failed to delete"}`, http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]interface{}{"success": true}) } // FIX: P&D Alert fetches array of actual Customer Names scheduled for today func handlePDAlert(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") today := time.Now().Format("01/02/2006") rows, err := db.Query("SELECT COALESCE(customer_name, 'UNKNOWN') FROM pd_tickets WHERE status = 'PENDING' AND pickup_date = ?", today) var names []string if err == nil { defer rows.Close() for rows.Next() { var name string if err := rows.Scan(&name); err == nil { names = append(names, name) } } } json.NewEncoder(w).Encode(map[string]interface{}{ "count": len(names), "names": names, }) }