Files
Gbanyan 642b879dd4 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>
2025-12-01 09:56:01 +08:00

330 lines
19 KiB
PHP

<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
收入詳情 - {{ $income->income_number }}
</h2>
</x-slot>
<div class="py-12">
<div class="mx-auto max-w-4xl sm:px-6 lg:px-8 space-y-6">
@if (session('status'))
<div class="rounded-md bg-green-50 dark:bg-green-900/30 p-4">
<p class="text-sm font-medium text-green-800 dark:text-green-200">{{ session('status') }}</p>
</div>
@endif
@if (session('error'))
<div class="rounded-md bg-red-50 dark:bg-red-900/30 p-4">
<p class="text-sm font-medium text-red-800 dark:text-red-200">{{ session('error') }}</p>
</div>
@endif
{{-- 狀態卡片 --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-gray-100">
{{ $income->title }}
</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
收入編號:{{ $income->income_number }}
</p>
</div>
<div>
@if ($income->isCancelled())
<span class="inline-flex items-center rounded-full bg-red-100 dark:bg-red-900 px-3 py-1 text-sm font-medium text-red-800 dark:text-red-200">
{{ $income->getStatusText() }}
</span>
@elseif ($income->isConfirmed())
<span class="inline-flex items-center rounded-full bg-green-100 dark:bg-green-900 px-3 py-1 text-sm font-medium text-green-800 dark:text-green-200">
{{ $income->getStatusText() }}
</span>
@else
<span class="inline-flex items-center rounded-full bg-yellow-100 dark:bg-yellow-900 px-3 py-1 text-sm font-medium text-yellow-800 dark:text-yellow-200">
{{ $income->getStatusText() }}
</span>
@endif
</div>
</div>
{{-- 操作按鈕 --}}
<div class="mt-6 flex space-x-3">
@if ($income->canBeConfirmed())
@can('confirm_income')
<form method="POST" action="{{ route('admin.incomes.confirm', $income) }}" class="inline">
@csrf
<button type="submit" onclick="return confirm('確定要確認此收入嗎?將會自動產生出納日記帳和會計分錄。')"
class="inline-flex items-center rounded-md border border-transparent bg-green-600 dark:bg-green-500 px-4 py-2 text-sm font-medium text-white hover:bg-green-700 dark:hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
<svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
確認收入
</button>
</form>
@endcan
@endif
@if ($income->canBeCancelled())
@can('cancel_income')
<form method="POST" action="{{ route('admin.incomes.cancel', $income) }}" class="inline">
@csrf
<button type="submit" onclick="return confirm('確定要取消此收入嗎?')"
class="inline-flex items-center rounded-md border border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 px-4 py-2 text-sm font-medium text-red-700 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2">
<svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
取消收入
</button>
</form>
@endcan
@endif
@if ($income->attachment_path)
<a href="{{ route('admin.incomes.download', $income) }}"
class="inline-flex items-center rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600">
<svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
下載附件
</a>
@endif
</div>
</div>
</div>
{{-- 金額與日期資訊 --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h4 class="text-base font-medium text-gray-900 dark:text-gray-100 mb-4">金額與日期</h4>
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div class="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-4">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">金額</dt>
<dd class="mt-1 text-2xl font-semibold text-gray-900 dark:text-gray-100">
NT$ {{ number_format($income->amount, 2) }}
</dd>
</div>
<div class="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-4">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">收入日期</dt>
<dd class="mt-1 text-lg font-medium text-gray-900 dark:text-gray-100">
{{ $income->income_date->format('Y-m-d') }}
</dd>
</div>
<div class="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-4">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">收入類型</dt>
<dd class="mt-1 text-lg font-medium text-gray-900 dark:text-gray-100">
{{ $income->getIncomeTypeText() }}
</dd>
</div>
</dl>
</div>
</div>
{{-- 會計資訊 --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h4 class="text-base font-medium text-gray-900 dark:text-gray-100 mb-4">會計資訊</h4>
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">會計科目</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
@if ($income->chartOfAccount)
{{ $income->chartOfAccount->account_code }} - {{ $income->chartOfAccount->account_name_zh }}
@else
<span class="text-gray-400">未指定</span>
@endif
</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">付款方式</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->getPaymentMethodText() }}
</dd>
</div>
@if ($income->bank_account)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">銀行帳號</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->bank_account }}
</dd>
</div>
@endif
@if ($income->receipt_number)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">收據編號</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->receipt_number }}
</dd>
</div>
@endif
@if ($income->transaction_reference)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">交易參考號</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->transaction_reference }}
</dd>
</div>
@endif
</dl>
</div>
</div>
{{-- 付款人資訊 --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h4 class="text-base font-medium text-gray-900 dark:text-gray-100 mb-4">付款人資訊</h4>
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
@if ($income->member)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">關聯會員</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
<a href="{{ route('admin.members.show', $income->member) }}" class="text-indigo-600 dark:text-indigo-400 hover:underline">
{{ $income->member->full_name }}
</a>
</dd>
</div>
@endif
@if ($income->payer_name)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">付款人姓名</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->payer_name }}
</dd>
</div>
@endif
@if (!$income->member && !$income->payer_name)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">付款人</dt>
<dd class="mt-1 text-sm text-gray-400">未記錄</dd>
</div>
@endif
</dl>
</div>
</div>
{{-- 說明與備註 --}}
@if ($income->description || $income->notes)
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h4 class="text-base font-medium text-gray-900 dark:text-gray-100 mb-4">說明與備註</h4>
<dl class="space-y-4">
@if ($income->description)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">說明</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 whitespace-pre-wrap">{{ $income->description }}</dd>
</div>
@endif
@if ($income->notes)
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">內部備註</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 whitespace-pre-wrap">{{ $income->notes }}</dd>
</div>
@endif
</dl>
</div>
</div>
@endif
{{-- 處理記錄 --}}
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h4 class="text-base font-medium text-gray-900 dark:text-gray-100 mb-4">處理記錄</h4>
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">出納記錄人</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->recordedByCashier?->name ?? '不適用' }}
</dd>
@if ($income->recorded_at)
<dd class="text-xs text-gray-500 dark:text-gray-400">
{{ $income->recorded_at->format('Y-m-d H:i') }}
</dd>
@endif
</div>
@if ($income->isConfirmed())
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">會計確認人</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
{{ $income->confirmedByAccountant?->name ?? '不適用' }}
</dd>
@if ($income->confirmed_at)
<dd class="text-xs text-gray-500 dark:text-gray-400">
{{ $income->confirmed_at->format('Y-m-d H:i') }}
</dd>
@endif
</div>
@endif
</dl>
</div>
</div>
{{-- 關聯記錄 --}}
@if ($income->isConfirmed() && ($income->cashierLedgerEntry || $income->accountingEntries->count() > 0))
<div class="bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h4 class="text-base font-medium text-gray-900 dark:text-gray-100 mb-4">關聯記錄</h4>
@if ($income->cashierLedgerEntry)
<div class="mb-4">
<h5 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">出納日記帳</h5>
<div class="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-3">
<a href="{{ route('admin.cashier-ledger.show', $income->cashierLedgerEntry) }}" class="text-indigo-600 dark:text-indigo-400 hover:underline">
查看出納日記帳記錄
</a>
</div>
</div>
@endif
@if ($income->accountingEntries->count() > 0)
<div>
<h5 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">會計分錄</h5>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-300">會計科目</th>
<th class="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-300">借方</th>
<th class="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-300">貸方</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
@foreach ($income->accountingEntries as $entry)
<tr>
<td class="px-4 py-2 text-sm text-gray-900 dark:text-gray-100">
{{ $entry->chartOfAccount->account_code }} - {{ $entry->chartOfAccount->account_name_zh }}
</td>
<td class="px-4 py-2 text-sm text-right text-gray-900 dark:text-gray-100">
@if ($entry->entry_type === 'debit')
NT$ {{ number_format($entry->amount, 2) }}
@endif
</td>
<td class="px-4 py-2 text-sm text-right text-gray-900 dark:text-gray-100">
@if ($entry->entry_type === 'credit')
NT$ {{ number_format($entry->amount, 2) }}
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
</div>
</div>
@endif
{{-- 返回按鈕 --}}
<div class="flex justify-start">
<a href="{{ route('admin.incomes.index') }}"
class="inline-flex items-center rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-600">
<svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
返回列表
</a>
</div>
</div>
</div>
</x-app-layout>