Files
usher-manage-stack/app/Http/Controllers/PaymentVerificationController.php

272 lines
10 KiB
PHP

<?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,
]);
// Activate member on final approval
if ($payment->member) {
$payment->member->update([
'membership_status' => \App\Models\Member::STATUS_ACTIVE,
'membership_started_at' => now(),
'membership_expires_at' => now()->addYear(),
]);
}
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));
Mail::to($payment->member->email)->queue(new \App\Mail\MembershipActivatedMail($payment->member));
// 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);
}
}