This commit is contained in:
Lizandro Guarnizo
2026-05-18 19:17:05 -05:00
parent 4b2003c752
commit 25f21be966
5 changed files with 230 additions and 3 deletions
+90
View File
@@ -405,6 +405,96 @@ func UpdateEntregableVisibilidad(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"ok": true})
}
// ─── Documentos (contratos / órdenes de servicio) ─────────────────────────────
func GetDocumentos(c *fiber.Ctx) error {
id, _ := strconv.ParseUint(c.Params("id"), 10, 32)
items, err := models.GetDocumentosByProyecto(uint(id))
if err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(items)
}
func UploadDocumento(c *fiber.Ctx) error {
proyID, _ := strconv.ParseUint(c.Params("id"), 10, 32)
if _, err := c.MultipartForm(); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Formato multipart requerido"})
}
nombre := strings.TrimSpace(c.FormValue("nombre"))
descripcion := c.FormValue("descripcion")
tipo := c.FormValue("tipo", "contrato")
validTipos := map[string]bool{"contrato": true, "orden_servicio": true, "otro": true}
if !validTipos[tipo] {
tipo = "otro"
}
file, err := c.FormFile("archivo")
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Archivo requerido"})
}
if file.Size > 50*1024*1024 {
return c.Status(400).JSON(fiber.Map{"error": "Archivo máximo 50MB"})
}
if nombre == "" {
nombre = file.Filename
}
dir := fmt.Sprintf("uploads/proyectos/%d/docs", proyID)
if err := os.MkdirAll(dir, 0755); err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Error al crear directorio"})
}
safeFile := safeFilenameProyecto(file.Filename)
savePath := filepath.Join(dir, safeFile)
if err := c.SaveFile(file, savePath); err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Error al guardar archivo"})
}
d := &models.ProyectoDocumento{
ProyectoID: uint(proyID),
Tipo: tipo,
Nombre: nombre,
Descripcion: descripcion,
Archivo: savePath,
OriginalName: file.Filename,
TipoMime: mimeFromHeader(file),
Tamanio: file.Size,
}
if err := models.CreateProyectoDocumento(d); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.Status(201).JSON(d)
}
func DeleteDocumento(c *fiber.Ctx) error {
docID, _ := strconv.ParseUint(c.Params("docID"), 10, 32)
item, err := models.GetProyectoDocumentoByID(uint(docID))
if err != nil {
return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
}
_ = os.Remove(item.Archivo)
if err := models.DeleteProyectoDocumento(uint(docID)); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"ok": true})
}
func DownloadDocumento(c *fiber.Ctx) error {
docID, _ := strconv.ParseUint(c.Params("docID"), 10, 32)
item, err := models.GetProyectoDocumentoByID(uint(docID))
if err != nil {
return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
}
clean := filepath.Clean(item.Archivo)
if !strings.HasPrefix(clean, "uploads/") {
return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"})
}
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, item.OriginalName))
return c.SendFile(clean)
}
// ─── Tickets (admin) ──────────────────────────────────────────────────────────
func GetTickets(c *fiber.Ctx) error {