Creating an advanced-level Leave Management System (LMS) with Flask and Bootstrap 5 involves designing a robust database structure, implementing backend logic, and creating responsive frontend pages. Below is a detailed implementation:

Database Structure

The database will be designed using SQLAlchemy (ORM for Flask). Here's the schema:

Tables

  • Users: Stores user information (employees, managers, and admins).
  • Employees: Stores employee information.
  • Managers: Stores manager information.
  • Leaves: Stores leave details.
  • LeaveTypes: Stores leave types (e.g., annual, sick, casual).
  • LeavePolicies: Stores leave policies (e.g., number of leaves allowed per year).
  • LeaveRequests: Stores leave requests.
  • LeaveApprovals: Stores leave approvals.

Database Models


from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
role = db.Column(db.String(20), default='employee') # Roles: employee, manager, admin
employee = db.relationship('Employee', backref='user', uselist=False)
manager = db.relationship('Manager', backref='user', uselist=False)
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
name = db.Column(db.String(100), nullable=False)
department = db.Column(db.String(100), nullable=False)
designation = db.Column(db.String(100), nullable=False)
leaves = db.relationship('Leave', backref='employee', lazy=True)
leave_requests = db.relationship('LeaveRequest', backref='employee', lazy=True)
class Manager(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
name = db.Column(db.String(100), nullable=False)
department = db.Column(db.String(100), nullable=False)
designation = db.Column(db.String(100), nullable=False)
leave_approvals = db.relationship('LeaveApproval', backref='manager', lazy=True)
class Leave(db.Model):
id = db.Column(db.Integer, primary_key=True)
employee_id = db.Column(db.Integer, db.ForeignKey('employee.id'), nullable=False)
leave_type_id = db.Column(db.Integer, db.ForeignKey('leave_type.id'), nullable=False)
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
number_of_days = db.Column(db.Integer, nullable=False)
class LeaveType(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text, nullable=False)
leaves = db.relationship('Leave', backref='leave_type', lazy=True)
class LeavePolicy(db.Model):
id = db.Column(db.Integer, primary_key=True)
leave_type_id = db.Column(db.Integer, db.ForeignKey('leave_type.id'), nullable=False)
number_of_leaves_allowed = db.Column(db.Integer, nullable=False)
leave_requests = db.relationship('LeaveRequest', backref='leave_policy', lazy=True)
class LeaveRequest(db.Model):
id = db.Column(db.Integer, primary_key=True)
employee_id = db.Column(db.Integer, db.ForeignKey('employee.id'), nullable=False)
leave_policy_id = db.Column(db.Integer, db.ForeignKey('leave_policy.id'), nullable=False)
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
number_of_days = db.Column(db.Integer, nullable=False)
status = db.Column(db.String(20), default='Pending') # Status: Pending, Approved, Rejected
class LeaveApproval(db.Model):
id = db.Column(db.Integer, primary_key=True)
leave_request_id = db.Column(db.Integer, db.ForeignKey('leave_request.id'), nullable=False)
manager_id = db.Column(db.Integer, db.ForeignKey('manager.id'), nullable=False)
approval_date = db.Column(db.DateTime, nullable=False)
status = db.Column(db.String(20), default='Pending') # Status: Pending, Approved, Rejected

Flask Application Structure


leave_management_system/

├── app.py
├── models.py
├── forms.py
├── templates/
│ ├── base.html
│ ├── index.html
│ ├── login.html
│ ├── register.html
│ ├── employee_dashboard.html
│ ├── manager_dashboard.html
│ ├── admin_dashboard.html
│ ├── apply_leave.html
│ ├── view_leaves.html
│ ├── view_leave_requests.html
│ ├── approve_leave_requests.html
│ └── manage_leave_types.html
├── static/
│ ├── css/
│ │ └── styles.css
│ ├── js/
│ │ └── scripts.js
│ └── images/
└── requirements.txt

Flask Application Code


from flask import Flask, render_template, request, redirect, url_for, flash, session
from flask_sqlalchemy import SQLAlchemy
from models import db, User, Employee, Manager, Leave, LeaveType, LeavePolicy, LeaveRequest, LeaveApproval
from forms import LoginForm, RegistrationForm, LeaveRequestForm, LeaveTypeForm
from datetime import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///leave_management.db'
db.init_app(app)
# Routes
@app.route('/')
def index():
return render_template('index.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data, password=form.password.data)
db.session.add(user)
db.session.commit()
flash('Registration successful! Please login.', 'success')
return redirect(url_for('login'))
return render_template('register.html', form=form)
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.password == form.password.data: # Use hashed passwords in production
session['user_id'] = user.id
session['role'] = user.role
flash('Login successful!', 'success')
return redirect(url_for('employee_dashboard') if user.role == 'employee' else 'manager_dashboard' if user.role == 'manager' else 'admin_dashboard')
flash('Login failed. Check your email and password.', 'danger')
return render_template('login.html', form=form)
@app.route('/employee_dashboard')
def employee_dashboard():
if 'user_id' not in session or session['role'] != 'employee':
return redirect(url_for('login'))
employee = Employee.query.filter_by(user_id=session['user_id']).first()
return render_template('employee_dashboard.html', employee=employee)
@app.route('/manager_dashboard')
def manager_dashboard():
if 'user_id' not in session or session['role'] != 'manager':
return redirect(url_for('login'))
manager = Manager.query.filter_by(user_id=session['user_id']).first()
return render_template('manager_dashboard.html', manager=manager)
@app.route('/admin_dashboard')
def admin_dashboard():
if 'user_id' not in session or session['role'] != 'admin':
return redirect(url_for('login'))
return render_template('admin_dashboard.html')
@app.route('/apply_leave', methods=['GET', 'POST'])
def apply_leave():
if 'user_id' not in session or session['role'] != 'employee':
return redirect(url_for('login'))
form = LeaveRequestForm()
if form.validate_on_submit():
leave_request = LeaveRequest(employee_id=session['user_id'], leave_policy_id=form.leave_policy_id.data, start_date=form.start_date.data, end_date=form.end_date.data, number_of_days=form.number_of_days.data)
db.session.add(leave_request)
db.session.commit()
flash('Leave request submitted successfully!', 'success')
return redirect(url_for('employee_dashboard'))
return render_template('apply_leave.html', form=form)
@app.route('/view_leaves')
def view_leaves():
if 'user_id' not in session:
return redirect(url_for('login'))
leaves = Leave.query.filter_by(employee_id=session['user_id']).all()
return render_template('view_leaves.html', leaves=leaves)
@app.route('/view_leave_requests')
def view_leave_requests():
if 'user_id' not in session or session['role'] != 'manager':
return redirect(url_for('login'))
leave_requests = LeaveRequest.query.all()
return render_template('view_leave_requests.html', leave_requests=leave_requests)
@app.route('/approve_leave_requests', methods=['GET', 'POST'])
def approve_leave_requests():
if 'user_id' not in session or session['role'] != 'manager':
return redirect(url_for('login'))
leave_requests = LeaveRequest.query.all()
if request.method == 'POST':
for request_id in request.form.getlist('leave_requests'):
leave_request = LeaveRequest.query.get(request_id)
approval = LeaveApproval(leave_request_id=leave_request.id, manager_id=session['user_id'], approval_date=datetime.now(), status='Approved')
db.session.add(approval)
leave_request.status = 'Approved'
db.session.commit()
flash('Leave requests approved successfully!', 'success')
return redirect(url_for('view_leave_requests'))
return render_template('approve_leave_requests.html', leave_requests=leave_requests)
@app.route('/manage_leave_types', methods=['GET', 'POST'])
def manage_leave_types():
if 'user_id' not in session or session['role'] != 'admin':
return redirect(url_for('login'))
form = LeaveTypeForm()
if form.validate_on_submit():
leave_type = LeaveType(name=form.name.data, description=form.description.data)
db.session.add(leave_type)
db.session.commit()
flash('Leave type added successfully!', 'success')
return redirect(url_for('manage_leave_types'))
leave_types = LeaveType.query.all()
return render_template('manage_leave_types.html', form=form, leave_types=leave_types)
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)

Forms


from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, DateTimeField, IntegerField, SelectField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Email, Length
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=50)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=200)])
submit = SubmitField('Register')
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Login')
class LeaveRequestForm(FlaskForm):
leave_policy_id = SelectField('Leave Policy', coerce=int, validators=[DataRequired()])
start_date = DateTimeField('Start Date', format='%Y-%m-%d %H:%M:%S', validators=[DataRequired()])
end_date = DateTimeField('End Date', format='%Y-%m-%d %H:%M:%S', validators=[DataRequired()])
number_of_days = IntegerField('Number of Days', validators=[DataRequired()])
submit = SubmitField('Apply Leave')
class LeaveTypeForm(FlaskForm):
name = StringField('Leave Type Name', validators=[DataRequired()])
description = TextAreaField('Description', validators=[DataRequired()])
submit = SubmitField('Add Leave Type')

