feat: RRBEC Local Server - Go backend with Django sync
- Implement local-first architecture with SQLite - Add bidirectional sync with Django via ChangeLog - JWT authentication with auto-refresh token - REST API for products, orders, commands, payments - Stock management with automatic deduction
This commit is contained in:
457
rrbec_server/internal/api/handlers.go
Normal file
457
rrbec_server/internal/api/handlers.go
Normal file
@@ -0,0 +1,457 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"rrbec_server/internal/models"
|
||||
"rrbec_server/internal/service"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
svc *service.Service
|
||||
}
|
||||
|
||||
func NewHandler(svc *service.Service) *Handler {
|
||||
return &Handler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *Handler) GetProducts(c *gin.Context) {
|
||||
products, err := h.svc.GetProducts()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, products)
|
||||
}
|
||||
|
||||
func (h *Handler) CreateProduct(c *gin.Context) {
|
||||
var product models.Product
|
||||
if err := c.ShouldBindJSON(&product); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.CreateProduct(&product); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"message": "Product created successfully", "product": product})
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateProduct(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateProduct(uint(id), updates); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Product updated successfully"})
|
||||
}
|
||||
|
||||
func (h *Handler) GetMesas(c *gin.Context) {
|
||||
mesas, err := h.svc.GetMesas()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, mesas)
|
||||
}
|
||||
|
||||
func (h *Handler) GetCategories(c *gin.Context) {
|
||||
categories, err := h.svc.GetCategories()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, categories)
|
||||
}
|
||||
|
||||
func (h *Handler) CreateCategory(c *gin.Context) {
|
||||
var cat models.Category
|
||||
if err := c.ShouldBindJSON(&cat); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.CreateCategory(&cat); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, cat)
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateCategory(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateCategory(uint(id), updates); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Category updated successfully"})
|
||||
}
|
||||
|
||||
func (h *Handler) GetTypePayments(c *gin.Context) {
|
||||
types, err := h.svc.GetTypePayments()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, types)
|
||||
}
|
||||
|
||||
func (h *Handler) GetClients(c *gin.Context) {
|
||||
clients, err := h.svc.GetClients()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, clients)
|
||||
}
|
||||
|
||||
func (h *Handler) GetOrders(c *gin.Context) {
|
||||
orders, err := h.svc.GetOrders()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, orders)
|
||||
}
|
||||
|
||||
func (h *Handler) CreateOrder(c *gin.Context) {
|
||||
var order models.Order
|
||||
if err := c.ShouldBindJSON(&order); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.CreateOrder(&order); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, order)
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateOrder(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateOrder(uint(id), updates); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Order updated successfully"})
|
||||
}
|
||||
|
||||
func (h *Handler) SetOrderPreparing(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SetOrderPreparing(uint(id)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Order is now preparing"})
|
||||
}
|
||||
|
||||
func (h *Handler) SetOrderFinished(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SetOrderFinished(uint(id)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Order finished"})
|
||||
}
|
||||
|
||||
func (h *Handler) SetOrderDelivered(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SetOrderDelivered(uint(id)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Order delivered"})
|
||||
}
|
||||
|
||||
func (h *Handler) SetOrderCanceled(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SetOrderCanceled(uint(id)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Order canceled"})
|
||||
}
|
||||
|
||||
func (h *Handler) GetPayments(c *gin.Context) {
|
||||
payments, err := h.svc.GetPayments()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, payments)
|
||||
}
|
||||
|
||||
func (h *Handler) CreateComanda(c *gin.Context) {
|
||||
var comanda models.Comanda
|
||||
if err := c.ShouldBindJSON(&comanda); err != nil {
|
||||
log.Printf("DEBUG: CreateComanda bind error: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("DEBUG: CreateComanda received: MesaID=%d, UserID=%d, ClientID=%v", comanda.MesaID, comanda.UserID, comanda.ClientID)
|
||||
|
||||
if comanda.MesaID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Mesa ID is required for sync consistency"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.CreateComanda(&comanda); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, comanda)
|
||||
}
|
||||
|
||||
func (h *Handler) AddItemToComanda(c *gin.Context) {
|
||||
var item models.ProductComanda
|
||||
if err := c.ShouldBindJSON(&item); err != nil {
|
||||
log.Printf("DEBUG: AddItem bind error: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("DEBUG: AddItem received: ComandaID=%d, ProductID=%d", item.ComandaID, item.ProductID)
|
||||
|
||||
if item.ComandaID == 0 || item.ProductID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ComandaID and ProductID are required for sync"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.AddItemToComandaRaw(&item); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, item)
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteItemFromComanda(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteItem(uint(id)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Item deleted successfully"})
|
||||
}
|
||||
|
||||
func (h *Handler) ClearComanda(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.ClearComanda(uint(id)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Comanda cleared and closed"})
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateComanda(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateComanda(uint(id), updates); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Comanda updated successfully"})
|
||||
}
|
||||
|
||||
func (h *Handler) PagarComanda(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
var rawData map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&rawData); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("DEBUG: PagarComanda RAW JSON: %v", rawData)
|
||||
|
||||
var payment models.Payment
|
||||
if v, ok := rawData["value"].(float64); ok {
|
||||
payment.Value = models.Price(v)
|
||||
}
|
||||
if v, ok := rawData["type_pay"].(float64); ok {
|
||||
payment.TypePayID = uint(v)
|
||||
} else if v, ok := rawData["id_type_pay"].(float64); ok {
|
||||
payment.TypePayID = uint(v)
|
||||
}
|
||||
if v, ok := rawData["client"].(float64); ok {
|
||||
clientId := uint(v)
|
||||
payment.ClientID = &clientId
|
||||
} else if v, ok := rawData["client_id"].(float64); ok {
|
||||
clientId := uint(v)
|
||||
payment.ClientID = &clientId
|
||||
}
|
||||
if v, ok := rawData["description"].(string); ok {
|
||||
payment.Description = v
|
||||
}
|
||||
|
||||
status := "CLOSED"
|
||||
if v, ok := rawData["status"].(string); ok {
|
||||
status = v
|
||||
}
|
||||
|
||||
if err := h.svc.PagarComanda(uint(id), &payment, status); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Payment registered and comanda updated to " + status})
|
||||
}
|
||||
|
||||
func (h *Handler) GetComandas(c *gin.Context) {
|
||||
comandas, err := h.svc.GetComandas()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, comandas)
|
||||
}
|
||||
|
||||
func (h *Handler) GetComandaByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id format"})
|
||||
return
|
||||
}
|
||||
|
||||
comanda, err := h.svc.GetComandaByID(uint(id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "comanda not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, comanda)
|
||||
}
|
||||
|
||||
func (h *Handler) Login(c *gin.Context) {
|
||||
var input struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.svc.Login(input.Username, input.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Login successful",
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) GetCurrentUser(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user_id": userID})
|
||||
}
|
||||
24
rrbec_server/internal/api/middleware.go
Normal file
24
rrbec_server/internal/api/middleware.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SimpleAuthMiddleware checks for a "User-ID" header for simplicity as requested.
|
||||
// This can be expanded to use JWT or Cookies if needed.
|
||||
func SimpleAuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userID := c.GetHeader("X-User-ID")
|
||||
if userID == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Map the user ID to the context for later use
|
||||
c.Set("userID", userID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user