package handler import ( "errors" "log" "net/http" "strings" "github.com/saveinmed/backend-go/internal/domain" "github.com/saveinmed/backend-go/internal/http/middleware" ) // CreateOrder godoc // @Summary Criação de pedido com split // @Tags Pedidos // @Accept json // @Produce json // @Param order body createOrderRequest true "Pedido" // @Success 201 {object} domain.Order // @Router /api/v1/orders [post] func (h *Handler) CreateOrder(w http.ResponseWriter, r *http.Request) { var req createOrderRequest if err := decodeJSON(r.Context(), r, &req); err != nil { writeError(w, http.StatusBadRequest, err) return } claims, ok := middleware.GetClaims(r.Context()) if !ok || claims.CompanyID == nil { writeError(w, http.StatusBadRequest, errors.New("missing buyer context")) return } order := &domain.Order{ BuyerID: *claims.CompanyID, SellerID: req.SellerID, Items: req.Items, Shipping: req.Shipping, PaymentMethod: domain.PaymentMethod(req.PaymentMethod.Type), } var total int64 for _, item := range req.Items { total += item.UnitCents * item.Quantity } order.TotalCents = total if err := h.svc.CreateOrder(r.Context(), order); err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusCreated, order) } // ListOrders godoc // @Summary Listar pedidos // @Tags Pedidos // @Security BearerAuth // @Produce json // @Success 200 {array} domain.Order // @Router /api/v1/orders [get] func (h *Handler) ListOrders(w http.ResponseWriter, r *http.Request) { log.Printf("📦 [ListOrders] ========== INÍCIO ==========") log.Printf("📦 [ListOrders] URL: %s", r.URL.String()) log.Printf("📦 [ListOrders] Method: %s", r.Method) page, pageSize := parsePagination(r) log.Printf("📦 [ListOrders] Paginação: page=%d, pageSize=%d", page, pageSize) filter := domain.OrderFilter{} // Parse role query param for filtering log.Printf("🔐 [ListOrders] Obtendo requester do contexto...") requester, err := getRequester(r) if err != nil { log.Printf("❌ [ListOrders] ERRO ao obter requester: %v", err) writeError(w, http.StatusUnauthorized, err) return } log.Printf("✅ [ListOrders] Requester obtido: Role=%s, CompanyID=%v", requester.Role, requester.CompanyID) role := r.URL.Query().Get("role") log.Printf("🎭 [ListOrders] Role query param: '%s'", role) // Determine effective role for filtering effectiveRole := role if effectiveRole == "" { // Default to requester role if not provided in query param effectiveRole = strings.ToLower(requester.Role) // Map role variants to canonical filter roles switch effectiveRole { case "dono", "vendedor", "seller", "owner", "comprador", "gerente", "manager": effectiveRole = "owner" case "colaborador", "employee": // Colaboradores pertencem a uma empresa compradora/vendedora; // filtramos como buyer para que vejam apenas pedidos da sua empresa. effectiveRole = "buyer" } log.Printf("🎭 [ListOrders] Effective role derived from requester: '%s'", effectiveRole) } if requester.CompanyID != nil { log.Printf("🏢 [ListOrders] CompanyID do requester: %s", *requester.CompanyID) switch effectiveRole { case "buyer": filter.BuyerID = requester.CompanyID log.Printf("🛒 [ListOrders] Filtro aplicado: BuyerID=%s", *requester.CompanyID) case "owner": filter.SellerID = requester.CompanyID log.Printf("💰 [ListOrders] Filtro aplicado: SellerID=%s", *requester.CompanyID) default: // Admin ou role desconhecido sem filtro explícito: retorna apenas da própria empresa if !domain.IsAdminRole(requester.Role) { filter.BuyerID = requester.CompanyID log.Printf("🔒 [ListOrders] Fallback seguro: BuyerID=%s", *requester.CompanyID) } } } else { log.Printf("⚠️ [ListOrders] Sem filtro de role aplicado. effectiveRole='%s', CompanyID=%v", effectiveRole, requester.CompanyID) } log.Printf("🔍 [ListOrders] Chamando svc.ListOrders com filter=%+v", filter) result, err := h.svc.ListOrders(r.Context(), filter, page, pageSize) if err != nil { log.Printf("❌ [ListOrders] ERRO no service ListOrders: %v", err) writeError(w, http.StatusInternalServerError, err) return } log.Printf("✅ [ListOrders] Sucesso! Total de pedidos: %d", result.Total) log.Printf("📦 [ListOrders] ========== FIM ==========") writeJSON(w, http.StatusOK, result) } // GetOrder godoc // @Summary Consulta pedido // @Tags Pedidos // @Security BearerAuth // @Produce json // @Param id path string true "Order ID" // @Success 200 {object} domain.Order // @Router /api/v1/orders/{id} [get] func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) { id, err := parseUUIDFromPath(r.URL.Path) if err != nil { writeError(w, http.StatusBadRequest, err) return } order, err := h.svc.GetOrder(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, err) return } // Security: non-Admin can only see orders of their own company if claims, ok := middleware.GetClaims(r.Context()); ok { if !domain.IsAdminRole(claims.Role) { if claims.CompanyID == nil || (order.BuyerID != *claims.CompanyID && order.SellerID != *claims.CompanyID) { writeError(w, http.StatusForbidden, errors.New("access denied: order does not belong to your company")) return } } } writeJSON(w, http.StatusOK, order) } // UpdateOrder godoc // @Summary Atualizar itens do pedido // @Tags Pedidos // @Security BearerAuth // @Accept json // @Produce json // @Param id path string true "Order ID" // @Param order body createOrderRequest true "Novos dados (itens)" // @Success 200 {object} domain.Order // @Router /api/v1/orders/{id} [put] func (h *Handler) UpdateOrder(w http.ResponseWriter, r *http.Request) { id, err := parseUUIDFromPath(r.URL.Path) if err != nil { writeError(w, http.StatusBadRequest, err) return } var req createOrderRequest if err := decodeJSON(r.Context(), r, &req); err != nil { writeError(w, http.StatusBadRequest, err) return } var total int64 for _, item := range req.Items { total += item.UnitCents * item.Quantity } // FIX: UpdateOrderItems expects []domain.OrderItem // req.Items is []domain.OrderItem (from dto.go definition) if err := h.svc.UpdateOrderItems(r.Context(), id, req.Items, total); err != nil { writeError(w, http.StatusInternalServerError, err) return } // Return updated order order, err := h.svc.GetOrder(r.Context(), id) if err != nil { writeJSON(w, http.StatusOK, map[string]string{"status": "updated"}) return } writeJSON(w, http.StatusOK, order) } // UpdateOrderStatus godoc // @Summary Atualiza status do pedido // @Tags Pedidos // @Security BearerAuth // @Accept json // @Produce json // @Param id path string true "Order ID" // @Param status body updateStatusRequest true "Novo status" // @Success 204 "" // @Router /api/v1/orders/{id}/status [patch] func (h *Handler) UpdateOrderStatus(w http.ResponseWriter, r *http.Request) { id, err := parseUUIDFromPath(r.URL.Path) if err != nil { writeError(w, http.StatusBadRequest, err) return } var req updateStatusRequest if err := decodeJSON(r.Context(), r, &req); err != nil { writeError(w, http.StatusBadRequest, err) return } if !isValidStatus(req.Status) { writeError(w, http.StatusBadRequest, errors.New("invalid status")) return } // Security: non-Admin can only update status of orders from their own company if claims, ok := middleware.GetClaims(r.Context()); ok { if !domain.IsAdminRole(claims.Role) { order, err := h.svc.GetOrder(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, err) return } if claims.CompanyID == nil || (order.BuyerID != *claims.CompanyID && order.SellerID != *claims.CompanyID) { writeError(w, http.StatusForbidden, errors.New("access denied: order does not belong to your company")) return } } } if err := h.svc.UpdateOrderStatus(r.Context(), id, domain.OrderStatus(req.Status)); err != nil { writeError(w, http.StatusInternalServerError, err) return } w.WriteHeader(http.StatusNoContent) } // DeleteOrder godoc // @Summary Remover pedido // @Tags Pedidos // @Security BearerAuth // @Param id path string true "Order ID" // @Success 204 "" // @Failure 400 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /api/v1/orders/{id} [delete] func (h *Handler) DeleteOrder(w http.ResponseWriter, r *http.Request) { id, err := parseUUIDFromPath(r.URL.Path) if err != nil { writeError(w, http.StatusBadRequest, err) return } // Security: non-Admin can only delete orders where they are the buyer if claims, ok := middleware.GetClaims(r.Context()); ok { if !domain.IsAdminRole(claims.Role) { order, err := h.svc.GetOrder(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, err) return } if claims.CompanyID == nil || order.BuyerID != *claims.CompanyID { writeError(w, http.StatusForbidden, errors.New("access denied: only the buyer company can delete an order")) return } } } if err := h.svc.DeleteOrder(r.Context(), id); err != nil { writeError(w, http.StatusBadRequest, err) return } w.WriteHeader(http.StatusNoContent) }