Initial commit
This commit is contained in:
261
app/Http/Controllers/PaymentVerificationController.php
Normal file
261
app/Http/Controllers/PaymentVerificationController.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Mail\PaymentApprovedByCashierMail;
|
||||
use App\Mail\PaymentApprovedByAccountantMail;
|
||||
use App\Mail\PaymentFullyApprovedMail;
|
||||
use App\Mail\PaymentRejectedMail;
|
||||
use App\Models\MembershipPayment;
|
||||
use App\Models\User;
|
||||
use App\Support\AuditLogger;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PaymentVerificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show payment verification dashboard
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$tab = $request->query('tab', 'all');
|
||||
|
||||
// Base query with relationships
|
||||
$query = MembershipPayment::with(['member', 'submittedBy', 'verifiedByCashier', 'verifiedByAccountant', 'verifiedByChair', 'rejectedBy'])
|
||||
->latest();
|
||||
|
||||
// Filter by tab
|
||||
if ($tab === 'cashier' && $user->can('verify_payments_cashier')) {
|
||||
$query->where('status', MembershipPayment::STATUS_PENDING);
|
||||
} elseif ($tab === 'accountant' && $user->can('verify_payments_accountant')) {
|
||||
$query->where('status', MembershipPayment::STATUS_APPROVED_CASHIER);
|
||||
} elseif ($tab === 'chair' && $user->can('verify_payments_chair')) {
|
||||
$query->where('status', MembershipPayment::STATUS_APPROVED_ACCOUNTANT);
|
||||
} elseif ($tab === 'rejected') {
|
||||
$query->where('status', MembershipPayment::STATUS_REJECTED);
|
||||
} elseif ($tab === 'approved') {
|
||||
$query->where('status', MembershipPayment::STATUS_APPROVED_CHAIR);
|
||||
}
|
||||
|
||||
// Filter by search
|
||||
if ($search = $request->query('search')) {
|
||||
$query->whereHas('member', function ($q) use ($search) {
|
||||
$q->where('full_name', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%");
|
||||
})->orWhere('reference', 'like', "%{$search}%");
|
||||
}
|
||||
|
||||
$payments = $query->paginate(20)->withQueryString();
|
||||
|
||||
// Get counts for tabs
|
||||
$counts = [
|
||||
'pending' => MembershipPayment::where('status', MembershipPayment::STATUS_PENDING)->count(),
|
||||
'cashier_approved' => MembershipPayment::where('status', MembershipPayment::STATUS_APPROVED_CASHIER)->count(),
|
||||
'accountant_approved' => MembershipPayment::where('status', MembershipPayment::STATUS_APPROVED_ACCOUNTANT)->count(),
|
||||
'approved' => MembershipPayment::where('status', MembershipPayment::STATUS_APPROVED_CHAIR)->count(),
|
||||
'rejected' => MembershipPayment::where('status', MembershipPayment::STATUS_REJECTED)->count(),
|
||||
];
|
||||
|
||||
return view('admin.payment-verifications.index', compact('payments', 'tab', 'counts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show verification form for a payment
|
||||
*/
|
||||
public function show(MembershipPayment $payment)
|
||||
{
|
||||
$payment->load(['member', 'submittedBy', 'verifiedByCashier', 'verifiedByAccountant', 'verifiedByChair', 'rejectedBy']);
|
||||
|
||||
return view('admin.payment-verifications.show', compact('payment'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve payment (cashier tier)
|
||||
*/
|
||||
public function approveByCashier(Request $request, MembershipPayment $payment)
|
||||
{
|
||||
if (!Auth::user()->can('verify_payments_cashier')) {
|
||||
abort(403, 'You do not have permission to verify payments as cashier.');
|
||||
}
|
||||
|
||||
if (!$payment->canBeApprovedByCashier()) {
|
||||
return back()->with('error', __('This payment cannot be approved at this stage.'));
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'notes' => ['nullable', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$payment->update([
|
||||
'status' => MembershipPayment::STATUS_APPROVED_CASHIER,
|
||||
'verified_by_cashier_id' => Auth::id(),
|
||||
'cashier_verified_at' => now(),
|
||||
'notes' => $validated['notes'] ?? $payment->notes,
|
||||
]);
|
||||
|
||||
AuditLogger::log('payment.approved_by_cashier', $payment, [
|
||||
'member_id' => $payment->member_id,
|
||||
'amount' => $payment->amount,
|
||||
'verified_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
// Send notification to member
|
||||
Mail::to($payment->member->email)->queue(new PaymentApprovedByCashierMail($payment));
|
||||
|
||||
// Send notification to accountants
|
||||
$accountants = User::permission('verify_payments_accountant')->get();
|
||||
foreach ($accountants as $accountant) {
|
||||
Mail::to($accountant->email)->queue(new PaymentApprovedByCashierMail($payment));
|
||||
}
|
||||
|
||||
return redirect()->route('admin.payment-verifications.index')
|
||||
->with('status', __('Payment approved by cashier. Forwarded to accountant for review.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve payment (accountant tier)
|
||||
*/
|
||||
public function approveByAccountant(Request $request, MembershipPayment $payment)
|
||||
{
|
||||
if (!Auth::user()->can('verify_payments_accountant')) {
|
||||
abort(403, 'You do not have permission to verify payments as accountant.');
|
||||
}
|
||||
|
||||
if (!$payment->canBeApprovedByAccountant()) {
|
||||
return back()->with('error', __('This payment cannot be approved at this stage.'));
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'notes' => ['nullable', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$payment->update([
|
||||
'status' => MembershipPayment::STATUS_APPROVED_ACCOUNTANT,
|
||||
'verified_by_accountant_id' => Auth::id(),
|
||||
'accountant_verified_at' => now(),
|
||||
'notes' => $validated['notes'] ?? $payment->notes,
|
||||
]);
|
||||
|
||||
AuditLogger::log('payment.approved_by_accountant', $payment, [
|
||||
'member_id' => $payment->member_id,
|
||||
'amount' => $payment->amount,
|
||||
'verified_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
// Send notification to member
|
||||
Mail::to($payment->member->email)->queue(new PaymentApprovedByAccountantMail($payment));
|
||||
|
||||
// Send notification to chairs
|
||||
$chairs = User::permission('verify_payments_chair')->get();
|
||||
foreach ($chairs as $chair) {
|
||||
Mail::to($chair->email)->queue(new PaymentApprovedByAccountantMail($payment));
|
||||
}
|
||||
|
||||
return redirect()->route('admin.payment-verifications.index')
|
||||
->with('status', __('Payment approved by accountant. Forwarded to chair for final approval.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve payment (chair tier - final approval)
|
||||
*/
|
||||
public function approveByChair(Request $request, MembershipPayment $payment)
|
||||
{
|
||||
if (!Auth::user()->can('verify_payments_chair')) {
|
||||
abort(403, 'You do not have permission to verify payments as chair.');
|
||||
}
|
||||
|
||||
if (!$payment->canBeApprovedByChair()) {
|
||||
return back()->with('error', __('This payment cannot be approved at this stage.'));
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'notes' => ['nullable', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$payment->update([
|
||||
'status' => MembershipPayment::STATUS_APPROVED_CHAIR,
|
||||
'verified_by_chair_id' => Auth::id(),
|
||||
'chair_verified_at' => now(),
|
||||
'notes' => $validated['notes'] ?? $payment->notes,
|
||||
]);
|
||||
|
||||
AuditLogger::log('payment.approved_by_chair', $payment, [
|
||||
'member_id' => $payment->member_id,
|
||||
'amount' => $payment->amount,
|
||||
'verified_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
// Send notification to member and admins
|
||||
Mail::to($payment->member->email)->queue(new PaymentFullyApprovedMail($payment));
|
||||
|
||||
// Notify membership managers
|
||||
$managers = User::permission('activate_memberships')->get();
|
||||
foreach ($managers as $manager) {
|
||||
Mail::to($manager->email)->queue(new PaymentFullyApprovedMail($payment));
|
||||
}
|
||||
|
||||
return redirect()->route('admin.payment-verifications.index')
|
||||
->with('status', __('Payment fully approved! Member can now be activated by membership manager.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject payment
|
||||
*/
|
||||
public function reject(Request $request, MembershipPayment $payment)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Check if user has any verification permission
|
||||
if (!$user->can('verify_payments_cashier')
|
||||
&& !$user->can('verify_payments_accountant')
|
||||
&& !$user->can('verify_payments_chair')) {
|
||||
abort(403, 'You do not have permission to reject payments.');
|
||||
}
|
||||
|
||||
if ($payment->isFullyApproved()) {
|
||||
return back()->with('error', __('Cannot reject a fully approved payment.'));
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'rejection_reason' => ['required', 'string', 'max:1000'],
|
||||
]);
|
||||
|
||||
$payment->update([
|
||||
'status' => MembershipPayment::STATUS_REJECTED,
|
||||
'rejected_by_user_id' => Auth::id(),
|
||||
'rejected_at' => now(),
|
||||
'rejection_reason' => $validated['rejection_reason'],
|
||||
]);
|
||||
|
||||
AuditLogger::log('payment.rejected', $payment, [
|
||||
'member_id' => $payment->member_id,
|
||||
'amount' => $payment->amount,
|
||||
'rejected_by' => Auth::id(),
|
||||
'reason' => $validated['rejection_reason'],
|
||||
]);
|
||||
|
||||
// Send notification to member
|
||||
Mail::to($payment->member->email)->queue(new PaymentRejectedMail($payment));
|
||||
|
||||
return redirect()->route('admin.payment-verifications.index')
|
||||
->with('status', __('Payment rejected. Member has been notified.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download payment receipt
|
||||
*/
|
||||
public function downloadReceipt(MembershipPayment $payment)
|
||||
{
|
||||
if (!$payment->receipt_path || !Storage::exists($payment->receipt_path)) {
|
||||
abort(404, 'Receipt file not found.');
|
||||
}
|
||||
|
||||
$fileName = 'payment_receipt_' . $payment->member->full_name . '_' . $payment->paid_at->format('Ymd') . '.' . pathinfo($payment->receipt_path, PATHINFO_EXTENSION);
|
||||
|
||||
return Storage::download($payment->receipt_path, $fileName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user