Added project dropdown in Time Attendance import
This commit is contained in:
@@ -6583,6 +6583,11 @@ def time_attendance_dashboard():
|
|||||||
@log_database_operations('time_attendance_import')
|
@log_database_operations('time_attendance_import')
|
||||||
def import_time_attendance():
|
def import_time_attendance():
|
||||||
"""Enhanced import with duplicate review"""
|
"""Enhanced import with duplicate review"""
|
||||||
|
if request.method == 'GET':
|
||||||
|
# Load active projects for dropdown
|
||||||
|
projects = Project.query.filter_by(active_status=True).order_by(Project.name).all()
|
||||||
|
return render_template('time_attendance_import.html', projects=projects)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
try:
|
try:
|
||||||
# Check if this is coming from invalid review (file is already in session)
|
# Check if this is coming from invalid review (file is already in session)
|
||||||
@@ -6717,12 +6722,16 @@ def import_time_attendance():
|
|||||||
# Proceed with import
|
# Proceed with import
|
||||||
print("🚀 Starting import process...")
|
print("🚀 Starting import process...")
|
||||||
import_source = request.form.get('import_source', f"Manual Import - {filename}")
|
import_source = request.form.get('import_source', f"Manual Import - {filename}")
|
||||||
|
project_id = request.form.get('project_id')
|
||||||
|
project_id = int(project_id) if project_id and project_id != '' else None
|
||||||
|
|
||||||
import_result = import_service.import_from_excel(
|
import_result = import_service.import_from_excel(
|
||||||
temp_path,
|
temp_path,
|
||||||
created_by=session['user_id'],
|
created_by=session['user_id'],
|
||||||
import_source=import_source,
|
import_source=import_source,
|
||||||
skip_duplicates=skip_duplicates,
|
skip_duplicates=skip_duplicates,
|
||||||
force_import_hashes=force_import_hashes
|
force_import_hashes=force_import_hashes,
|
||||||
|
project_id=project_id
|
||||||
)
|
)
|
||||||
|
|
||||||
if import_result['success']:
|
if import_result['success']:
|
||||||
|
|||||||
@@ -41,12 +41,16 @@ class TimeAttendance(base.db.Model):
|
|||||||
import_batch_id = base.db.Column(base.db.String(36), nullable=True, index=True)
|
import_batch_id = base.db.Column(base.db.String(36), nullable=True, index=True)
|
||||||
import_date = base.db.Column(base.db.DateTime, default=datetime.utcnow)
|
import_date = base.db.Column(base.db.DateTime, default=datetime.utcnow)
|
||||||
import_source = base.db.Column(base.db.String(100), nullable=True)
|
import_source = base.db.Column(base.db.String(100), nullable=True)
|
||||||
|
project_id = base.db.Column(base.db.Integer, base.db.ForeignKey('projects.id'), nullable=True, index=True)
|
||||||
|
|
||||||
# Audit fields
|
# Audit fields
|
||||||
created_by = base.db.Column(base.db.Integer, base.db.ForeignKey('users.id'), nullable=True)
|
created_by = base.db.Column(base.db.Integer, base.db.ForeignKey('users.id'), nullable=True)
|
||||||
created_date = base.db.Column(base.db.DateTime, default=datetime.utcnow)
|
created_date = base.db.Column(base.db.DateTime, default=datetime.utcnow)
|
||||||
updated_date = base.db.Column(base.db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
updated_date = base.db.Column(base.db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||||
|
|
||||||
|
# Relationship
|
||||||
|
project = base.db.relationship('Project', backref='time_attendance_records')
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'<TimeAttendance {self.employee_id} - {self.employee_name} at {self.location_name} on {self.attendance_date}>'
|
return f'<TimeAttendance {self.employee_id} - {self.employee_name} at {self.location_name} on {self.attendance_date}>'
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,16 @@
|
|||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/time_attendance.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/time_attendance.css') }}">
|
||||||
<style>
|
<style>
|
||||||
|
.form-select:hover {
|
||||||
|
border-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #8b5cf6;
|
||||||
|
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.validation-preview {
|
.validation-preview {
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
border: 2px solid #e2e8f0;
|
border: 2px solid #e2e8f0;
|
||||||
@@ -410,6 +420,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Project Selection - REQUIRED -->
|
||||||
|
<div class="form-group" style="margin-bottom: 1.5rem;">
|
||||||
|
<h3 style="margin-bottom: 0.75rem;">
|
||||||
|
<i class="fas fa-project-diagram"></i>
|
||||||
|
Select Project <span style="color: #ef4444;">*</span>
|
||||||
|
</h3>
|
||||||
|
<select name="project_id" id="project_id" class="form-select" required style="
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
background-color: #ffffff;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
">
|
||||||
|
<option value="">-- Select Project (Required) --</option>
|
||||||
|
{% for project in projects %}
|
||||||
|
<option value="{{ project.id }}">{{ project.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<p style="
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #64748b;
|
||||||
|
">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
<strong>Required:</strong> Select which project this import data belongs to for filtering and reporting
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Import Source -->
|
<!-- Import Source -->
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -707,12 +747,29 @@ importForm.addEventListener('submit', (e) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate project selection
|
||||||
|
const projectSelect = document.getElementById('project_id');
|
||||||
|
if (!projectSelect.value || projectSelect.value === '') {
|
||||||
|
e.preventDefault();
|
||||||
|
alert('Please select a project. Project selection is required for attendance imports.');
|
||||||
|
projectSelect.focus();
|
||||||
|
projectSelect.style.borderColor = '#ef4444';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Show progress indicator
|
// Show progress indicator
|
||||||
progressIndicator.classList.add('active');
|
progressIndicator.classList.add('active');
|
||||||
submitBtn.disabled = true;
|
submitBtn.disabled = true;
|
||||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset border color when project is selected
|
||||||
|
document.getElementById('project_id').addEventListener('change', function() {
|
||||||
|
if (this.value) {
|
||||||
|
this.style.borderColor = '#e2e8f0';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
importForm.reset();
|
importForm.reset();
|
||||||
fileInfo.style.display = 'none';
|
fileInfo.style.display = 'none';
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ class TimeAttendanceImportService:
|
|||||||
|
|
||||||
def import_from_excel(self, file_path: str, created_by: int = None,
|
def import_from_excel(self, file_path: str, created_by: int = None,
|
||||||
import_source: str = None, skip_duplicates: bool = True,
|
import_source: str = None, skip_duplicates: bool = True,
|
||||||
force_import_hashes: List[str] = None) -> Dict[str, Any]:
|
force_import_hashes: List[str] = None, project_id: int = None) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Import time attendance data from Excel file with enhanced duplicate handling
|
Import time attendance data from Excel file with enhanced duplicate handling
|
||||||
|
|
||||||
@@ -581,7 +581,8 @@ class TimeAttendanceImportService:
|
|||||||
**record_data,
|
**record_data,
|
||||||
import_batch_id=batch_id,
|
import_batch_id=batch_id,
|
||||||
import_source=import_source or f"Excel Import - {file_path}",
|
import_source=import_source or f"Excel Import - {file_path}",
|
||||||
created_by=created_by
|
created_by=created_by,
|
||||||
|
project_id=project_id
|
||||||
)
|
)
|
||||||
|
|
||||||
self.db.session.add(time_attendance_record)
|
self.db.session.add(time_attendance_record)
|
||||||
|
|||||||
Reference in New Issue
Block a user