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>
This commit is contained in:
154
app/Services/MembershipFeeCalculator.php
Normal file
154
app/Services/MembershipFeeCalculator.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Member;
|
||||
use App\Models\MembershipPayment;
|
||||
|
||||
class MembershipFeeCalculator
|
||||
{
|
||||
protected SettingsService $settings;
|
||||
|
||||
public function __construct(SettingsService $settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 計算會費金額
|
||||
*
|
||||
* @param Member $member 會員
|
||||
* @param string $feeType 會費類型 (entrance_fee | annual_fee)
|
||||
* @return array{base_amount: float, discount_amount: float, final_amount: float, disability_discount: bool, fee_type: string}
|
||||
*/
|
||||
public function calculate(Member $member, string $feeType): array
|
||||
{
|
||||
$baseAmount = $this->getBaseAmount($feeType);
|
||||
$discountRate = $member->hasApprovedDisability()
|
||||
? $this->getDisabilityDiscountRate()
|
||||
: 0;
|
||||
|
||||
$discountAmount = round($baseAmount * $discountRate, 2);
|
||||
$finalAmount = round($baseAmount - $discountAmount, 2);
|
||||
|
||||
return [
|
||||
'fee_type' => $feeType,
|
||||
'base_amount' => $baseAmount,
|
||||
'discount_amount' => $discountAmount,
|
||||
'final_amount' => $finalAmount,
|
||||
'disability_discount' => $discountRate > 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 為會員計算下一次應繳的會費
|
||||
*
|
||||
* @param Member $member
|
||||
* @return array{base_amount: float, discount_amount: float, final_amount: float, disability_discount: bool, fee_type: string}
|
||||
*/
|
||||
public function calculateNextFee(Member $member): array
|
||||
{
|
||||
$feeType = $member->getNextFeeType();
|
||||
return $this->calculate($member, $feeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得基本會費金額
|
||||
*
|
||||
* @param string $feeType
|
||||
* @return float
|
||||
*/
|
||||
public function getBaseAmount(string $feeType): float
|
||||
{
|
||||
return match($feeType) {
|
||||
MembershipPayment::FEE_TYPE_ENTRANCE => $this->getEntranceFee(),
|
||||
MembershipPayment::FEE_TYPE_ANNUAL => $this->getAnnualFee(),
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得入會會費金額
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getEntranceFee(): float
|
||||
{
|
||||
return (float) $this->settings->get('membership_fee.entrance_fee', 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得常年會費金額
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getAnnualFee(): float
|
||||
{
|
||||
return (float) $this->settings->get('membership_fee.annual_fee', 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得身心障礙折扣比例
|
||||
*
|
||||
* @return float 折扣比例 (0-1)
|
||||
*/
|
||||
public function getDisabilityDiscountRate(): float
|
||||
{
|
||||
return (float) $this->settings->get('membership_fee.disability_discount_rate', 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得身心障礙折扣百分比 (用於顯示)
|
||||
*
|
||||
* @return int 百分比 (0-100)
|
||||
*/
|
||||
public function getDisabilityDiscountPercentage(): int
|
||||
{
|
||||
return (int) ($this->getDisabilityDiscountRate() * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證繳費金額是否正確
|
||||
*
|
||||
* @param MembershipPayment $payment
|
||||
* @return bool
|
||||
*/
|
||||
public function validatePaymentAmount(MembershipPayment $payment): bool
|
||||
{
|
||||
$member = $payment->member;
|
||||
$expected = $this->calculate($member, $payment->fee_type);
|
||||
|
||||
// 繳費金額應等於或大於應繳金額
|
||||
return $payment->amount >= $expected['final_amount'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得會費類型的標籤
|
||||
*
|
||||
* @param string $feeType
|
||||
* @return string
|
||||
*/
|
||||
public function getFeeTypeLabel(string $feeType): string
|
||||
{
|
||||
return match($feeType) {
|
||||
MembershipPayment::FEE_TYPE_ENTRANCE => '入會會費',
|
||||
MembershipPayment::FEE_TYPE_ANNUAL => '常年會費',
|
||||
default => $feeType,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得所有會費設定(用於管理界面)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeeSettings(): array
|
||||
{
|
||||
return [
|
||||
'entrance_fee' => $this->getEntranceFee(),
|
||||
'annual_fee' => $this->getAnnualFee(),
|
||||
'disability_discount_rate' => $this->getDisabilityDiscountRate(),
|
||||
'disability_discount_percentage' => $this->getDisabilityDiscountPercentage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user