diff --git a/app.py b/app.py index 5522507..a820b81 100644 --- a/app.py +++ b/app.py @@ -5128,8 +5128,10 @@ def edit_attendance(record_id): edit_note = request.form.get('edit_note', '').strip() if not edit_note: flash('Edit reason is required for audit purposes.', 'error') + projects = Project.query.filter_by(active_status=True).order_by(Project.name).all() return render_template('edit_attendance.html', attendance_record=attendance_record, + projects=projects, qr_codes=QRCode.query.filter_by(active_status=True).all()) # Track changes for logging @@ -5138,7 +5140,9 @@ def edit_attendance(record_id): 'employee_id': attendance_record.employee_id, 'check_in_date': attendance_record.check_in_date, 'check_in_time': attendance_record.check_in_time, - 'location_name': attendance_record.location_name + 'location_name': attendance_record.location_name, + 'qr_code_id': attendance_record.qr_code_id, + 'location_event': attendance_record.qr_code.location_event if attendance_record.qr_code else None } # Update attendance record fields @@ -5146,6 +5150,26 @@ def edit_attendance(record_id): new_check_in_date = datetime.strptime(request.form['check_in_date'], '%Y-%m-%d').date() new_check_in_time = datetime.strptime(request.form['check_in_time'], '%H:%M').time() new_location_name = request.form['location_name'].strip() + + # Get the new QR code ID from the form (this determines the location event) + new_qr_code_id = request.form.get('qr_code_id', '').strip() + if not new_qr_code_id: + flash('Location event selection is required.', 'error') + projects = Project.query.filter_by(active_status=True).order_by(Project.name).all() + return render_template('edit_attendance.html', + attendance_record=attendance_record, + projects=projects, + qr_codes=QRCode.query.filter_by(active_status=True).all()) + + # Validate the QR code exists + new_qr_code = QRCode.query.get(int(new_qr_code_id)) + if not new_qr_code: + flash('Selected location event not found.', 'error') + projects = Project.query.filter_by(active_status=True).order_by(Project.name).all() + return render_template('edit_attendance.html', + attendance_record=attendance_record, + projects=projects, + qr_codes=QRCode.query.filter_by(active_status=True).all()) # Track what changed if attendance_record.employee_id != new_employee_id: @@ -5156,12 +5180,18 @@ def edit_attendance(record_id): changes['check_in_time'] = f"{attendance_record.check_in_time} → {new_check_in_time}" if attendance_record.location_name != new_location_name: changes['location_name'] = f"{attendance_record.location_name} → {new_location_name}" + if attendance_record.qr_code_id != int(new_qr_code_id): + old_event = attendance_record.qr_code.location_event if attendance_record.qr_code else 'Unknown' + new_event = new_qr_code.location_event + changes['location_event'] = f"{old_event} → {new_event}" + changes['qr_code_id'] = f"{attendance_record.qr_code_id} → {new_qr_code_id}" # Apply changes attendance_record.employee_id = new_employee_id attendance_record.check_in_date = new_check_in_date attendance_record.check_in_time = new_check_in_time attendance_record.location_name = new_location_name + attendance_record.qr_code_id = int(new_qr_code_id) attendance_record.updated_timestamp = datetime.utcnow() # Store the audit note with timestamp and user info @@ -5216,11 +5246,15 @@ def edit_attendance(record_id): return redirect(url_for('attendance_report')) # GET request - show edit form - # Get available QR codes for location dropdown + # Get available projects for the dropdown + projects = Project.query.filter_by(active_status=True).order_by(Project.name).all() + + # Get available QR codes for location dropdown (for backward compatibility) qr_codes = QRCode.query.filter_by(active_status=True).all() return render_template('edit_attendance.html', attendance_record=attendance_record, + projects=projects, qr_codes=qr_codes) except Exception as e: diff --git a/templates/edit_attendance.html b/templates/edit_attendance.html index 51ca818..55e9a45 100644 --- a/templates/edit_attendance.html +++ b/templates/edit_attendance.html @@ -137,6 +137,11 @@

Current Date: {{ attendance_record.check_in_date.strftime('%Y-%m-%d') }}

Current Time: {{ attendance_record.check_in_time.strftime('%H:%M') }}

Current Location: {{ attendance_record.location_name }}

+

Current Event Type: + + {{ attendance_record.qr_code.location_event if attendance_record.qr_code else 'Unknown' }} + +

@@ -222,9 +227,48 @@
-

Location Information

+

Location & Event Information

