185 lines
6.7 KiB
Python
185 lines
6.7 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
|
||
|
)
|
||
|
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 = [(code, schema['descripcion']) for code, schema in schemas.items()]
|
||
|
|
||
|
# 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)
|
||
|
|
||
|
@projects_bp.route('/<int: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('/<int: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 = [(code, schema['descripcion']) for code, schema in schemas.items()]
|
||
|
|
||
|
# 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)
|
||
|
|
||
|
@projects_bp.route('/<int: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('/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])
|