Files
usher-manage-stack/app/Http/Controllers/MemberPaymentController.php
Gbanyan 642b879dd4 Add membership fee system with disability discount and fix document permissions
Features:
- Implement two fee types: entrance fee and annual fee (both NT$1,000)
- Add 50% discount for disability certificate holders
- Add disability certificate upload in member profile
- Integrate disability verification into cashier approval workflow
- Add membership fee settings in system admin

Document permissions:
- Fix hard-coded role logic in Document model
- Use permission-based authorization instead of role checks

Additional features:
- Add announcements, general ledger, and trial balance modules
- Add income management and accounting entries
- Add comprehensive test suite with factories
- Update UI translations to Traditional Chinese

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 09:56:01 +08:00

119 lines
4.3 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Mail\PaymentSubmittedMail;
use App\Models\Member;
use App\Models\MembershipPayment;
use App\Models\User;
use App\Services\MembershipFeeCalculator;
use App\Support\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rule;
class MemberPaymentController extends Controller
{
protected MembershipFeeCalculator $feeCalculator;
public function __construct(MembershipFeeCalculator $feeCalculator)
{
$this->feeCalculator = $feeCalculator;
}
/**
* Show payment submission form
*/
public function create()
{
$member = Auth::user()->member;
if (!$member) {
return redirect()->route('member.dashboard')
->with('error', __('You must have a member account to submit payment.'));
}
// Check if member can submit payment
if (!$member->canSubmitPayment()) {
return redirect()->route('member.dashboard')
->with('error', __('You cannot submit payment at this time. You may already have a pending payment or your membership is already active.'));
}
// Calculate fee details
$feeDetails = $this->feeCalculator->calculateNextFee($member);
return view('member.submit-payment', compact('member', 'feeDetails'));
}
/**
* Store payment submission
*/
public function store(Request $request)
{
$member = Auth::user()->member;
if (!$member || !$member->canSubmitPayment()) {
return redirect()->route('member.dashboard')
->with('error', __('You cannot submit payment at this time.'));
}
// Calculate fee details
$feeDetails = $this->feeCalculator->calculateNextFee($member);
$validated = $request->validate([
'amount' => ['required', 'numeric', 'min:' . $feeDetails['final_amount']],
'paid_at' => ['required', 'date', 'before_or_equal:today'],
'payment_method' => ['required', Rule::in([
MembershipPayment::METHOD_BANK_TRANSFER,
MembershipPayment::METHOD_CONVENIENCE_STORE,
MembershipPayment::METHOD_CASH,
MembershipPayment::METHOD_CREDIT_CARD,
])],
'reference' => ['nullable', 'string', 'max:255'],
'receipt' => ['required', 'file', 'mimes:jpg,jpeg,png,pdf', 'max:10240'], // 10MB max
'notes' => ['nullable', 'string', 'max:500'],
]);
// Store receipt file
$receiptFile = $request->file('receipt');
$receiptPath = $receiptFile->store('payment-receipts', 'private');
// Create payment record with fee details
$payment = MembershipPayment::create([
'member_id' => $member->id,
'fee_type' => $feeDetails['fee_type'],
'amount' => $validated['amount'],
'base_amount' => $feeDetails['base_amount'],
'discount_amount' => $feeDetails['discount_amount'],
'final_amount' => $feeDetails['final_amount'],
'disability_discount' => $feeDetails['disability_discount'],
'paid_at' => $validated['paid_at'],
'payment_method' => $validated['payment_method'],
'reference' => $validated['reference'] ?? null,
'receipt_path' => $receiptPath,
'notes' => $validated['notes'] ?? null,
'submitted_by_user_id' => Auth::id(),
'status' => MembershipPayment::STATUS_PENDING,
]);
AuditLogger::log('payment.submitted', $payment, [
'member_id' => $member->id,
'amount' => $payment->amount,
'payment_method' => $payment->payment_method,
]);
// Send notification to member (confirmation)
Mail::to($member->email)->queue(new PaymentSubmittedMail($payment, 'member'));
// Send notification to cashiers (action needed)
$cashiers = User::permission('verify_payments_cashier')->get();
foreach ($cashiers as $cashier) {
Mail::to($cashier->email)->queue(new PaymentSubmittedMail($payment, 'cashier'));
}
return redirect()->route('member.dashboard')
->with('status', __('Payment submitted successfully! We will review your payment and notify you once verified.'));
}
}