Added new role Accounting
This commit is contained in:
@@ -218,10 +218,10 @@ def cache_coordinates(address, lat, lng, accuracy):
|
|||||||
print(f"⚠️ Error caching coordinates: {e}")
|
print(f"⚠️ Error caching coordinates: {e}")
|
||||||
|
|
||||||
# Valid user roles with new additions
|
# Valid user roles with new additions
|
||||||
VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager']
|
VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager', 'accounting']
|
||||||
|
|
||||||
# Roles that have staff-level permissions (non-admin roles)
|
# Roles that have staff-level permissions (non-admin roles)
|
||||||
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager']
|
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager', 'accounting']
|
||||||
|
|
||||||
# Import and initialize models
|
# Import and initialize models
|
||||||
from models import set_db
|
from models import set_db
|
||||||
@@ -303,6 +303,24 @@ def get_role_permissions(role):
|
|||||||
'Cannot manage other users',
|
'Cannot manage other users',
|
||||||
'Cannot access admin settings'
|
'Cannot access admin settings'
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
'accounting': {
|
||||||
|
'title': 'Accounting Specialist Permissions',
|
||||||
|
'permissions': [
|
||||||
|
'View and modify employee records',
|
||||||
|
'Access attendance reports and analytics',
|
||||||
|
'View and manage time attendance data',
|
||||||
|
'Export payroll and attendance data',
|
||||||
|
'Access financial reports and statistics',
|
||||||
|
'Update personal profile information',
|
||||||
|
'Delete attendance records (same as payroll)'
|
||||||
|
],
|
||||||
|
'restrictions': [
|
||||||
|
'Cannot create or delete QR codes',
|
||||||
|
'Cannot manage other users',
|
||||||
|
'Cannot access admin settings',
|
||||||
|
'Cannot manage projects'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return permissions.get(role, {})
|
return permissions.get(role, {})
|
||||||
@@ -2659,6 +2677,7 @@ def user_stats_api():
|
|||||||
staff_users = User.query.filter_by(role='staff', active_status=True).count()
|
staff_users = User.query.filter_by(role='staff', active_status=True).count()
|
||||||
payroll_users = User.query.filter_by(role='payroll', active_status=True).count()
|
payroll_users = User.query.filter_by(role='payroll', active_status=True).count()
|
||||||
project_manager_users = User.query.filter_by(role='project_manager', active_status=True).count()
|
project_manager_users = User.query.filter_by(role='project_manager', active_status=True).count()
|
||||||
|
accounting_users = User.query.filter_by(role='accounting', active_status=True).count()
|
||||||
inactive_users = User.query.filter_by(active_status=False).count()
|
inactive_users = User.query.filter_by(active_status=False).count()
|
||||||
|
|
||||||
recent_registrations = User.query.filter(
|
recent_registrations = User.query.filter(
|
||||||
@@ -2676,6 +2695,7 @@ def user_stats_api():
|
|||||||
'staff_users': staff_users,
|
'staff_users': staff_users,
|
||||||
'payroll_users': payroll_users,
|
'payroll_users': payroll_users,
|
||||||
'project_manager_users': project_manager_users,
|
'project_manager_users': project_manager_users,
|
||||||
|
'accounting_users': accounting_users,
|
||||||
'inactive_users': inactive_users,
|
'inactive_users': inactive_users,
|
||||||
'recent_registrations': recent_registrations,
|
'recent_registrations': recent_registrations,
|
||||||
'recent_logins': recent_logins
|
'recent_logins': recent_logins
|
||||||
@@ -5132,7 +5152,7 @@ def attendance_report():
|
|||||||
def edit_attendance(record_id):
|
def edit_attendance(record_id):
|
||||||
"""Edit attendance record (Admin and Payroll only)"""
|
"""Edit attendance record (Admin and Payroll only)"""
|
||||||
# Check if user has permission to edit attendance records
|
# Check if user has permission to edit attendance records
|
||||||
if session.get('role') not in ['admin', 'payroll']:
|
if session.get('role') not in ['admin', 'payroll', 'accounting']:
|
||||||
flash('Access denied. Only administrators and payroll staff can edit attendance records.', 'error')
|
flash('Access denied. Only administrators and payroll staff can edit attendance records.', 'error')
|
||||||
return redirect(url_for('attendance_report'))
|
return redirect(url_for('attendance_report'))
|
||||||
|
|
||||||
@@ -5252,7 +5272,7 @@ def edit_attendance(record_id):
|
|||||||
def delete_attendance(record_id):
|
def delete_attendance(record_id):
|
||||||
"""Delete attendance record (Admin and Payroll only)"""
|
"""Delete attendance record (Admin and Payroll only)"""
|
||||||
# Check if user has permission to delete attendance records
|
# Check if user has permission to delete attendance records
|
||||||
if session.get('role') not in ['admin', 'payroll']:
|
if session.get('role') not in ['admin', 'payroll', 'accounting']:
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
@@ -5320,7 +5340,7 @@ def verification_review():
|
|||||||
"""Admin page to review pending photo verifications"""
|
"""Admin page to review pending photo verifications"""
|
||||||
try:
|
try:
|
||||||
# Only admins can access
|
# Only admins can access
|
||||||
if session.get('role') not in ['admin', 'payroll']:
|
if session.get('role') not in ['admin', 'payroll', 'accounting']:
|
||||||
flash('Unauthorized access.', 'error')
|
flash('Unauthorized access.', 'error')
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
@@ -5382,7 +5402,7 @@ def update_verification_status(record_id):
|
|||||||
"""Update verification status (approve/reject)"""
|
"""Update verification status (approve/reject)"""
|
||||||
try:
|
try:
|
||||||
# Only admins can update
|
# Only admins can update
|
||||||
if session.get('role') not in ['admin', 'payroll']:
|
if session.get('role') not in ['admin', 'payroll', 'accounting']:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': 'Unauthorized access'
|
'message': 'Unauthorized access'
|
||||||
@@ -5448,7 +5468,7 @@ def get_verification_details(record_id):
|
|||||||
|
|
||||||
# Check if user has permission to view
|
# Check if user has permission to view
|
||||||
# Allow admin and payroll staff to view verification details
|
# Allow admin and payroll staff to view verification details
|
||||||
if session.get('role') not in ['admin', 'payroll']:
|
if session.get('role') not in ['admin', 'payroll', 'accounting']:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': 'Unauthorized access'
|
'message': 'Unauthorized access'
|
||||||
@@ -5567,7 +5587,7 @@ def export_configuration():
|
|||||||
"""Display export configuration page for customizing Excel exports"""
|
"""Display export configuration page for customizing Excel exports"""
|
||||||
try:
|
try:
|
||||||
user_role = session.get('role')
|
user_role = session.get('role')
|
||||||
if user_role not in ['admin', 'payroll']:
|
if user_role not in ['admin', 'payroll', 'accounting']:
|
||||||
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted unauthorized access to export configuration")
|
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted unauthorized access to export configuration")
|
||||||
flash('Access denied. Only administrators and payroll staff can access export configuration.', 'error')
|
flash('Access denied. Only administrators and payroll staff can access export configuration.', 'error')
|
||||||
return redirect(url_for('attendance_report'))
|
return redirect(url_for('attendance_report'))
|
||||||
@@ -5654,7 +5674,7 @@ def generate_excel_export():
|
|||||||
"""Generate and download Excel file with selected columns in specified order"""
|
"""Generate and download Excel file with selected columns in specified order"""
|
||||||
try:
|
try:
|
||||||
user_role = session.get('role')
|
user_role = session.get('role')
|
||||||
if user_role not in ['admin', 'payroll']:
|
if user_role not in ['admin', 'payroll', 'accounting']:
|
||||||
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted unauthorized Excel export")
|
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted unauthorized Excel export")
|
||||||
flash('Access denied. Only administrators and payroll staff can export data.', 'error')
|
flash('Access denied. Only administrators and payroll staff can export data.', 'error')
|
||||||
return redirect(url_for('attendance_report'))
|
return redirect(url_for('attendance_report'))
|
||||||
@@ -6293,7 +6313,7 @@ def payroll_dashboard():
|
|||||||
try:
|
try:
|
||||||
# Check if user has payroll access
|
# Check if user has payroll access
|
||||||
user_role = session.get('role')
|
user_role = session.get('role')
|
||||||
if user_role not in ['admin', 'payroll']:
|
if user_role not in ['admin', 'payroll', 'accounting']:
|
||||||
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted to access payroll dashboard without permissions")
|
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted to access payroll dashboard without permissions")
|
||||||
flash('Access denied. Only administrators and payroll staff can access payroll features.', 'error')
|
flash('Access denied. Only administrators and payroll staff can access payroll features.', 'error')
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
@@ -6440,7 +6460,7 @@ def export_payroll_excel():
|
|||||||
try:
|
try:
|
||||||
# Check permissions
|
# Check permissions
|
||||||
user_role = session.get('role')
|
user_role = session.get('role')
|
||||||
if user_role not in ['admin', 'payroll']:
|
if user_role not in ['admin', 'payroll', 'accounting']:
|
||||||
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted unauthorized payroll Excel export")
|
logger_handler.logger.warning(f"User {session.get('username', 'unknown')} (role: {user_role}) attempted unauthorized payroll Excel export")
|
||||||
flash('Access denied. Only administrators and payroll staff can export payroll data.', 'error')
|
flash('Access denied. Only administrators and payroll staff can export payroll data.', 'error')
|
||||||
return redirect(url_for('payroll_dashboard'))
|
return redirect(url_for('payroll_dashboard'))
|
||||||
@@ -6681,7 +6701,7 @@ def calculate_working_hours_api():
|
|||||||
try:
|
try:
|
||||||
# Check permissions
|
# Check permissions
|
||||||
user_role = session.get('role')
|
user_role = session.get('role')
|
||||||
if user_role not in ['admin', 'payroll']:
|
if user_role not in ['admin', 'payroll', 'accounting']:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': 'Access denied. Insufficient permissions.'
|
'message': 'Access denied. Insufficient permissions.'
|
||||||
@@ -6758,7 +6778,7 @@ def get_miss_punch_details(employee_id):
|
|||||||
try:
|
try:
|
||||||
# Check permissions
|
# Check permissions
|
||||||
user_role = session.get('role')
|
user_role = session.get('role')
|
||||||
if user_role not in ['admin', 'payroll']:
|
if user_role not in ['admin', 'payroll', 'accounting']:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': 'Access denied. Insufficient permissions.'
|
'message': 'Access denied. Insufficient permissions.'
|
||||||
@@ -7152,7 +7172,7 @@ def export_statistics():
|
|||||||
"""Export statistics data to CSV/Excel"""
|
"""Export statistics data to CSV/Excel"""
|
||||||
try:
|
try:
|
||||||
# Check permissions
|
# Check permissions
|
||||||
if session.get('role') not in ['admin', 'payroll']:
|
if session.get('role') not in ['admin', 'payroll', 'accounting']:
|
||||||
return jsonify({'error': 'Access denied'}), 403
|
return jsonify({'error': 'Access denied'}), 403
|
||||||
|
|
||||||
# Log export attempt
|
# Log export attempt
|
||||||
|
|||||||
+3
-2
@@ -10,7 +10,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Valid user roles (kept in sync with app.py)
|
# Valid user roles (kept in sync with app.py)
|
||||||
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager']
|
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager', 'accounting']
|
||||||
|
|
||||||
# Import db from app - this works because app.py imports this file after db is created
|
# Import db from app - this works because app.py imports this file after db is created
|
||||||
import sys
|
import sys
|
||||||
@@ -63,6 +63,7 @@ class User(base.db.Model):
|
|||||||
'admin': 'Administrator',
|
'admin': 'Administrator',
|
||||||
'staff': 'Staff User',
|
'staff': 'Staff User',
|
||||||
'payroll': 'Payroll Specialist',
|
'payroll': 'Payroll Specialist',
|
||||||
'project_manager': 'Project Manager'
|
'project_manager': 'Project Manager',
|
||||||
|
'accounting': 'Accounting Specialist'
|
||||||
}
|
}
|
||||||
return role_names.get(self.role, self.role.title())
|
return role_names.get(self.role, self.role.title())
|
||||||
@@ -107,6 +107,10 @@
|
|||||||
background: linear-gradient(90deg, #17a2b8, #138496);
|
background: linear-gradient(90deg, #17a2b8, #138496);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-card.accounting::before {
|
||||||
|
background: linear-gradient(90deg, #059669, #047857);
|
||||||
|
}
|
||||||
|
|
||||||
.stat-card.project-manager::before {
|
.stat-card.project-manager::before {
|
||||||
background: linear-gradient(90deg, #6f42c1, #5a2d91);
|
background: linear-gradient(90deg, #6f42c1, #5a2d91);
|
||||||
}
|
}
|
||||||
@@ -149,6 +153,10 @@
|
|||||||
background: linear-gradient(135deg, #17a2b8, #138496);
|
background: linear-gradient(135deg, #17a2b8, #138496);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-icon.accounting {
|
||||||
|
background: linear-gradient(135deg, #059669, #047857);
|
||||||
|
}
|
||||||
|
|
||||||
.stat-icon.project-manager {
|
.stat-icon.project-manager {
|
||||||
background: linear-gradient(135deg, #6f42c1, #5a2d91);
|
background: linear-gradient(135deg, #6f42c1, #5a2d91);
|
||||||
}
|
}
|
||||||
@@ -385,6 +393,12 @@
|
|||||||
border: 1px solid rgba(23, 162, 184, 0.3);
|
border: 1px solid rgba(23, 162, 184, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-role.accounting {
|
||||||
|
background-color: rgba(5, 150, 105, 0.1);
|
||||||
|
color: #047857;
|
||||||
|
border: 1px solid rgba(5, 150, 105, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
.user-role.project_manager {
|
.user-role.project_manager {
|
||||||
background-color: rgba(111, 66, 193, 0.1);
|
background-color: rgba(111, 66, 193, 0.1);
|
||||||
color: #5a2d91;
|
color: #5a2d91;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
<i class="fas fa-clipboard-list"></i>
|
<i class="fas fa-clipboard-list"></i>
|
||||||
<span class="menu-text">System Logs</span>
|
<span class="menu-text">System Logs</span>
|
||||||
</a>
|
</a>
|
||||||
{% elif session.role in ['payroll'] %}
|
{% elif session.role in ['payroll', 'accounting'] %}
|
||||||
<a href="{{ url_for('employees') }}" class="menu-item">
|
<a href="{{ url_for('employees') }}" class="menu-item">
|
||||||
<i class="fas fa-user"></i>
|
<i class="fas fa-user"></i>
|
||||||
<span class="menu-text">Employees</span>
|
<span class="menu-text">Employees</span>
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ Code Management{% endblock %} {% block content %}
|
|||||||
<option value="">Select Role</option>
|
<option value="">Select Role</option>
|
||||||
<option value="staff">Staff User</option>
|
<option value="staff">Staff User</option>
|
||||||
<option value="payroll">Payroll Specialist</option>
|
<option value="payroll">Payroll Specialist</option>
|
||||||
|
<option value="accounting">Accounting Specialist</option>
|
||||||
<option value="project_manager">Project Manager</option>
|
<option value="project_manager">Project Manager</option>
|
||||||
<option value="admin">Administrator</option>
|
<option value="admin">Administrator</option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
<select id="role" name="role" required>
|
<select id="role" name="role" required>
|
||||||
<option value="staff" {{ 'selected' if user.role == 'staff' else '' }}>Staff User</option>
|
<option value="staff" {{ 'selected' if user.role == 'staff' else '' }}>Staff User</option>
|
||||||
<option value="payroll" {{ 'selected' if user.role == 'payroll' else '' }}>Payroll Specialist</option>
|
<option value="payroll" {{ 'selected' if user.role == 'payroll' else '' }}>Payroll Specialist</option>
|
||||||
|
<option value="accounting" {{ 'selected' if user.role == 'accounting' else '' }}>Accounting Specialist</option>
|
||||||
<option value="project_manager" {{ 'selected' if user.role == 'project_manager' else '' }}>Project Manager</option>
|
<option value="project_manager" {{ 'selected' if user.role == 'project_manager' else '' }}>Project Manager</option>
|
||||||
<option value="admin" {{ 'selected' if user.role == 'admin' else '' }}>Administrator</option>
|
<option value="admin" {{ 'selected' if user.role == 'admin' else '' }}>Administrator</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -587,6 +588,7 @@
|
|||||||
const roleNames = {
|
const roleNames = {
|
||||||
'staff': 'Staff User',
|
'staff': 'Staff User',
|
||||||
'payroll': 'Payroll Specialist',
|
'payroll': 'Payroll Specialist',
|
||||||
|
'accounting': 'Accounting Specialist',
|
||||||
'project_manager': 'Project Manager',
|
'project_manager': 'Project Manager',
|
||||||
'admin': 'Administrator'
|
'admin': 'Administrator'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,6 +65,19 @@ Code Management{% endblock %} {% block extra_head %}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card accounting">
|
||||||
|
<div class="stat-icon accounting">
|
||||||
|
<i class="fas fa-file-invoice-dollar"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>
|
||||||
|
{{ users|selectattr('role', 'equalto',
|
||||||
|
'accounting')|selectattr('active_status', 'equalto', True)|list|length }}
|
||||||
|
</h3>
|
||||||
|
<p>Accounting Specialists</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="stat-card project-manager">
|
<div class="stat-card project-manager">
|
||||||
<div class="stat-icon project-manager">
|
<div class="stat-icon project-manager">
|
||||||
<i class="fas fa-project-diagram"></i>
|
<i class="fas fa-project-diagram"></i>
|
||||||
|
|||||||
Reference in New Issue
Block a user