+ +
- + + +
+ + +
+ + + +
+ + +
+ + + Select whether this is a Check In or Check Out event +
+ + + + + +
+ + This field is automatically updated based on your location selection above
@@ -268,6 +314,151 @@ const userRole = '{{ session.role }}'; const authorizedRoles = ['admin', 'accounting']; + // Store location data for the dropdowns + let locationData = {}; + + // Get form elements + const projectSelect = document.getElementById('project_id'); + const locationSelect = document.getElementById('location_select'); + const eventTypeSelect = document.getElementById('event_type_select'); + const qrCodeIdHidden = document.getElementById('qr_code_id'); + const locationNameInput = document.getElementById('location_name'); + const locationLoading = document.getElementById('location_loading'); + + // Current record data + const currentQrCodeId = {{ attendance_record.qr_code_id }}; + const currentLocationName = "{{ attendance_record.location_name }}"; + const currentProjectId = {{ attendance_record.qr_code.project_id if attendance_record.qr_code and attendance_record.qr_code.project_id else 'null' }}; + const currentLocationEvent = "{{ attendance_record.qr_code.location_event if attendance_record.qr_code else '' }}"; + + // Project selection handler + projectSelect.addEventListener('change', function() { + const projectId = this.value; + + // Reset dependent fields + locationSelect.innerHTML = ''; + locationSelect.disabled = true; + eventTypeSelect.innerHTML = ''; + eventTypeSelect.disabled = true; + qrCodeIdHidden.value = ''; + locationData = {}; + + if (!projectId) { + locationSelect.innerHTML = ''; + return; + } + + // Load locations for selected project + locationLoading.style.display = 'block'; + + fetch(`/api/get_project_locations?project_id=${projectId}`) + .then(response => response.json()) + .then(data => { + locationLoading.style.display = 'none'; + + if (data.success && data.locations.length > 0) { + locationSelect.disabled = false; + + data.locations.forEach((loc, index) => { + const locationKey = `loc_${index}`; + locationData[locationKey] = { + location: loc.location, + location_address: loc.location_address, + qr_codes: loc.qr_codes + }; + + const option = document.createElement('option'); + option.value = locationKey; + option.textContent = `${loc.location} - ${loc.location_address}`; + + // Pre-select if this matches current location + if (loc.location === currentLocationName && currentProjectId == projectId) { + option.selected = true; + } + + locationSelect.appendChild(option); + }); + + // Trigger change to populate event types if location was pre-selected + if (locationSelect.value) { + locationSelect.dispatchEvent(new Event('change')); + } + } else { + locationSelect.innerHTML = ''; + } + }) + .catch(error => { + locationLoading.style.display = 'none'; + console.error('Error loading locations:', error); + alert('Error loading locations. Please try again.'); + }); + }); + + // Location selection handler + locationSelect.addEventListener('change', function() { + const locationKey = this.value; + + eventTypeSelect.innerHTML = ''; + eventTypeSelect.disabled = false; + qrCodeIdHidden.value = ''; + + if (locationKey && locationData[locationKey]) { + const location = locationData[locationKey]; + const qrCodes = location.qr_codes; + + // Update location name field + locationNameInput.value = location.location; + + // Add available event types + if (qrCodes['Check In']) { + const option = document.createElement('option'); + option.value = qrCodes['Check In']; + option.textContent = 'Check In'; + + // Pre-select if this matches current event + if (currentLocationEvent === 'Check In' && currentQrCodeId == qrCodes['Check In']) { + option.selected = true; + } + + eventTypeSelect.appendChild(option); + } + + if (qrCodes['Check Out']) { + const option = document.createElement('option'); + option.value = qrCodes['Check Out']; + option.textContent = 'Check Out'; + + // Pre-select if this matches current event + if (currentLocationEvent === 'Check Out' && currentQrCodeId == qrCodes['Check Out']) { + option.selected = true; + } + + eventTypeSelect.appendChild(option); + } + + if (eventTypeSelect.options.length === 0) { + eventTypeSelect.innerHTML = ''; + eventTypeSelect.disabled = true; + } else { + // Set hidden field to selected or first option + qrCodeIdHidden.value = eventTypeSelect.value || eventTypeSelect.options[0].value; + } + } else { + eventTypeSelect.innerHTML = ''; + eventTypeSelect.disabled = true; + } + }); + + // Event type selection handler + eventTypeSelect.addEventListener('change', function() { + qrCodeIdHidden.value = this.value; + }); + + // Initialize by triggering project change if project is already selected + if (currentProjectId && projectSelect.value) { + projectSelect.dispatchEvent(new Event('change')); + } + // Disable all form inputs for non-authorized roles if (!authorizedRoles.includes(userRole)) { const formInputs = document.querySelectorAll('input, textarea, select, button[type="submit"]'); @@ -294,12 +485,13 @@ const form = document.querySelector("form"); form.addEventListener("submit", function (e) { const employeeId = document.getElementById("employee_id").value.trim(); - const locationName = document.getElementById("location_name").value.trim(); + const locationName = locationNameInput.value.trim(); const editNote = document.getElementById("edit_note").value.trim(); + const qrCodeId = qrCodeIdHidden.value.trim(); - if (!employeeId || !locationName || !editNote) { + if (!employeeId || !locationName || !editNote || !qrCodeId) { e.preventDefault(); - alert("Please fill in all required fields, including the audit note."); + alert("Please fill in all required fields, including selecting the project, location, and event type."); return; }