saveinmed/backend-old/internal/http/handler/company_handler.go
NANDO9322 0a0c344022 feat(geral): implementa fluxo de aprovação, api de endereços e acesso master
Backend:
- Implementa API de Endereços (`POST /enderecos`) e migration da tabela `addresses`.
- Adiciona bloqueio de login para usuários de empresas não verificadas (status `pending`).
- Criação automática do usuário Master (`seedAdmin`) com empresa verificada.
- Adiciona aliases de rota em PT-BR (`/api/v1/empresas` GET/PATCH, `/api/v1/usuarios` PATCH) para compatibilidade com o frontend.
- Atualiza DTOs para suportar campos em português no registro de empresas e atualização de usuários.
- Endpoint `/auth/me` agora retorna `company_name` e flag `superadmin`.
- Ajusta filtro de repositório para listar empresas por status de verificação.

Frontend:
- Nova página `/usuarios-pendentes` com layout padrão e funcionalidade de aprovação.
- Atualiza [Header](cci:1://file:///c:/Projetos/saveinmed/saveinmed-frontend/src/components/Header.tsx:29:0-337:2) para exibir o nome da empresa do usuário logado.
- Serviço `empresaApiService`: correções de mapeamento (`corporate_name` -> `razao_social`) e novos métodos.
- Tipagem atualizada para incluir campos de empresa no [UserData](cci:2://file:///c:/Projetos/saveinmed/saveinmed-frontend/src/types/auth.ts:15:0-30:1).

Fixes:
- Correção de erro 405 (Method Not Allowed) nas rotas de atualização.
- Correção de erro 404 na listagem de pendentes.
- Resolução de campos vazios na listagem de empresas.
2026-01-21 17:20:06 -03:00

285 lines
7.3 KiB
Go

package handler
import (
"errors"
"net/http"
"strconv"
"strings"
"github.com/saveinmed/backend-go/internal/domain"
"github.com/saveinmed/backend-go/internal/http/middleware"
)
// CreateCompany godoc
// @Summary Registro de empresas
// @Description Cadastra farmácia, distribuidora ou administrador com CNPJ e licença sanitária.
// @Tags Empresas
// @Accept json
// @Produce json
// @Param company body registerCompanyRequest true "Dados da empresa"
// @Success 201 {object} domain.Company
// @Router /api/v1/companies [post]
func (h *Handler) CreateCompany(w http.ResponseWriter, r *http.Request) {
var req registerCompanyRequest
if err := decodeJSON(r.Context(), r, &req); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
// Map Portuguese fields if English ones are empty
if req.CorporateName == "" {
req.CorporateName = req.RazaoSocial
}
if req.Category == "" {
// Default category if not provided or map from Activity Code?
// For now, use description or default
if req.DescricaoAtividade != "" {
req.Category = req.DescricaoAtividade
} else {
req.Category = "farmacia" // Default
}
}
company := &domain.Company{
Category: req.Category,
CNPJ: req.CNPJ,
CorporateName: req.CorporateName,
LicenseNumber: req.LicenseNumber, // Frontend might not send this yet?
Latitude: req.Latitude,
Longitude: req.Longitude,
City: req.City,
State: req.State,
Phone: req.Telefone,
}
if err := h.svc.RegisterCompany(r.Context(), company); err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusCreated, company)
}
// ListCompanies godoc
// @Summary Lista empresas
// @Tags Empresas
// @Produce json
// @Success 200 {array} domain.Company
// @Router /api/v1/companies [get]
func (h *Handler) ListCompanies(w http.ResponseWriter, r *http.Request) {
page, pageSize := parsePagination(r)
filter := domain.CompanyFilter{
Category: r.URL.Query().Get("category"),
Search: r.URL.Query().Get("search"),
City: r.URL.Query().Get("city"),
State: r.URL.Query().Get("state"),
}
if v := r.URL.Query().Get("is_verified"); v != "" {
if b, err := strconv.ParseBool(v); err == nil {
filter.IsVerified = &b
}
}
result, err := h.svc.ListCompanies(r.Context(), filter, page, pageSize)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, result)
}
// GetCompany godoc
// @Summary Obter empresa
// @Tags Empresas
// @Produce json
// @Param id path string true "Company ID"
// @Success 200 {object} domain.Company
// @Failure 404 {object} map[string]string
// @Router /api/v1/companies/{id} [get]
func (h *Handler) GetCompany(w http.ResponseWriter, r *http.Request) {
id, err := parseUUIDFromPath(r.URL.Path)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
company, err := h.svc.GetCompany(r.Context(), id)
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
writeJSON(w, http.StatusOK, company)
}
// UpdateCompany godoc
// @Summary Atualizar empresa
// @Tags Empresas
// @Accept json
// @Produce json
// @Param id path string true "Company ID"
// @Param payload body updateCompanyRequest true "Campos para atualização"
// @Success 200 {object} domain.Company
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/companies/{id} [patch]
func (h *Handler) UpdateCompany(w http.ResponseWriter, r *http.Request) {
id, err := parseUUIDFromPath(r.URL.Path)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
var req updateCompanyRequest
if err := decodeJSON(r.Context(), r, &req); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
company, err := h.svc.GetCompany(r.Context(), id)
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
if req.Category != nil {
company.Category = *req.Category
}
if req.CNPJ != nil {
company.CNPJ = *req.CNPJ
}
if req.CorporateName != nil {
company.CorporateName = *req.CorporateName
}
if req.LicenseNumber != nil {
company.LicenseNumber = *req.LicenseNumber
}
if req.IsVerified != nil {
company.IsVerified = *req.IsVerified
}
if req.Latitude != nil {
company.Latitude = *req.Latitude
}
if req.Longitude != nil {
company.Longitude = *req.Longitude
}
if req.City != nil {
company.City = *req.City
}
if req.State != nil {
company.State = *req.State
}
if err := h.svc.UpdateCompany(r.Context(), company); err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, company)
}
// DeleteCompany godoc
// @Summary Remover empresa
// @Tags Empresas
// @Param id path string true "Company ID"
// @Success 204 ""
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/companies/{id} [delete]
func (h *Handler) DeleteCompany(w http.ResponseWriter, r *http.Request) {
id, err := parseUUIDFromPath(r.URL.Path)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
if err := h.svc.DeleteCompany(r.Context(), id); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
// VerifyCompany godoc
// @Summary Verificar empresa
// @Tags Empresas
// @Security BearerAuth
// @Param id path string true "Company ID"
// @Success 200 {object} domain.Company
// @Router /api/v1/companies/{id}/verify [patch]
// VerifyCompany toggles the verification flag for a company (admin only).
func (h *Handler) VerifyCompany(w http.ResponseWriter, r *http.Request) {
if !strings.HasSuffix(r.URL.Path, "/verify") {
http.NotFound(w, r)
return
}
id, err := parseUUIDFromPath(r.URL.Path)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
company, err := h.svc.VerifyCompany(r.Context(), id)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, company)
}
// GetMyCompany godoc
// @Summary Obter minha empresa
// @Tags Empresas
// @Security BearerAuth
// @Produce json
// @Success 200 {object} domain.Company
// @Router /api/v1/companies/me [get]
// GetMyCompany returns the company linked to the authenticated user.
func (h *Handler) GetMyCompany(w http.ResponseWriter, r *http.Request) {
claims, ok := middleware.GetClaims(r.Context())
if !ok || claims.CompanyID == nil {
writeError(w, http.StatusBadRequest, errors.New("missing company context"))
return
}
company, err := h.svc.GetCompany(r.Context(), *claims.CompanyID)
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
writeJSON(w, http.StatusOK, company)
}
// GetCompanyRating godoc
// @Summary Obter avaliação da empresa
// @Tags Empresas
// @Produce json
// @Param id path string true "Company ID"
// @Success 200 {object} domain.CompanyRating
// @Router /api/v1/companies/{id}/rating [get]
// GetCompanyRating exposes the average score for a company.
func (h *Handler) GetCompanyRating(w http.ResponseWriter, r *http.Request) {
if !strings.HasSuffix(r.URL.Path, "/rating") {
http.NotFound(w, r)
return
}
id, err := parseUUIDFromPath(r.URL.Path)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
rating, err := h.svc.GetCompanyRating(r.Context(), id)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, rating)
}