172 lines
5.2 KiB
Go
172 lines
5.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
"workorders/internal/config"
|
|
"workorders/internal/model"
|
|
)
|
|
|
|
type WorkOrderHandler struct {
|
|
db *sqlx.DB
|
|
cfg *config.Config
|
|
}
|
|
|
|
func NewWorkOrderHandler(db *sqlx.DB, cfg *config.Config) *WorkOrderHandler {
|
|
return &WorkOrderHandler{db: db, cfg: cfg}
|
|
}
|
|
|
|
func (h *WorkOrderHandler) List(w http.ResponseWriter, r *http.Request) {
|
|
status := r.URL.Query().Get("status")
|
|
search := r.URL.Query().Get("search")
|
|
if search != "" {
|
|
search = "%" + search + "%"
|
|
}
|
|
|
|
query := `
|
|
SELECT
|
|
wo.id, wo.wo_number, wo.title, wo.status, wo.priority,
|
|
wo.site_name, wo.scheduled_start,
|
|
COUNT(s.id) AS step_count,
|
|
ISNULL(SUM(CAST(s.completed AS INT)),0) AS steps_done,
|
|
COUNT(a.id) AS photo_count
|
|
FROM work_orders wo
|
|
LEFT JOIN wo_steps s ON s.wo_id = wo.id
|
|
LEFT JOIN wo_attachments a ON a.wo_id = wo.id
|
|
WHERE (@p1 = '' OR wo.status = @p1)
|
|
AND (@p2 = '' OR wo.title LIKE @p2 OR wo.wo_number LIKE @p2 OR wo.site_name LIKE @p2)
|
|
GROUP BY wo.id, wo.wo_number, wo.title, wo.status,
|
|
wo.priority, wo.site_name, wo.scheduled_start
|
|
ORDER BY wo.scheduled_start DESC, wo.id DESC`
|
|
|
|
rows := []model.WorkOrderListItem{}
|
|
if err := h.db.Select(&rows, query, status, search); err != nil {
|
|
respondError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
respond(w, http.StatusOK, rows)
|
|
}
|
|
|
|
func (h *WorkOrderHandler) Get(w http.ResponseWriter, r *http.Request) {
|
|
id, err := intParam(r, "id")
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid id")
|
|
return
|
|
}
|
|
var wo model.WorkOrder
|
|
if err = h.db.Get(&wo, `SELECT * FROM work_orders WHERE id = @p1`, id); err != nil {
|
|
respondError(w, http.StatusNotFound, "not found")
|
|
return
|
|
}
|
|
respond(w, http.StatusOK, wo)
|
|
}
|
|
|
|
func (h *WorkOrderHandler) Create(w http.ResponseWriter, r *http.Request) {
|
|
user := userFromCtx(r)
|
|
var body model.WorkOrder
|
|
if err := decode(r, &body); err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid body")
|
|
return
|
|
}
|
|
if body.Priority == "" {
|
|
body.Priority = "normal"
|
|
}
|
|
|
|
var id int
|
|
err := h.db.QueryRow(`
|
|
INSERT INTO work_orders
|
|
(title, description, instructions, status, priority,
|
|
scheduled_start, scheduled_end, site_name, address, lat, lng,
|
|
access_notes, parent_type, parent_id, created_by, updated_at)
|
|
OUTPUT INSERTED.id
|
|
VALUES (@p1,@p2,@p3,'draft',@p4,@p5,@p6,@p7,@p8,@p9,@p10,@p11,@p12,@p13,@p14,GETUTCDATE())`,
|
|
body.Title, body.Description, body.Instructions, body.Priority,
|
|
body.ScheduledStart, body.ScheduledEnd, body.SiteName, body.Address,
|
|
body.Lat, body.Lng, body.AccessNotes, body.ParentType, body.ParentID,
|
|
user.Email,
|
|
).Scan(&id)
|
|
if err != nil {
|
|
respondError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
var wo model.WorkOrder
|
|
h.db.Get(&wo, `SELECT * FROM work_orders WHERE id = @p1`, id)
|
|
respond(w, http.StatusCreated, wo)
|
|
}
|
|
|
|
func (h *WorkOrderHandler) Update(w http.ResponseWriter, r *http.Request) {
|
|
id, err := intParam(r, "id")
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid id")
|
|
return
|
|
}
|
|
var body model.WorkOrder
|
|
if err := decode(r, &body); err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid body")
|
|
return
|
|
}
|
|
_, err = h.db.Exec(`
|
|
UPDATE work_orders SET
|
|
title=@p1, description=@p2, instructions=@p3, priority=@p4,
|
|
scheduled_start=@p5, scheduled_end=@p6, site_name=@p7, address=@p8,
|
|
lat=@p9, lng=@p10, access_notes=@p11, updated_at=GETUTCDATE()
|
|
WHERE id=@p12`,
|
|
body.Title, body.Description, body.Instructions, body.Priority,
|
|
body.ScheduledStart, body.ScheduledEnd, body.SiteName, body.Address,
|
|
body.Lat, body.Lng, body.AccessNotes, id,
|
|
)
|
|
if err != nil {
|
|
respondError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
var wo model.WorkOrder
|
|
h.db.Get(&wo, `SELECT * FROM work_orders WHERE id = @p1`, id)
|
|
respond(w, http.StatusOK, wo)
|
|
}
|
|
|
|
func (h *WorkOrderHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
|
id, err := intParam(r, "id")
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid id")
|
|
return
|
|
}
|
|
h.db.Exec(`DELETE FROM work_orders WHERE id = @p1`, id)
|
|
respond(w, http.StatusOK, map[string]any{"deleted": id})
|
|
}
|
|
|
|
func (h *WorkOrderHandler) UpdateStatus(w http.ResponseWriter, r *http.Request) {
|
|
id, err := intParam(r, "id")
|
|
if err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid id")
|
|
return
|
|
}
|
|
user := userFromCtx(r)
|
|
var body struct {
|
|
Status string `json:"status"`
|
|
}
|
|
if err := decode(r, &body); err != nil {
|
|
respondError(w, http.StatusBadRequest, "invalid body")
|
|
return
|
|
}
|
|
now := time.Now().UTC()
|
|
_, err = h.db.Exec(`
|
|
UPDATE work_orders SET
|
|
status=@p1, updated_at=GETUTCDATE(),
|
|
actual_start = CASE WHEN @p1='in_progress' AND actual_start IS NULL THEN @p2 ELSE actual_start END,
|
|
actual_end = CASE WHEN @p1='closed' THEN @p2 ELSE actual_end END,
|
|
closed_at = CASE WHEN @p1='closed' THEN @p2 ELSE closed_at END,
|
|
closed_by = CASE WHEN @p1='closed' THEN @p3 ELSE closed_by END
|
|
WHERE id=@p4`,
|
|
body.Status, now, user.Email, id,
|
|
)
|
|
if err != nil {
|
|
respondError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
var wo model.WorkOrder
|
|
h.db.Get(&wo, `SELECT * FROM work_orders WHERE id = @p1`, id)
|
|
respond(w, http.StatusOK, wo)
|
|
}
|