Main routes handle core user functionality including loan requests, visit registration, and user dashboards.
Public Routes
GET/POST /fast_loan
Fast loan interface for librarians to quickly register loans for any user.
Blueprint: main
Methods: GET, POST
Template: main/fast_loan.html
Login Required: No
Workflow
- Search Phase: Librarian searches for user by document_id
- Loan Phase: Librarian selects item and confirms loan
User’s document ID to search for
Submit button for search phase
Hidden field with selected user’s ID
Type of item (e.g., ‘computo’, ‘accesorio’)
Number of items (1 for computers, variable for accessories)
Environment for use (e.g., ‘sala’, ‘externo’)
Submit button for loan confirmation
Example
# Search for user
user = User.query.filter_by(document_id=search_form.document_id.data).first()
if not user:
flash('Usuario no encontrado. Debes estar registrado.', 'danger')
return redirect(url_for('main.fast_loan'))
# Process loan
success, reserved_ids, msg = InventoryService.reserve_instances(
catalog_id, quantity
)
if not success:
flash(msg, 'warning')
return redirect(url_for('main.fast_loan'))
try:
for inst_id in reserved_ids:
LoanService.create_loan(
user_id=user_id,
instance_id=inst_id,
environment=environment
)
db.session.commit()
flash('Préstamo rápido registrado con éxito.', 'success')
except Exception as e:
db.session.rollback()
flash('Error al procesar la solicitud.', 'danger')
Source Reference: app/main/routes.py:13
GET/POST /visit
Public kiosk for registering library visits.
Blueprint: main
Methods: GET, POST
Template: main/visit.html
Login Required: No
Purpose of visit (e.g., ‘Estudio’, ‘Lectura’, ‘Préstamo’)
Manual name entry (required only if document_id not registered)
Business Logic
- If document_id exists in system: Uses registered name and role
- If document_id not found: Requires manual name, role set to ‘Visitante’
- Creates
LibraryLog entry with timestamp
Example
document_id = form.document_id.data.strip()
activity = form.activity.data.strip()
manual_name = form.visitor_name.data.strip() if form.visitor_name.data else ''
user = User.query.filter_by(document_id=document_id).first()
if user:
name, role = user.full_name, user.role
else:
if not manual_name:
flash('Documento no registrado. Por favor ingrese su Nombre.', 'warning')
return render_template('main/visit.html', form=form, pre_doc=document_id)
name, role = manual_name, 'Visitante'
visit = LibraryLog(
visitor_name=name,
visitor_id=document_id,
role=role,
activity=activity
)
db.session.add(visit)
db.session.commit()
flash(f'Bienvenido/a {name}. Actividad: {activity}', 'success')
Source Reference: app/main/routes.py:64
GET /
Application home/index page with role-based redirection.
Blueprint: main
Methods: GET
Template: main/index.html
Login Required: No
Behavior
- If authenticated: Redirects to appropriate dashboard based on role
- If not authenticated: Shows public landing page
Source Reference: app/main/routes.py:93
Authenticated Routes
GET /dashboard
User dashboard showing their loans and account information.
Blueprint: main
Methods: GET
Template: premium/dashboard.html
Login Required: Yes
Roles: premium, cliente
Response Data
User’s loans ordered by request_date (most recent first)
Example
@bp.route('/dashboard')
@role_required('premium', 'cliente')
def premium_dashboard():
loans = Loan.query.filter_by(user_id=current_user.id)\
.order_by(Loan.request_date.desc()).all()
return render_template('premium/dashboard.html', loans=loans)
Source Reference: app/main/routes.py:104
GET /profile
User profile page with loan history.
Blueprint: main
Methods: GET
Template: main/profile.html
Login Required: Yes
Roles: All authenticated users
Response Data
User’s complete loan history ordered by request_date
Source Reference: app/main/routes.py:110
GET/POST /request/laptop
Request a laptop loan.
Blueprint: main
Methods: GET, POST
Template: premium/request_laptop.html
Login Required: Yes
Roles: premium, cliente
ID of the computer to request
Number of laptops (1 for ‘cliente’, variable for ‘premium’)
Where laptop will be used (‘sala’ or ‘externo’)
Business Rules
- User must not have existing active computer loan
- Validation via
LoanService.can_request_laptop()
- Premium users can request multiple units
- Cliente users limited to 1 unit
Example
# Validation
can_request, msg = LoanService.can_request_laptop(current_user.id)
if not can_request:
flash(msg, 'warning')
return redirect(url_for('main.premium_dashboard'))
# Quantity based on role
quantity = form.quantity.data if current_user.role == 'premium' else 1
# Transactional reservation
success, reserved_ids, r_msg = InventoryService.reserve_instances(
catalog_id, quantity
)
if not success:
flash(r_msg, 'danger')
return redirect(url_for('main.premium_dashboard'))
try:
for inst_id in reserved_ids:
LoanService.create_loan(
user_id=current_user.id,
instance_id=inst_id,
environment=environment
)
db.session.commit()
flash('Solicitud de portátil enviada correctamente.', 'success')
except Exception as e:
db.session.rollback()
flash('Error al generar el préstamo.', 'danger')
Source Reference: app/main/routes.py:116
GET/POST /request/accessory
Request accessory items (cables, mice, etc.).
Blueprint: main
Methods: GET, POST
Template: premium/request_accessory.html
Login Required: Yes
Roles: premium, cliente
ID of the accessory to request
Number of accessories to request
Business Rules
- Maximum 2 simultaneous accessories per user
- Validation via
LoanService.can_request_accessory()
- Cliente users cannot request ‘computo’ category items
- Premium users can request any non-book items
Example
# Validation
can_request, msg = LoanService.can_request_accessory(current_user.id)
if not can_request:
flash(msg, 'warning')
return redirect(url_for('main.index'))
# Category exclusion based on role
exclude_cat = 'computo' if current_user.role == 'cliente' else None
available_items = CatalogService.get_catalog_with_counts(
exclude_category=exclude_cat
)
# Process request
success, reserved_ids, r_msg = InventoryService.reserve_instances(
catalog_id, quantity
)
if not success:
flash(r_msg, 'danger')
return redirect(url_for('main.request_accessory'))
try:
for inst_id in reserved_ids:
LoanService.create_loan(
user_id=current_user.id,
instance_id=inst_id
)
db.session.commit()
flash('Solicitud realizada con éxito.', 'success')
except Exception as e:
db.session.rollback()
flash('Error al procesar la solicitud.', 'danger')
Source Reference: app/main/routes.py:154
GET/POST /request/book
Request a book loan.
Blueprint: main
Methods: GET, POST
Template: premium/request_book.html
Login Required: Yes
Roles: premium, cliente
ID of the book to request
Business Logic
- Always requests quantity of 1
- Uses optimized
CatalogService.get_catalog_with_counts() to avoid N+1 queries
- Transactional inventory reservation
- User must pick up book at counter
Example
# Get available books (optimized query)
available_books = CatalogService.get_catalog_with_counts(
category_filter='libro'
)
# Reserve single book instance
success, reserved_ids, msg = InventoryService.reserve_instances(
catalog_id, 1
)
if not success:
flash(msg, 'danger')
return redirect(url_for('main.request_book'))
try:
LoanService.create_loan(
user_id=current_user.id,
instance_id=reserved_ids[0]
)
db.session.commit()
flash('Solicitud de libro registrada. Acércate al mostrador.', 'success')
except Exception as e:
db.session.rollback()
flash('Ocurrió un error al registrar la solicitud.', 'danger')
Source Reference: app/main/routes.py:190