feat: registrar proyectos con fases, adjuntar documentos y asignar tareas por Telegram
- crear_fase_proyecto: agrega fases/etapas a un proyecto (se puede llamar varias veces seguidas para cargar todas las fases de un proyecto nuevo). - adjuntar_documento_proyecto: guarda el archivo que el usuario acaba de enviar por Telegram como documento del proyecto (mismo mecanismo de adjuntos ya usado para facturas — se generalizó TelegramAttachment para servir a ambos casos). - listar_usuarios + asignado_id en crear_tarea + asignar_tarea: permite asignar tareas a alguien del equipo por Telegram, disparando la misma notificación (Telegram/email/sistema) que ya dispara el dashboard. - El prompt del sistema ahora indica cómo decidir entre adjuntar_factura y adjuntar_documento_proyecto según el contexto del archivo recibido. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
cd0ca6d232
commit
a0b2e9ae0a
@@ -2,7 +2,10 @@ package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
@@ -50,3 +53,80 @@ func CrearProyectoSimple(clienteID uint, nombre, stack, descripcion string) (*mo
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// CrearFaseProyecto agrega una fase/etapa a un proyecto ya existente, para el
|
||||
// flujo de automatización con IA ("registra el proyecto y coloca las fases").
|
||||
func CrearFaseProyecto(proyectoID uint, nombre, descripcion, estado string, orden int, fechaEstimada *time.Time) (*models.ProyectoFase, error) {
|
||||
if proyectoID == 0 {
|
||||
return nil, fmt.Errorf("proyecto_id requerido")
|
||||
}
|
||||
if strings.TrimSpace(nombre) == "" {
|
||||
return nil, fmt.Errorf("nombre requerido")
|
||||
}
|
||||
if estado == "" {
|
||||
estado = "pendiente"
|
||||
}
|
||||
f := &models.ProyectoFase{
|
||||
ProyectoID: proyectoID,
|
||||
Nombre: nombre,
|
||||
Descripcion: descripcion,
|
||||
Estado: estado,
|
||||
Orden: orden,
|
||||
FechaEstimada: fechaEstimada,
|
||||
}
|
||||
if err := models.CreateProyectoFase(f); err != nil {
|
||||
return nil, fmt.Errorf("no se pudo crear la fase: %w", err)
|
||||
}
|
||||
_ = models.ActualizarProgresoProyecto(proyectoID)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// AdjuntarDocumentoProyecto toma el archivo pendiente de Telegram para este chat
|
||||
// (descargado por el webhook) y lo guarda como documento del proyecto, igual que
|
||||
// si se subiera desde el dashboard.
|
||||
func AdjuntarDocumentoProyecto(chatID int64, proyectoID uint, tipo, nombre, descripcion string) (*models.ProyectoDocumento, error) {
|
||||
if proyectoID == 0 {
|
||||
return nil, fmt.Errorf("proyecto_id requerido")
|
||||
}
|
||||
att, ok := PopTelegramAttachment(chatID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no hay ningún documento pendiente en este chat; pide al usuario que lo envíe de nuevo")
|
||||
}
|
||||
if tipo == "" {
|
||||
tipo = "otro"
|
||||
}
|
||||
if nombre == "" {
|
||||
nombre = att.OriginalName
|
||||
}
|
||||
|
||||
dir := fmt.Sprintf("uploads/proyectos/%d/docs", proyectoID)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
_ = os.Remove(att.Path)
|
||||
return nil, fmt.Errorf("no se pudo crear el directorio: %w", err)
|
||||
}
|
||||
ext := filepath.Ext(att.OriginalName)
|
||||
finalPath := filepath.Join(dir, fmt.Sprintf("telegram_%d%s", time.Now().UnixNano(), ext))
|
||||
if err := os.Rename(att.Path, finalPath); err != nil {
|
||||
return nil, fmt.Errorf("no se pudo guardar el archivo: %w", err)
|
||||
}
|
||||
|
||||
var tamanio int64
|
||||
if info, err := os.Stat(finalPath); err == nil {
|
||||
tamanio = info.Size()
|
||||
}
|
||||
|
||||
d := &models.ProyectoDocumento{
|
||||
ProyectoID: proyectoID,
|
||||
Tipo: tipo,
|
||||
Nombre: nombre,
|
||||
Descripcion: descripcion,
|
||||
Archivo: finalPath,
|
||||
OriginalName: att.OriginalName,
|
||||
TipoMime: att.MimeType,
|
||||
Tamanio: tamanio,
|
||||
}
|
||||
if err := models.CreateProyectoDocumento(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user