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("/") @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("//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("//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("//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])