240 lines
7.3 KiB
Python
240 lines
7.3 KiB
Python
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
|
from flask_login import login_required, current_user
|
|
from flask_wtf import FlaskForm
|
|
from wtforms import StringField, SelectField, TextAreaField, SubmitField
|
|
from wtforms.validators import DataRequired, Length
|
|
|
|
from services.project_service import (
|
|
create_project,
|
|
update_project,
|
|
get_project,
|
|
delete_project,
|
|
get_all_projects,
|
|
get_project_children,
|
|
get_project_document_count,
|
|
filter_projects,
|
|
archive_project,
|
|
)
|
|
from services.schema_service import get_all_schemas
|
|
from utils.security import permission_required
|
|
|
|
# Definir Blueprint
|
|
projects_bp = Blueprint("projects", __name__, url_prefix="/projects")
|
|
|
|
|
|
# Formularios
|
|
class ProjectForm(FlaskForm):
|
|
"""Formulario de proyecto."""
|
|
|
|
descripcion = StringField(
|
|
"Descripción", validators=[DataRequired(), Length(1, 100)]
|
|
)
|
|
cliente = StringField("Cliente", validators=[DataRequired(), Length(1, 100)])
|
|
destinacion = StringField("Destinación", validators=[Length(0, 100)])
|
|
esquema = SelectField("Esquema", validators=[DataRequired()])
|
|
proyecto_padre = SelectField("Proyecto Padre", validators=[])
|
|
submit = SubmitField("Guardar")
|
|
|
|
|
|
class ProjectFilterForm(FlaskForm):
|
|
"""Formulario de filtrado de proyectos."""
|
|
|
|
cliente = StringField("Cliente")
|
|
estado = SelectField(
|
|
"Estado",
|
|
choices=[("", "Todos"), ("activo", "Activo"), ("inactivo", "Inactivo")],
|
|
)
|
|
ano_inicio = StringField("Año Inicio")
|
|
ano_fin = StringField("Año Fin")
|
|
descripcion = StringField("Descripción")
|
|
submit = SubmitField("Filtrar")
|
|
|
|
|
|
# Rutas
|
|
@projects_bp.route("/")
|
|
@login_required
|
|
def list():
|
|
"""Listar proyectos."""
|
|
filter_form = ProjectFilterForm(request.args)
|
|
|
|
# Obtener proyectos según filtros
|
|
if request.args:
|
|
projects = filter_projects(request.args)
|
|
else:
|
|
projects = get_all_projects()
|
|
|
|
return render_template(
|
|
"projects/list.html", projects=projects, filter_form=filter_form
|
|
)
|
|
|
|
|
|
@projects_bp.route("/create", methods=["GET", "POST"])
|
|
@login_required
|
|
@permission_required(1000) # Nivel mínimo para crear proyectos
|
|
def create():
|
|
"""Crear nuevo proyecto."""
|
|
form = ProjectForm()
|
|
|
|
# Cargar opciones para esquemas
|
|
schemas = get_all_schemas()
|
|
form.esquema.choices = [
|
|
(schema["id"], schema.get("name", "") or schema.get("descripcion", ""))
|
|
for schema in schemas
|
|
]
|
|
|
|
# Cargar opciones para proyectos padre
|
|
projects = [(p["codigo"], p["descripcion"]) for p in get_all_projects()]
|
|
form.proyecto_padre.choices = [("", "Ninguno")] + projects
|
|
|
|
if form.validate_on_submit():
|
|
# Preparar datos del proyecto
|
|
project_data = {
|
|
"descripcion": form.descripcion.data,
|
|
"cliente": form.cliente.data,
|
|
"destinacion": form.destinacion.data,
|
|
"esquema": form.esquema.data,
|
|
"proyecto_padre": (
|
|
form.proyecto_padre.data if form.proyecto_padre.data else None
|
|
),
|
|
}
|
|
|
|
# Crear proyecto
|
|
success, message, project_id = create_project(
|
|
project_data, current_user.username
|
|
)
|
|
|
|
if success:
|
|
flash(message, "success")
|
|
return redirect(url_for("projects.view", project_id=project_id))
|
|
else:
|
|
flash(message, "danger")
|
|
|
|
return render_template("projects/create.html", form=form, schemas=schemas)
|
|
|
|
|
|
@projects_bp.route("/<project_id>")
|
|
@login_required
|
|
def view(project_id):
|
|
"""Ver detalles de un proyecto."""
|
|
project = get_project(project_id)
|
|
|
|
if not project:
|
|
flash("Proyecto no encontrado.", "danger")
|
|
return redirect(url_for("projects.list"))
|
|
|
|
# Obtener información adicional
|
|
children = get_project_children(project_id)
|
|
document_count = get_project_document_count(project_id)
|
|
|
|
return render_template(
|
|
"projects/view.html",
|
|
project=project,
|
|
children=children,
|
|
document_count=document_count,
|
|
)
|
|
|
|
|
|
@projects_bp.route("/<project_id>/edit", methods=["GET", "POST"])
|
|
@login_required
|
|
@permission_required(5000) # Nivel mínimo para editar proyectos
|
|
def edit(project_id):
|
|
"""Editar un proyecto."""
|
|
project = get_project(project_id)
|
|
|
|
if not project:
|
|
flash("Proyecto no encontrado.", "danger")
|
|
return redirect(url_for("projects.list"))
|
|
|
|
form = ProjectForm()
|
|
|
|
# Cargar opciones para esquemas
|
|
schemas = get_all_schemas()
|
|
form.esquema.choices = [
|
|
(schema["id"], schema.get("name", "") or schema.get("descripcion", ""))
|
|
for schema in schemas
|
|
]
|
|
|
|
# Cargar opciones para proyectos padre (excluyendo este proyecto y sus hijos)
|
|
all_projects = get_all_projects()
|
|
children_codes = [child["codigo"] for child in get_project_children(project_id)]
|
|
available_projects = [
|
|
(p["codigo"], p["descripcion"])
|
|
for p in all_projects
|
|
if p["codigo"] != project["codigo"] and p["codigo"] not in children_codes
|
|
]
|
|
|
|
form.proyecto_padre.choices = [("", "Ninguno")] + available_projects
|
|
|
|
if request.method == "GET":
|
|
# Cargar datos actuales
|
|
form.descripcion.data = project["descripcion"]
|
|
form.cliente.data = project["cliente"]
|
|
form.destinacion.data = project.get("destinacion", "")
|
|
form.esquema.data = project["esquema"]
|
|
form.proyecto_padre.data = project.get("proyecto_padre", "")
|
|
|
|
if form.validate_on_submit():
|
|
# Preparar datos actualizados
|
|
project_data = {
|
|
"descripcion": form.descripcion.data,
|
|
"cliente": form.cliente.data,
|
|
"destinacion": form.destinacion.data,
|
|
"esquema": form.esquema.data,
|
|
"proyecto_padre": (
|
|
form.proyecto_padre.data if form.proyecto_padre.data else None
|
|
),
|
|
}
|
|
|
|
# Actualizar proyecto
|
|
success, message = update_project(
|
|
project_id, project_data, current_user.username
|
|
)
|
|
|
|
if success:
|
|
flash(message, "success")
|
|
return redirect(url_for("projects.view", project_id=project_id))
|
|
else:
|
|
flash(message, "danger")
|
|
|
|
return render_template(
|
|
"projects/edit.html", form=form, project=project, schemas=schemas
|
|
)
|
|
|
|
|
|
@projects_bp.route("/<project_id>/delete", methods=["POST"])
|
|
@login_required
|
|
@permission_required(9000) # Nivel alto para eliminar proyectos
|
|
def delete(project_id):
|
|
"""Eliminar un proyecto (marcar como inactivo)."""
|
|
success, message = delete_project(project_id)
|
|
|
|
if success:
|
|
flash(message, "success")
|
|
else:
|
|
flash(message, "danger")
|
|
|
|
return redirect(url_for("projects.list"))
|
|
|
|
|
|
@projects_bp.route("/<project_id>/archive", methods=["POST"])
|
|
@login_required
|
|
@permission_required(9000) # Nivel alto para archivar proyectos
|
|
def archive(project_id):
|
|
"""Archivar un proyecto."""
|
|
success, message = archive_project(project_id, current_user.username)
|
|
|
|
if success:
|
|
flash(message, "success")
|
|
else:
|
|
flash(message, "danger")
|
|
|
|
return redirect(url_for("projects.list"))
|
|
|
|
|
|
@projects_bp.route("/api/list")
|
|
@login_required
|
|
def api_list():
|
|
"""API para listar proyectos (para selects dinámicos)."""
|
|
projects = get_all_projects()
|
|
return jsonify([{"id": p["codigo"], "text": p["descripcion"]} for p in projects])
|