Templates

Base Template


<!DOCTYPE html>
<html lang="en">
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('index') }}">Leave Management</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
{% if 'user_id' in session %}
{% if session['role'] == 'employee' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('employee_dashboard') }}">Dashboard</a>
</li>
{% elif session['role'] == 'manager' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('manager_dashboard') }}">Dashboard</a>
</li>
{% elif session['role'] == 'admin' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin_dashboard') }}">Dashboard</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('register') }}">Register</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-info">
{% for message in messages %}
{{ message }}<br>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Index Template


{% extends 'base.html' %}
{% block content %}
<h1>Welcome to the Leave Management System</h1>
<p>Manage your leaves efficiently and effectively!</p>
<a href="{{ url_for('register') }}" class="btn btn-primary">Register</a>
<a href="{{ url_for('login') }}" class="btn btn-secondary">Login</a>
{% endblock %}

Login Template


{% extends 'base.html' %}
{% block content %}
<h2>Login</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.email.label(class="form-label") }}
{{ form.email(class="form-control") }}
</div>
<div class="mb-3">
{{ form.password.label(class="form-label") }}
{{ form.password(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
{% endblock %}

Register Template


{% extends 'base.html' %}
{% block content %}
<h2>Register</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.username.label(class="form-label") }}
{{ form.username(class="form-control") }}
</div>
<div class="mb-3">
{{ form.email.label(class="form-label") }}
{{ form.email(class="form-control") }}
</div>
<div class="mb-3">
{{ form.password.label(class="form-label") }}
{{ form.password(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
{% endblock %}

Employee Dashboard Template


{% extends 'base.html' %}
{% block content %}
<h2>Employee Dashboard</h2>
<p>Welcome, {{ employee.name }}!</p>
<a href="{{ url_for('apply_leave') }}" class="btn btn-primary">Apply for Leave</a>
<a href="{{ url_for('view_leaves') }}" class="btn btn-secondary">View My Leaves</a>
{% endblock %}

Manager Dashboard Template


{% extends 'base.html' %}
{% block content %}
<h2>Manager Dashboard</h2>
<p>Welcome, {{ manager.name }}!</p>
<a href="{{ url_for('view_leave_requests') }}" class="btn btn-primary">View Leave Requests</a>
{% endblock %}

Admin Dashboard Template


{% extends 'base.html' %}
{% block content %}
<h2>Admin Dashboard</h2>
<p>Welcome, Admin!</p>
<a href="{{ url_for('manage_leave_types') }}" class="btn btn-primary">Manage Leave Types</a>
{% endblock %}

Apply Leave Template


{% extends 'base.html' %}
{% block content %}
<h2>Apply for Leave</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.leave_policy_id.label(class="form-label") }}
{{ form.leave_policy_id(class="form-control") }}
</div>
<div class="mb-3">
{{ form.start_date.label(class="form-label") }}
{{ form.start_date(class="form-control") }}
</div>
<div class="mb-3">
{{ form.end_date.label(class="form-label") }}
{{ form.end_date(class="form-control") }}
</div>
<div class="mb-3">
{{ form.number_of_days.label(class="form-label") }}
{{ form.number_of_days(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
{% endblock %}

View Leaves Template


{% extends 'base.html' %}
{% block content %}
<h2>Your Leaves</h2>
<table class="table">
<thead>
<tr>
<th>Leave Type</th>
<th>Start Date</th>
<th>End Date</th>
<th>Number of Days</th>
</tr>
</thead>
<tbody>
{% for leave in leaves %}
<tr>
<td>{{ leave.leave_type.name }}</td>
<td>{{ leave.start_date }}</td>
<td>{{ leave.end_date }}</td>
<td>{{ leave.number_of_days }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View Leave Requests Template


{% extends 'base.html' %}
{% block content %}
<h2>Leave Requests</h2>
<table class="table">
<thead>
<tr>
<th>Employee</th>
<th>Leave Policy</th>
<th>Start Date</th>
<th>End Date</th>
<th>Number of Days</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for request in leave_requests %}
<tr>
<td>{{ request.employee.name }}</td>
<td>{{ request.leave_policy.number_of_leaves_allowed }}</td>
<td>{{ request.start_date }}</td>
<td>{{ request.end_date }}</td>
<td>{{ request.number_of_days }}</td>
<td>{{ request.status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

Approve Leave Requests Template


{% extends 'base.html' %}
{% block content %}
<h2>Approve Leave Requests</h2>
<form method="POST">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Employee</th>
<th>Leave Policy</th>
<th>Start Date</th>
<th>End Date</th>
<th>Number of Days</th>
</tr>
</thead>
<tbody>
{% for request in leave_requests %}
<tr>
<td><input type="checkbox" name="leave_requests" value="{{ request.id }}"></td>
<td>{{ request.employee.name }}</td>
<td>{{ request.leave_policy.number_of_leaves_allowed }}</td>
<td>{{ request.start_date }}</td>
<td>{{ request.end_date }}</td>
<td>{{ request.number_of_days }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit" class="btn btn-primary">Approve Selected</button>
</form>
{% endblock %}

Manage Leave Types Template


{% extends 'base.html' %}
{% block content %}
<h2>Manage Leave Types</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.name.label(class="form-label") }}
{{ form.name(class="form-control") }}
</div>
<div class="mb-3">
{{ form.description.label(class="form-label") }}
{{ form.description(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
<table class="table mt-4">
<thead>
<tr>
<th>Leave Type Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for leave_type in leave_types %}
<tr>
<td>{{ leave_type.name }}</td>
<td>{{ leave_type.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

Static Files

CSS Styles


body {
background-color: #f8f9fa;
}
.navbar {
margin-bottom: 20px;
}
.table {
margin-top: 20px;
}

JavaScript


// Custom JavaScript can be added here
document.addEventListener('DOMContentLoaded', function() {
console.log("Leave Management System Loaded");
});

Requirements


Flask==2.0.1
Flask-SQLAlchemy==2.5.1
Flask-WTF==0.15.1
WTForms==2.3.3

This structure provides a comprehensive foundation for an advanced Leave Management System using Flask and Bootstrap 5. You can expand upon this by adding features such as leave balance tracking, notifications for leave approvals, and detailed reporting functionalities.