Implement dark mode, bug report page, and schema dump
This commit is contained in:
@@ -43,6 +43,7 @@ class FinanceDocument extends Model
|
||||
protected $fillable = [
|
||||
'member_id',
|
||||
'submitted_by_user_id',
|
||||
'submitted_by_id',
|
||||
'title',
|
||||
'amount',
|
||||
'status',
|
||||
@@ -50,10 +51,13 @@ class FinanceDocument extends Model
|
||||
'attachment_path',
|
||||
'submitted_at',
|
||||
'approved_by_cashier_id',
|
||||
'cashier_approved_by_id',
|
||||
'cashier_approved_at',
|
||||
'approved_by_accountant_id',
|
||||
'accountant_approved_by_id',
|
||||
'accountant_approved_at',
|
||||
'approved_by_chair_id',
|
||||
'chair_approved_by_id',
|
||||
'chair_approved_at',
|
||||
'rejected_by_user_id',
|
||||
'rejected_at',
|
||||
@@ -65,6 +69,7 @@ class FinanceDocument extends Model
|
||||
'budget_item_id',
|
||||
'requires_board_meeting',
|
||||
'approved_by_board_meeting_id',
|
||||
'board_meeting_approved_by_id',
|
||||
'board_meeting_approved_at',
|
||||
'payment_order_created_by_accountant_id',
|
||||
'payment_order_created_at',
|
||||
@@ -81,6 +86,7 @@ class FinanceDocument extends Model
|
||||
'actual_payment_amount',
|
||||
'cashier_ledger_entry_id',
|
||||
'accounting_transaction_id',
|
||||
'bank_reconciliation_id',
|
||||
'reconciliation_status',
|
||||
'reconciled_at',
|
||||
];
|
||||
@@ -183,9 +189,17 @@ class FinanceDocument extends Model
|
||||
/**
|
||||
* Check if document can be approved by cashier
|
||||
*/
|
||||
public function canBeApprovedByCashier(): bool
|
||||
public function canBeApprovedByCashier(?User $user = null): bool
|
||||
{
|
||||
return $this->status === self::STATUS_PENDING;
|
||||
if ($this->status !== self::STATUS_PENDING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user && $this->submitted_by_user_id && $this->submitted_by_user_id === $user->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +272,8 @@ class FinanceDocument extends Model
|
||||
*/
|
||||
public function needsBoardMeetingApproval(): bool
|
||||
{
|
||||
return $this->amount_tier === self::AMOUNT_TIER_LARGE;
|
||||
$tier = $this->amount_tier ?? $this->determineAmountTier();
|
||||
return $tier === self::AMOUNT_TIER_LARGE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,18 +281,20 @@ class FinanceDocument extends Model
|
||||
*/
|
||||
public function isApprovalStageComplete(): bool
|
||||
{
|
||||
$tier = $this->amount_tier ?? $this->determineAmountTier();
|
||||
|
||||
// For small amounts: cashier + accountant
|
||||
if ($this->amount_tier === self::AMOUNT_TIER_SMALL) {
|
||||
if ($tier === self::AMOUNT_TIER_SMALL) {
|
||||
return $this->status === self::STATUS_APPROVED_ACCOUNTANT;
|
||||
}
|
||||
|
||||
// For medium amounts: cashier + accountant + chair
|
||||
if ($this->amount_tier === self::AMOUNT_TIER_MEDIUM) {
|
||||
if ($tier === self::AMOUNT_TIER_MEDIUM) {
|
||||
return $this->status === self::STATUS_APPROVED_CHAIR;
|
||||
}
|
||||
|
||||
// For large amounts: cashier + accountant + chair + board meeting
|
||||
if ($this->amount_tier === self::AMOUNT_TIER_LARGE) {
|
||||
if ($tier === self::AMOUNT_TIER_LARGE) {
|
||||
return $this->status === self::STATUS_APPROVED_CHAIR &&
|
||||
$this->board_meeting_approved_at !== null;
|
||||
}
|
||||
@@ -321,9 +338,7 @@ class FinanceDocument extends Model
|
||||
*/
|
||||
public function isPaymentCompleted(): bool
|
||||
{
|
||||
return $this->payment_executed_at !== null &&
|
||||
$this->paymentOrder !== null &&
|
||||
$this->paymentOrder->isExecuted();
|
||||
return $this->payment_executed_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,8 +346,7 @@ class FinanceDocument extends Model
|
||||
*/
|
||||
public function isRecordingComplete(): bool
|
||||
{
|
||||
return $this->cashier_ledger_entry_id !== null &&
|
||||
$this->accounting_transaction_id !== null;
|
||||
return $this->cashier_recorded_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,8 +364,65 @@ class FinanceDocument extends Model
|
||||
*/
|
||||
public function isReconciled(): bool
|
||||
{
|
||||
return $this->reconciliation_status === self::RECONCILIATION_MATCHED ||
|
||||
$this->reconciliation_status === self::RECONCILIATION_RESOLVED;
|
||||
return $this->bank_reconciliation_id !== null;
|
||||
}
|
||||
|
||||
// ============== Attribute aliases for backward compatibility ==============
|
||||
public function getSubmittedByIdAttribute(): ?int
|
||||
{
|
||||
return $this->attributes['submitted_by_id'] ?? $this->attributes['submitted_by_user_id'] ?? null;
|
||||
}
|
||||
|
||||
public function setSubmittedByIdAttribute($value): void
|
||||
{
|
||||
$this->attributes['submitted_by_user_id'] = $value;
|
||||
$this->attributes['submitted_by_id'] = $value;
|
||||
}
|
||||
|
||||
public function setSubmittedByUserIdAttribute($value): void
|
||||
{
|
||||
$this->attributes['submitted_by_user_id'] = $value;
|
||||
$this->attributes['submitted_by_id'] = $value;
|
||||
}
|
||||
|
||||
public function getCashierApprovedByIdAttribute(): ?int
|
||||
{
|
||||
return $this->approved_by_cashier_id ?? $this->attributes['approved_by_cashier_id'] ?? null;
|
||||
}
|
||||
|
||||
public function setCashierApprovedByIdAttribute($value): void
|
||||
{
|
||||
$this->attributes['approved_by_cashier_id'] = $value;
|
||||
}
|
||||
|
||||
public function getAccountantApprovedByIdAttribute(): ?int
|
||||
{
|
||||
return $this->approved_by_accountant_id ?? $this->attributes['approved_by_accountant_id'] ?? null;
|
||||
}
|
||||
|
||||
public function setAccountantApprovedByIdAttribute($value): void
|
||||
{
|
||||
$this->attributes['approved_by_accountant_id'] = $value;
|
||||
}
|
||||
|
||||
public function getChairApprovedByIdAttribute(): ?int
|
||||
{
|
||||
return $this->approved_by_chair_id ?? $this->attributes['approved_by_chair_id'] ?? null;
|
||||
}
|
||||
|
||||
public function setChairApprovedByIdAttribute($value): void
|
||||
{
|
||||
$this->attributes['approved_by_chair_id'] = $value;
|
||||
}
|
||||
|
||||
public function getBoardMeetingApprovedByIdAttribute(): ?int
|
||||
{
|
||||
return $this->approved_by_board_meeting_id ?? $this->attributes['approved_by_board_meeting_id'] ?? null;
|
||||
}
|
||||
|
||||
public function setBoardMeetingApprovedByIdAttribute($value): void
|
||||
{
|
||||
$this->attributes['approved_by_board_meeting_id'] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,10 +431,10 @@ class FinanceDocument extends Model
|
||||
public function getRequestTypeText(): string
|
||||
{
|
||||
return match ($this->request_type) {
|
||||
self::REQUEST_TYPE_EXPENSE_REIMBURSEMENT => '事後報銷',
|
||||
self::REQUEST_TYPE_ADVANCE_PAYMENT => '預支/借款',
|
||||
self::REQUEST_TYPE_EXPENSE_REIMBURSEMENT => '費用報銷',
|
||||
self::REQUEST_TYPE_ADVANCE_PAYMENT => '預支款項',
|
||||
self::REQUEST_TYPE_PURCHASE_REQUEST => '採購申請',
|
||||
self::REQUEST_TYPE_PETTY_CASH => '零用金領取',
|
||||
self::REQUEST_TYPE_PETTY_CASH => '零用金',
|
||||
default => '未知',
|
||||
};
|
||||
}
|
||||
@@ -374,9 +445,9 @@ class FinanceDocument extends Model
|
||||
public function getAmountTierText(): string
|
||||
{
|
||||
return match ($this->amount_tier) {
|
||||
self::AMOUNT_TIER_SMALL => '小額 (< 5,000)',
|
||||
self::AMOUNT_TIER_MEDIUM => '中額 (5,000-50,000)',
|
||||
self::AMOUNT_TIER_LARGE => '大額 (> 50,000)',
|
||||
self::AMOUNT_TIER_SMALL => '小額(< 5000)',
|
||||
self::AMOUNT_TIER_MEDIUM => '中額(5000-50000)',
|
||||
self::AMOUNT_TIER_LARGE => '大額(> 50000)',
|
||||
default => '未知',
|
||||
};
|
||||
}
|
||||
@@ -417,19 +488,22 @@ class FinanceDocument extends Model
|
||||
return 'approval';
|
||||
}
|
||||
|
||||
if (!$this->isPaymentCompleted()) {
|
||||
if ($this->payment_order_created_at === null) {
|
||||
return 'approval';
|
||||
}
|
||||
|
||||
if ($this->cashier_recorded_at === null) {
|
||||
return 'payment';
|
||||
}
|
||||
|
||||
if (!$this->isRecordingComplete()) {
|
||||
if ($this->bank_reconciliation_id !== null) {
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
if (! $this->exists) {
|
||||
return 'recording';
|
||||
}
|
||||
|
||||
if (!$this->isReconciled()) {
|
||||
return 'reconciliation';
|
||||
}
|
||||
|
||||
return 'completed';
|
||||
return 'reconciliation';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user