kingkay000 commited on
Commit
7831092
·
verified ·
1 Parent(s): 5e649db

Create index_inactives.php

Browse files
Files changed (1) hide show
  1. easypay/index_inactives.php +711 -0
easypay/index_inactives.php ADDED
@@ -0,0 +1,711 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Student Fee Payment Registration - Main Page
4
+ * Search for students and display outstanding fees
5
+ */
6
+
7
+ require_once 'db_config.php';
8
+
9
+ $studentData = null;
10
+ $outstandingFees = [];
11
+ $studentId = $_GET['student_id'] ?? '';
12
+
13
+ // If student is selected, fetch their data and outstanding fees
14
+ if (!empty($studentId)) {
15
+ try {
16
+ // Fetch student details
17
+ $sql = "SELECT
18
+ sr.id,
19
+ sr.student_code,
20
+ CONCAT(sr.last_name, ' ', sr.first_name, ' ', COALESCE(sr.other_name, '')) AS full_name,
21
+ al.level_name
22
+ FROM tb_student_registrations sr
23
+ LEFT JOIN tb_academic_levels al ON al.id = sr.level_id
24
+ WHERE sr.id = :student_id";
25
+
26
+ $stmt = $pdo->prepare($sql);
27
+ $stmt->execute(['student_id' => $studentId]);
28
+ $studentData = $stmt->fetch();
29
+
30
+ if ($studentData) {
31
+ // Fetch outstanding fees per fee_id
32
+ $sql = "SELECT
33
+ ar.id AS receivable_id,
34
+ ar.fee_id,
35
+ asf.description AS fee_description,
36
+ ar.academic_session,
37
+ ar.term_of_session,
38
+ ar.actual_value AS billed_amount,
39
+ COALESCE(asp.total_paid_for_period, 0) AS total_paid, -- Renamed column for clarity
40
+ (ar.actual_value - COALESCE(asp.total_paid_for_period, 0)) AS outstanding_amount,
41
+ ar.created_on
42
+ FROM
43
+ tb_account_receivables ar
44
+ JOIN
45
+ tb_account_school_fees asf ON asf.id = ar.fee_id
46
+ LEFT JOIN (
47
+ -- Subquery now calculates total payments specific to a session, term, and fee
48
+ SELECT
49
+ fee_id,
50
+ student_id,
51
+ academic_session,
52
+ term_of_session,
53
+ SUM(payment_to_date) AS total_paid_for_period
54
+ FROM
55
+ tb_account_student_payments
56
+ GROUP BY
57
+ fee_id,
58
+ student_id,
59
+ academic_session,
60
+ term_of_session
61
+ ) asp ON asp.fee_id = ar.fee_id
62
+ AND asp.student_id = ar.student_id
63
+ AND asp.academic_session = ar.academic_session
64
+ AND asp.term_of_session = ar.term_of_session
65
+ WHERE
66
+ ar.student_id = :student_id
67
+ AND ar.academic_session > 2023
68
+ -- Only show records where the calculated outstanding amount is greater than zero
69
+ AND (ar.actual_value - COALESCE(asp.total_paid_for_period, 0)) > 0
70
+ ORDER BY
71
+ ar.academic_session ASC, ar.term_of_session ASC, ar.created_on ASC";
72
+
73
+ $stmt = $pdo->prepare($sql);
74
+ $stmt->execute(['student_id' => $studentId]);
75
+ $outstandingFees = $stmt->fetchAll();
76
+ }
77
+ } catch (PDOException $e) {
78
+ $error = "Error fetching student data: " . $e->getMessage();
79
+ }
80
+ }
81
+ ?>
82
+ <!DOCTYPE html>
83
+ <html lang="en">
84
+
85
+ <head>
86
+ <meta charset="UTF-8">
87
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
88
+ <title>Student Fee Payment Registration</title>
89
+ <style>
90
+ * {
91
+ margin: 0;
92
+ padding: 0;
93
+ box-sizing: border-box;
94
+ }
95
+
96
+ body {
97
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
98
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
99
+ min-height: 100vh;
100
+ padding: 20px;
101
+ }
102
+
103
+ .container {
104
+ max-width: 1200px;
105
+ margin: 0 auto;
106
+ background: white;
107
+ border-radius: 12px;
108
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
109
+ padding: 30px;
110
+ }
111
+
112
+ h1 {
113
+ color: #333;
114
+ margin-bottom: 30px;
115
+ text-align: center;
116
+ font-size: 28px;
117
+ }
118
+
119
+ .search-section {
120
+ margin-bottom: 30px;
121
+ padding: 20px;
122
+ background: #f8f9fa;
123
+ border-radius: 8px;
124
+ }
125
+
126
+ .search-box {
127
+ position: relative;
128
+ }
129
+
130
+ .search-box label {
131
+ display: block;
132
+ margin-bottom: 8px;
133
+ font-weight: 600;
134
+ color: #555;
135
+ }
136
+
137
+ .search-box input {
138
+ width: 100%;
139
+ padding: 12px 15px;
140
+ border: 2px solid #ddd;
141
+ border-radius: 6px;
142
+ font-size: 16px;
143
+ transition: border-color 0.3s;
144
+ }
145
+
146
+ .search-box input:focus {
147
+ outline: none;
148
+ border-color: #667eea;
149
+ }
150
+
151
+ .search-results {
152
+ position: absolute;
153
+ top: 100%;
154
+ left: 0;
155
+ right: 0;
156
+ background: white;
157
+ border: 2px solid #667eea;
158
+ border-top: none;
159
+ border-radius: 0 0 6px 6px;
160
+ max-height: 300px;
161
+ overflow-y: auto;
162
+ display: none;
163
+ z-index: 1000;
164
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
165
+ }
166
+
167
+ .search-results.active {
168
+ display: block;
169
+ }
170
+
171
+ .search-result-item {
172
+ padding: 12px 15px;
173
+ cursor: pointer;
174
+ border-bottom: 1px solid #eee;
175
+ transition: background-color 0.2s;
176
+ }
177
+
178
+ .search-result-item:hover {
179
+ background-color: #f0f0f0;
180
+ }
181
+
182
+ .search-result-item:last-child {
183
+ border-bottom: none;
184
+ }
185
+
186
+ .student-code {
187
+ color: #667eea;
188
+ font-weight: 600;
189
+ margin-right: 10px;
190
+ }
191
+
192
+ .student-details {
193
+ margin-bottom: 30px;
194
+ padding: 20px;
195
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
196
+ color: white;
197
+ border-radius: 8px;
198
+ }
199
+
200
+ .student-details h2 {
201
+ margin-bottom: 10px;
202
+ font-size: 24px;
203
+ }
204
+
205
+ .student-details p {
206
+ font-size: 16px;
207
+ margin-bottom: 5px;
208
+ }
209
+
210
+ .fees-section {
211
+ margin-bottom: 30px;
212
+ }
213
+
214
+ .fees-section h3 {
215
+ margin-bottom: 15px;
216
+ color: #333;
217
+ font-size: 20px;
218
+ }
219
+
220
+ table {
221
+ width: 100%;
222
+ border-collapse: collapse;
223
+ margin-bottom: 20px;
224
+ }
225
+
226
+ th,
227
+ td {
228
+ padding: 12px;
229
+ text-align: left;
230
+ border-bottom: 1px solid #ddd;
231
+ }
232
+
233
+ th {
234
+ background-color: #667eea;
235
+ color: white;
236
+ font-weight: 600;
237
+ }
238
+
239
+ tr:hover {
240
+ background-color: #f8f9fa;
241
+ }
242
+
243
+ .amount {
244
+ text-align: right;
245
+ font-family: 'Courier New', monospace;
246
+ }
247
+
248
+ .btn {
249
+ padding: 12px 30px;
250
+ border: none;
251
+ border-radius: 6px;
252
+ font-size: 16px;
253
+ font-weight: 600;
254
+ cursor: pointer;
255
+ transition: all 0.3s;
256
+ }
257
+
258
+ .btn-primary {
259
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
260
+ color: white;
261
+ }
262
+
263
+ .btn-primary:hover {
264
+ transform: translateY(-2px);
265
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
266
+ }
267
+
268
+ .btn-primary:disabled {
269
+ background: #ccc;
270
+ cursor: not-allowed;
271
+ transform: none;
272
+ }
273
+
274
+ .modal {
275
+ display: none;
276
+ position: fixed;
277
+ top: 0;
278
+ left: 0;
279
+ width: 100%;
280
+ height: 100%;
281
+ background: rgba(0, 0, 0, 0.5);
282
+ z-index: 2000;
283
+ align-items: center;
284
+ justify-content: center;
285
+ }
286
+
287
+ .modal.active {
288
+ display: flex;
289
+ }
290
+
291
+ .modal-content {
292
+ background: white;
293
+ padding: 30px;
294
+ border-radius: 12px;
295
+ max-width: 600px;
296
+ width: 90%;
297
+ max-height: 90vh;
298
+ overflow-y: auto;
299
+ }
300
+
301
+ .modal-header {
302
+ margin-bottom: 20px;
303
+ }
304
+
305
+ .modal-header h2 {
306
+ color: #333;
307
+ font-size: 22px;
308
+ }
309
+
310
+ .form-group {
311
+ margin-bottom: 20px;
312
+ }
313
+
314
+ .form-group label {
315
+ display: block;
316
+ margin-bottom: 8px;
317
+ font-weight: 600;
318
+ color: #555;
319
+ }
320
+
321
+ .form-group input,
322
+ .form-group textarea {
323
+ width: 100%;
324
+ padding: 10px 12px;
325
+ border: 2px solid #ddd;
326
+ border-radius: 6px;
327
+ font-size: 14px;
328
+ font-family: inherit;
329
+ }
330
+
331
+ .form-group input:focus,
332
+ .form-group textarea:focus {
333
+ outline: none;
334
+ border-color: #667eea;
335
+ }
336
+
337
+ .form-group input[readonly] {
338
+ background-color: #f0f0f0;
339
+ cursor: not-allowed;
340
+ }
341
+
342
+ .modal-actions {
343
+ display: flex;
344
+ gap: 10px;
345
+ justify-content: flex-end;
346
+ margin-top: 25px;
347
+ }
348
+
349
+ .btn-secondary {
350
+ background: #6c757d;
351
+ color: white;
352
+ }
353
+
354
+ .btn-secondary:hover {
355
+ background: #5a6268;
356
+ }
357
+
358
+ .error {
359
+ color: #dc3545;
360
+ font-size: 14px;
361
+ margin-top: 5px;
362
+ }
363
+
364
+ .success {
365
+ color: #28a745;
366
+ font-size: 14px;
367
+ margin-top: 5px;
368
+ }
369
+
370
+ .alert {
371
+ padding: 15px;
372
+ border-radius: 6px;
373
+ margin-bottom: 20px;
374
+ }
375
+
376
+ .alert-error {
377
+ background-color: #f8d7da;
378
+ color: #721c24;
379
+ border: 1px solid #f5c6cb;
380
+ }
381
+
382
+ .total-row {
383
+ font-weight: bold;
384
+ background-color: #f0f0f0;
385
+ }
386
+
387
+ .loading {
388
+ display: inline-block;
389
+ width: 16px;
390
+ height: 16px;
391
+ border: 3px solid #f3f3f3;
392
+ border-top: 3px solid #667eea;
393
+ border-radius: 50%;
394
+ animation: spin 1s linear infinite;
395
+ margin-left: 10px;
396
+ }
397
+
398
+ @keyframes spin {
399
+ 0% {
400
+ transform: rotate(0deg);
401
+ }
402
+
403
+ 100% {
404
+ transform: rotate(360deg);
405
+ }
406
+ }
407
+ </style>
408
+ </head>
409
+
410
+ <body>
411
+ <div class="container">
412
+ <h1>Student Fee Payment Registration</h1>
413
+
414
+ <?php if (isset($error)): ?>
415
+ <div class="alert alert-error"><?php echo htmlspecialchars($error); ?></div>
416
+ <?php endif; ?>
417
+
418
+ <!-- Student Search Section -->
419
+ <div class="search-section">
420
+ <div class="search-box">
421
+ <label for="studentSearch">Search Student (by name or student code)</label>
422
+ <input type="text" id="studentSearch" placeholder="Type to search..." autocomplete="off">
423
+ <div class="search-results" id="searchResults"></div>
424
+ </div>
425
+ </div>
426
+
427
+ <?php if ($studentData): ?>
428
+ <!-- Student Details -->
429
+ <div class="student-details">
430
+ <h2><?php echo htmlspecialchars($studentData['full_name']); ?></h2>
431
+ <p><strong>Student Code:</strong> <?php echo htmlspecialchars($studentData['student_code']); ?></p>
432
+ <p><strong>Academic Level:</strong> <?php echo htmlspecialchars($studentData['level_name'] ?? 'N/A'); ?></p>
433
+ </div>
434
+
435
+ <?php if (count($outstandingFees) > 0): ?>
436
+ <!-- Outstanding Fees Section -->
437
+ <div class="fees-section">
438
+ <h3>Outstanding Fees</h3>
439
+ <form id="paymentForm">
440
+ <input type="hidden" name="student_id" value="<?php echo htmlspecialchars($studentData['id']); ?>">
441
+ <input type="hidden" name="student_code"
442
+ value="<?php echo htmlspecialchars($studentData['student_code']); ?>">
443
+
444
+ <table>
445
+ <thead>
446
+ <tr>
447
+ <th width="50">Select</th>
448
+ <th>Fee Description</th>
449
+ <th width="100">Session</th>
450
+ <th width="80">Term</th>
451
+ <th width="120" class="amount">Billed</th>
452
+ <th width="120" class="amount">Paid</th>
453
+ <th width="120" class="amount">Outstanding</th>
454
+ </tr>
455
+ </thead>
456
+ <tbody>
457
+ <?php
458
+ $totalOutstanding = 0;
459
+ foreach ($outstandingFees as $fee):
460
+ $totalOutstanding += $fee['outstanding_amount'];
461
+ ?>
462
+ <tr>
463
+ <td>
464
+ <input type="checkbox" class="fee-checkbox" name="selected_fees[]" value="<?php echo htmlspecialchars(json_encode([
465
+ 'receivable_id' => $fee['receivable_id'],
466
+ 'fee_id' => $fee['fee_id'],
467
+ 'academic_session' => $fee['academic_session'],
468
+ 'term_of_session' => $fee['term_of_session'],
469
+ 'outstanding_amount' => $fee['outstanding_amount']
470
+ ])); ?>" checked>
471
+ </td>
472
+ <td><?php echo htmlspecialchars($fee['fee_description']); ?></td>
473
+ <td><?php echo htmlspecialchars($fee['academic_session']); ?></td>
474
+ <td><?php echo htmlspecialchars($fee['term_of_session']); ?></td>
475
+ <td class="amount">₦<?php echo number_format($fee['billed_amount'], 2); ?></td>
476
+ <td class="amount">₦<?php echo number_format($fee['total_paid'], 2); ?></td>
477
+ <td class="amount">₦<?php echo number_format($fee['outstanding_amount'], 2); ?></td>
478
+ </tr>
479
+ <?php endforeach; ?>
480
+ <tr class="total-row">
481
+ <td colspan="6" style="text-align: right;">Total Outstanding:</td>
482
+ <td class="amount">₦<?php echo number_format($totalOutstanding, 2); ?></td>
483
+ </tr>
484
+ </tbody>
485
+ </table>
486
+
487
+ <button type="button" class="btn btn-primary" id="processPaymentBtn">Process Payment</button>
488
+ </form>
489
+ </div>
490
+ <?php else: ?>
491
+ <div class="alert alert-error">No outstanding fees found for this student.</div>
492
+ <?php endif; ?>
493
+ <?php endif; ?>
494
+ </div>
495
+
496
+ <!-- Payment Modal -->
497
+ <div class="modal" id="paymentModal">
498
+ <div class="modal-content">
499
+ <div class="modal-header">
500
+ <h2>Process Payment</h2>
501
+ </div>
502
+
503
+ <form id="paymentDetailsForm" method="POST" action="process_payment.php">
504
+ <input type="hidden" name="student_id" id="modal_student_id">
505
+ <input type="hidden" name="student_code" id="modal_student_code">
506
+ <input type="hidden" name="selected_fees" id="modal_selected_fees">
507
+ <input type="hidden" name="payment_date" id="modal_payment_date">
508
+ <input type="hidden" name="bank_description" id="modal_bank_description">
509
+
510
+ <div class="form-group">
511
+ <label for="teller_number">Teller Number *</label>
512
+ <input type="text" id="teller_number" name="teller_number" required>
513
+ <span class="loading" id="tellerLoading" style="display:none;"></span>
514
+ <div class="error" id="tellerError"></div>
515
+ </div>
516
+
517
+ <div class="form-group">
518
+ <label for="bank_narration">Bank Narration</label>
519
+ <textarea id="bank_narration" name="bank_narration" rows="3" readonly></textarea>
520
+ </div>
521
+
522
+ <div class="form-group">
523
+ <label for="unreconciled_amount">Unreconciled Amount on Teller</label>
524
+ <input type="text" id="unreconciled_amount" readonly>
525
+ </div>
526
+
527
+ <div class="form-group">
528
+ <label for="amount_to_use">Amount to Use for Fees *</label>
529
+ <input type="number" id="amount_to_use" name="amount_to_use" step="0.01" min="0" required>
530
+ <div class="error" id="amountError"></div>
531
+ </div>
532
+
533
+ <div class="modal-actions">
534
+ <button type="button" class="btn btn-secondary" id="cancelBtn">Cancel</button>
535
+ <button type="submit" class="btn btn-primary" id="proceedBtn" disabled>OK PROCEED!</button>
536
+ </div>
537
+ </form>
538
+ </div>
539
+ </div>
540
+
541
+ <script>
542
+ // Student search functionality
543
+ const searchInput = document.getElementById('studentSearch');
544
+ const searchResults = document.getElementById('searchResults');
545
+ let searchTimeout;
546
+
547
+ searchInput.addEventListener('input', function () {
548
+ clearTimeout(searchTimeout);
549
+ const searchTerm = this.value.trim();
550
+
551
+ if (searchTerm.length < 2) {
552
+ searchResults.classList.remove('active');
553
+ searchResults.innerHTML = '';
554
+ return;
555
+ }
556
+
557
+ searchTimeout = setTimeout(() => {
558
+ fetch(`ajax_handlers_inactives.php?action=search_students&search=${encodeURIComponent(searchTerm)}`)
559
+ .then(response => response.json())
560
+ .then(data => {
561
+ if (data.error) {
562
+ searchResults.innerHTML = `<div class="search-result-item">${data.error}</div>`;
563
+ } else if (data.length === 0) {
564
+ searchResults.innerHTML = '<div class="search-result-item">No students found</div>';
565
+ } else {
566
+ searchResults.innerHTML = data.map(student =>
567
+ `<div class="search-result-item" data-id="${student.id}">
568
+ <span class="student-code">${student.student_code}</span>
569
+ <span>${student.full_name}</span>
570
+ </div>`
571
+ ).join('');
572
+
573
+ // Add click handlers
574
+ document.querySelectorAll('.search-result-item').forEach(item => {
575
+ item.addEventListener('click', function () {
576
+ const studentId = this.dataset.id;
577
+ window.location.href = `?student_id=${studentId}`;
578
+ });
579
+ });
580
+ }
581
+ searchResults.classList.add('active');
582
+ })
583
+ .catch(error => {
584
+ console.error('Search error:', error);
585
+ searchResults.innerHTML = '<div class="search-result-item">Error searching students</div>';
586
+ searchResults.classList.add('active');
587
+ });
588
+ }, 300);
589
+ });
590
+
591
+ // Close search results when clicking outside
592
+ document.addEventListener('click', function (e) {
593
+ if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
594
+ searchResults.classList.remove('active');
595
+ }
596
+ });
597
+
598
+ // Payment modal functionality
599
+ const modal = document.getElementById('paymentModal');
600
+ const processPaymentBtn = document.getElementById('processPaymentBtn');
601
+ const cancelBtn = document.getElementById('cancelBtn');
602
+ const tellerInput = document.getElementById('teller_number');
603
+ const tellerLoading = document.getElementById('tellerLoading');
604
+ const tellerError = document.getElementById('tellerError');
605
+ const amountInput = document.getElementById('amount_to_use');
606
+ const amountError = document.getElementById('amountError');
607
+ const proceedBtn = document.getElementById('proceedBtn');
608
+
609
+ let unreconciledAmount = 0;
610
+
611
+ processPaymentBtn?.addEventListener('click', function () {
612
+ const checkedFees = document.querySelectorAll('.fee-checkbox:checked');
613
+
614
+ if (checkedFees.length === 0) {
615
+ alert('Please select at least one fee to process payment.');
616
+ return;
617
+ }
618
+
619
+ // Collect selected fees
620
+ const selectedFees = Array.from(checkedFees).map(cb => JSON.parse(cb.value));
621
+
622
+ // Populate modal
623
+ document.getElementById('modal_student_id').value = document.querySelector('input[name="student_id"]').value;
624
+ document.getElementById('modal_student_code').value = document.querySelector('input[name="student_code"]').value;
625
+ document.getElementById('modal_selected_fees').value = JSON.stringify(selectedFees);
626
+
627
+ // Reset form
628
+ document.getElementById('paymentDetailsForm').reset();
629
+ tellerError.textContent = '';
630
+ amountError.textContent = '';
631
+ proceedBtn.disabled = true;
632
+ unreconciledAmount = 0;
633
+
634
+ modal.classList.add('active');
635
+ });
636
+
637
+ cancelBtn.addEventListener('click', function () {
638
+ modal.classList.remove('active');
639
+ });
640
+
641
+ // Teller lookup on blur
642
+ tellerInput.addEventListener('blur', function () {
643
+ const tellerNumber = this.value.trim();
644
+
645
+ if (!tellerNumber) {
646
+ return;
647
+ }
648
+
649
+ tellerLoading.style.display = 'inline-block';
650
+ tellerError.textContent = '';
651
+
652
+ fetch(`ajax_handlers_inactives.php?action=lookup_teller&teller_number=${encodeURIComponent(tellerNumber)}`)
653
+ .then(response => response.json())
654
+ .then(data => {
655
+ tellerLoading.style.display = 'none';
656
+
657
+ if (data.error) {
658
+ tellerError.textContent = data.error;
659
+ document.getElementById('bank_narration').value = '';
660
+ document.getElementById('unreconciled_amount').value = '';
661
+ unreconciledAmount = 0;
662
+ proceedBtn.disabled = true;
663
+ } else {
664
+ document.getElementById('bank_narration').value = data.teller_name;
665
+ document.getElementById('unreconciled_amount').value = '₦' + parseFloat(data.unreconciled_amount).toFixed(2);
666
+ document.getElementById('modal_payment_date').value = data.payment_date;
667
+ document.getElementById('modal_bank_description').value = data.description;
668
+ unreconciledAmount = parseFloat(data.unreconciled_amount);
669
+
670
+ // Enable proceed button if amount is valid
671
+ validateAmount();
672
+ }
673
+ })
674
+ .catch(error => {
675
+ tellerLoading.style.display = 'none';
676
+ tellerError.textContent = 'Error looking up teller number';
677
+ console.error('Teller lookup error:', error);
678
+ });
679
+ });
680
+
681
+ // Amount validation
682
+ amountInput.addEventListener('input', validateAmount);
683
+
684
+ function validateAmount() {
685
+ const amount = parseFloat(amountInput.value);
686
+
687
+ if (isNaN(amount) || amount <= 0) {
688
+ amountError.textContent = 'Amount must be greater than zero';
689
+ proceedBtn.disabled = true;
690
+ return;
691
+ }
692
+
693
+ if (unreconciledAmount === 0) {
694
+ amountError.textContent = 'Please enter a valid teller number first';
695
+ proceedBtn.disabled = true;
696
+ return;
697
+ }
698
+
699
+ if (amount > unreconciledAmount) {
700
+ amountError.textContent = `Amount cannot exceed unreconciled amount (₦${unreconciledAmount.toFixed(2)})`;
701
+ proceedBtn.disabled = true;
702
+ return;
703
+ }
704
+
705
+ amountError.textContent = '';
706
+ proceedBtn.disabled = false;
707
+ }
708
+ </script>
709
+ </body>
710
+
711
+ </html>