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>
133 lines
3.1 KiB
PHP
133 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
class CashierLedgerEntry extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $fillable = [
|
|
'finance_document_id',
|
|
'entry_date',
|
|
'entry_type',
|
|
'payment_method',
|
|
'bank_account',
|
|
'amount',
|
|
'balance_before',
|
|
'balance_after',
|
|
'receipt_number',
|
|
'transaction_reference',
|
|
'recorded_by_cashier_id',
|
|
'recorded_at',
|
|
'notes',
|
|
];
|
|
|
|
protected $casts = [
|
|
'entry_date' => 'date',
|
|
'amount' => 'decimal:2',
|
|
'balance_before' => 'decimal:2',
|
|
'balance_after' => 'decimal:2',
|
|
'recorded_at' => 'datetime',
|
|
];
|
|
|
|
/**
|
|
* 類型常數
|
|
*/
|
|
const ENTRY_TYPE_RECEIPT = 'receipt';
|
|
const ENTRY_TYPE_PAYMENT = 'payment';
|
|
|
|
const PAYMENT_METHOD_BANK_TRANSFER = 'bank_transfer';
|
|
const PAYMENT_METHOD_CHECK = 'check';
|
|
const PAYMENT_METHOD_CASH = 'cash';
|
|
|
|
/**
|
|
* 關聯到報銷申請單
|
|
*/
|
|
public function financeDocument(): BelongsTo
|
|
{
|
|
return $this->belongsTo(FinanceDocument::class);
|
|
}
|
|
|
|
/**
|
|
* 記錄的出納人員
|
|
*/
|
|
public function recordedByCashier(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'recorded_by_cashier_id');
|
|
}
|
|
|
|
/**
|
|
* 計算交易後餘額
|
|
*/
|
|
public function calculateBalanceAfter(float $currentBalance): float
|
|
{
|
|
if ($this->entry_type === self::ENTRY_TYPE_RECEIPT) {
|
|
return $currentBalance + $this->amount;
|
|
} else {
|
|
return $currentBalance - $this->amount;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取得最新餘額(從最後一筆記錄)
|
|
*/
|
|
public static function getLatestBalance(string $bankAccount = null): float
|
|
{
|
|
$query = self::orderBy('entry_date', 'desc')
|
|
->orderBy('id', 'desc');
|
|
|
|
if ($bankAccount) {
|
|
$query->where('bank_account', $bankAccount);
|
|
}
|
|
|
|
$latest = $query->first();
|
|
|
|
return $latest ? $latest->balance_after : 0.00;
|
|
}
|
|
|
|
/**
|
|
* 取得類型文字
|
|
*/
|
|
public function getEntryTypeText(): string
|
|
{
|
|
return match ($this->entry_type) {
|
|
self::ENTRY_TYPE_RECEIPT => '收入',
|
|
self::ENTRY_TYPE_PAYMENT => '支出',
|
|
default => '未知',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 取得付款方式文字
|
|
*/
|
|
public function getPaymentMethodText(): string
|
|
{
|
|
return match ($this->payment_method) {
|
|
self::PAYMENT_METHOD_BANK_TRANSFER => '銀行轉帳',
|
|
self::PAYMENT_METHOD_CHECK => '支票',
|
|
self::PAYMENT_METHOD_CASH => '現金',
|
|
default => '未知',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 是否為收入記錄
|
|
*/
|
|
public function isReceipt(): bool
|
|
{
|
|
return $this->entry_type === self::ENTRY_TYPE_RECEIPT;
|
|
}
|
|
|
|
/**
|
|
* 是否為支出記錄
|
|
*/
|
|
public function isPayment(): bool
|
|
{
|
|
return $this->entry_type === self::ENTRY_TYPE_PAYMENT;
|
|
}
|
|
}
|