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>
155 lines
4.1 KiB
PHP
155 lines
4.1 KiB
PHP
<?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(),
|
|
];
|
|
}
|
|
}
|