Commit 99e7632a authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(waiver): live target debt check on create page + require waiver doc

- Added GET /api/members/{id}/debts endpoint returning comprehensive
  debt check as JSON (clear, debts array, total)
- Create page now fetches and displays target member debts live after
  selection via AJAX — shows detailed debt table or green "clear" badge
- Form submission blocked if target has outstanding debts, with error
  message and direct link to payment page
- Made waiver request document upload required (HTML validation)
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent d92f1d38
...@@ -134,4 +134,10 @@ class MemberApiController extends Controller ...@@ -134,4 +134,10 @@ class MemberApiController extends Controller
return $this->json(['results' => $results]); return $this->json(['results' => $results]);
} }
public function debts(Request $request, string $id): Response
{
$result = \App\Modules\Waiver\Services\WaiverProcessor::checkDebtsComprehensive((int) $id);
return $this->json($result);
}
} }
\ No newline at end of file
...@@ -27,6 +27,7 @@ return [ ...@@ -27,6 +27,7 @@ return [
['POST', '/api/members/parse-nid', 'Members\Controllers\MemberApiController@parseNid', ['auth'], 'member.create'], ['POST', '/api/members/parse-nid', 'Members\Controllers\MemberApiController@parseNid', ['auth'], 'member.create'],
['GET', '/api/members/search', 'Members\Controllers\MemberApiController@searchGet', ['auth'], 'member.view'], ['GET', '/api/members/search', 'Members\Controllers\MemberApiController@searchGet', ['auth'], 'member.view'],
['POST', '/api/members/search', 'Members\Controllers\MemberApiController@search', ['auth'], 'member.view'], ['POST', '/api/members/search', 'Members\Controllers\MemberApiController@search', ['auth'], 'member.view'],
['GET', '/api/members/{id}/debts', 'Members\Controllers\MemberApiController@debts', ['auth'], 'member.view'],
// Reports // Reports
['GET', '/reports', 'Members\Controllers\ReportController@index', ['auth'], 'member.reports'], ['GET', '/reports', 'Members\Controllers\ReportController@index', ['auth'], 'member.reports'],
['GET', '/reports/children-aging', 'Members\Controllers\ReportController@childrenAgingOut', ['auth'], 'member.reports'], ['GET', '/reports/children-aging', 'Members\Controllers\ReportController@childrenAgingOut', ['auth'], 'member.reports'],
......
...@@ -147,7 +147,7 @@ ...@@ -147,7 +147,7 @@
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group"> <div class="form-group">
<label class="form-label">استمارة طلب التنازل <span style="color:#DC2626;">*</span></label> <label class="form-label">استمارة طلب التنازل <span style="color:#DC2626;">*</span></label>
<input type="file" name="waiver_request_doc" class="form-input" accept=".pdf,.jpg,.jpeg,.png" style="padding:8px;"> <input type="file" name="waiver_request_doc" class="form-input" accept=".pdf,.jpg,.jpeg,.png" style="padding:8px;" required>
<small style="color:#6B7280;">PDF أو صورة (حد أقصى 5 ميجا)</small> <small style="color:#6B7280;">PDF أو صورة (حد أقصى 5 ميجا)</small>
</div> </div>
<div class="form-group"> <div class="form-group">
...@@ -176,6 +176,7 @@ ...@@ -176,6 +176,7 @@
</div> </div>
</div> </div>
<input type="hidden" name="target_member_id" id="target-member-id" value=""> <input type="hidden" name="target_member_id" id="target-member-id" value="">
<div id="target-debt-status" style="display:none;margin-top:12px;"></div>
<p style="margin:10px 0 0;font-size:12px;color:#6B7280;">💡 إذا لم يكن المستفيد مسجلاً، <a href="/members/create" target="_blank" style="color:#0D7377;">أنشئ عضو جديد</a> أولاً ثم ابحث عنه هنا. يمكنك أيضاً تحديد المستفيد لاحقاً من شاشة الطلب.</p> <p style="margin:10px 0 0;font-size:12px;color:#6B7280;">💡 إذا لم يكن المستفيد مسجلاً، <a href="/members/create" target="_blank" style="color:#0D7377;">أنشئ عضو جديد</a> أولاً ثم ابحث عنه هنا. يمكنك أيضاً تحديد المستفيد لاحقاً من شاشة الطلب.</p>
</div> </div>
...@@ -183,7 +184,7 @@ ...@@ -183,7 +184,7 @@
<div class="form-group"><label class="form-label">ملاحظات</label><textarea name="notes" class="form-textarea" rows="3"></textarea></div> <div class="form-group"><label class="form-label">ملاحظات</label><textarea name="notes" class="form-textarea" rows="3"></textarea></div>
</div> </div>
<button type="submit" class="btn btn-primary" onclick="return confirm('سيتم تقديم طلب التنازل. الرسوم ستُرسل لطابور الخزينة باسم المتنازل إليه بعد اعتماد مجلس الأمناء. متأكد؟')">تقديم الطلب</button> <button type="submit" class="btn btn-primary" onclick="return validateWaiverForm()">تقديم الطلب</button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">إلغاء</a> <a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">إلغاء</a>
</form> </form>
<?php else: ?> <?php else: ?>
...@@ -233,6 +234,7 @@ ...@@ -233,6 +234,7 @@
selectedDiv.style.display = 'block'; selectedDiv.style.display = 'block';
resultsDiv.style.display = 'none'; resultsDiv.style.display = 'none';
searchInput.value = ''; searchInput.value = '';
checkTargetDebts(opt.dataset.id);
}); });
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
...@@ -252,6 +254,50 @@ ...@@ -252,6 +254,50 @@
function clearTarget() { function clearTarget() {
document.getElementById('target-member-id').value = ''; document.getElementById('target-member-id').value = '';
document.getElementById('target-selected').style.display = 'none'; document.getElementById('target-selected').style.display = 'none';
document.getElementById('target-debt-status').style.display = 'none';
window._targetHasDebts = false;
}
function checkTargetDebts(memberId) {
var statusDiv = document.getElementById('target-debt-status');
statusDiv.style.display = 'block';
statusDiv.innerHTML = '<div style="padding:10px;color:#6B7280;font-size:13px;">⏳ جاري التحقق من الحالة المالية...</div>';
window._targetHasDebts = false;
fetch('/api/members/' + memberId + '/debts')
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.clear) {
statusDiv.innerHTML = '<div style="padding:10px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;font-size:13px;color:#166534;">✅ لا توجد مديونيات على المتنازل إليه — الوضع المالي سليم</div>';
window._targetHasDebts = false;
} else {
var html = '<div style="padding:12px;background:#FEF2F2;border:2px solid #DC2626;border-radius:8px;">';
html += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;"><span style="font-size:16px;">🚫</span><strong style="color:#DC2626;font-size:13px;">يوجد مديونية على المتنازل إليه — يجب سدادها قبل تقديم الطلب</strong></div>';
html += '<table style="width:100%;font-size:12px;border-collapse:collapse;">';
html += '<thead><tr style="background:#FEE2E2;"><th style="padding:4px 6px;text-align:right;">الشخص</th><th style="padding:4px 6px;text-align:right;">النوع</th><th style="padding:4px 6px;text-align:right;">نوع المديونية</th><th style="padding:4px 6px;text-align:left;">المبلغ</th></tr></thead><tbody>';
(data.debts || []).forEach(function(d) {
html += '<tr style="border-bottom:1px solid #FECACA;"><td style="padding:4px 6px;">' + (d.person_name||'') + '</td><td style="padding:4px 6px;"><span style="background:#FEE2E2;color:#991B1B;padding:1px 4px;border-radius:2px;font-size:10px;">' + (d.person_label||'') + '</span></td><td style="padding:4px 6px;">' + (d.debt_type||'') + '</td><td style="padding:4px 6px;text-align:left;color:#DC2626;font-weight:700;">' + (d.amount||'0') + '</td></tr>';
});
html += '</tbody></table>';
html += '<div style="margin-top:8px;font-size:13px;font-weight:700;color:#DC2626;">الإجمالي: ' + (data.total||'0') + ' ج.م</div>';
html += '<div style="margin-top:8px;"><a href="/payments/process/' + memberId + '" class="btn btn-outline" style="padding:6px 12px;font-size:12px;color:#DC2626;border-color:#DC2626;">الانتقال إلى السداد ←</a></div>';
html += '</div>';
statusDiv.innerHTML = html;
window._targetHasDebts = true;
}
})
.catch(function() {
statusDiv.innerHTML = '<div style="padding:10px;color:#D97706;font-size:12px;">⚠ تعذر التحقق من الحالة المالية</div>';
});
}
function validateWaiverForm() {
var targetId = document.getElementById('target-member-id').value;
if (targetId && window._targetHasDebts) {
alert('لا يمكن تقديم الطلب — يوجد مديونية على المتنازل إليه يجب سدادها أولاً');
return false;
}
return confirm('سيتم تقديم طلب التنازل. الرسوم ستُرسل لطابور الخزينة باسم المتنازل إليه بعد اعتماد مجلس الأمناء. متأكد؟');
} }
</script> </script>
<?php $__template->endSection(); ?> <?php $__template->endSection(); ?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment