Files
usher-manage-stack/app/Models/CashierLedgerEntry.php
gbanyan 42099759e8 Add phone login support and member import functionality
Features:
- Support login via phone number or email (LoginRequest)
- Add members:import-roster command for Excel roster import
- Merge survey emails with roster data

Code Quality (Phase 1-4):
- Add database locking for balance calculation
- Add self-approval checks for finance workflow
- Create service layer (FinanceDocumentApprovalService, PaymentVerificationService)
- Add HasAccountingEntries and HasApprovalWorkflow traits
- Create FormRequest classes for validation
- Add status-badge component
- Define authorization gates in AuthServiceProvider
- Add accounting config file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 03:08:06 +08:00

138 lines
3.2 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;
}
}
/**
* 取得最新餘額(從最後一筆記錄)
* 注意:調用此方法時應在 DB::transaction() 中進行,以確保鎖定生效
*/
public static function getLatestBalance(?string $bankAccount = null): float
{
$query = self::orderBy('entry_date', 'desc')
->orderBy('id', 'desc')
->lockForUpdate();
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;
}
}