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>
119 lines
4.3 KiB
PHP
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.'));
|
|
}
|
|
}
|