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>
This commit is contained in:
2025-12-01 09:56:01 +08:00
parent 83ce1f7fc8
commit 642b879dd4
207 changed files with 19487 additions and 3048 deletions

View File

@@ -1,7 +1,7 @@
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
{{ __('Edit Budget') }} - {{ $budget->fiscal_year }}
編輯預算 - {{ $budget->fiscal_year }}
</h2>
</x-slot>
@@ -13,30 +13,30 @@
<!-- Basic Info -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">{{ __('Basic Information') }}</h3>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">基本資訊</h3>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Budget Name') }} *</label>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">預算名稱 *</label>
<input type="text" name="name" id="name" value="{{ old('name', $budget->name) }}" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
@error('name')<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>@enderror
</div>
<div>
<label for="period_start" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Period Start') }} *</label>
<label for="period_start" class="block text-sm font-medium text-gray-700 dark:text-gray-300">期間開始 *</label>
<input type="date" name="period_start" id="period_start" value="{{ old('period_start', $budget->period_start->format('Y-m-d')) }}" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
</div>
<div>
<label for="period_end" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Period End') }} *</label>
<label for="period_end" class="block text-sm font-medium text-gray-700 dark:text-gray-300">期間結束 *</label>
<input type="date" name="period_end" id="period_end" value="{{ old('period_end', $budget->period_end->format('Y-m-d')) }}" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
</div>
<div class="sm:col-span-2">
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Notes') }}</label>
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300">備註</label>
<textarea name="notes" id="notes" rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">{{ old('notes', $budget->notes) }}</textarea>
</div>
</div>
@@ -45,25 +45,25 @@
<!-- Income Items -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('Income') }} (6e)</h3>
<button type="button" @click="addItem('income')" class="btn-secondary text-sm">+ {{ __('Add Income Item') }}</button>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">收入 </h3>
<button type="button" @click="addItem('income')" class="btn-secondary text-sm">+ 新增收入項目</button>
</div>
<div class="space-y-4">
<template x-for="(item, index) in incomeItems" :key="index">
<div class="flex gap-4 items-start bg-gray-50 dark:bg-gray-900 p-4 rounded-md">
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Account') }}</label>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">帳戶</label>
<select :name="'budget_items[income_' + index + '][chart_of_account_id]'" x-model="item.account_id" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<option value="">{{ __('Select account...') }}</option>
<option value="">選擇帳戶...</option>
@foreach($incomeAccounts as $account)
<option value="{{ $account->id }}">{{ $account->account_code }} - {{ $account->account_name_zh }}</option>
@endforeach
</select>
</div>
<div class="w-48">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Amount') }}</label>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">金額</label>
<input type="number" :name="'budget_items[income_' + index + '][budgeted_amount]'" x-model="item.amount" step="0.01" min="0" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
</div>
@@ -75,7 +75,7 @@
</div>
</template>
<div x-show="incomeItems.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
{{ __('No income items. Click "Add Income Item" to get started.') }}
無收入項目。點擊「新增收入項目」開始。
</div>
</div>
</div>
@@ -83,25 +83,25 @@
<!-- Expense Items -->
<div class="bg-white shadow sm:rounded-lg dark:bg-gray-800 px-4 py-5 sm:p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('Expenses') }} (/ú)</h3>
<button type="button" @click="addItem('expense')" class="btn-secondary text-sm">+ {{ __('Add Expense Item') }}</button>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">支出 (/<EFBFBD>)</h3>
<button type="button" @click="addItem('expense')" class="btn-secondary text-sm">+ 新增支出項目</button>
</div>
<div class="space-y-4">
<template x-for="(item, index) in expenseItems" :key="index">
<div class="flex gap-4 items-start bg-gray-50 dark:bg-gray-900 p-4 rounded-md">
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Account') }}</label>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">帳戶</label>
<select :name="'budget_items[expense_' + index + '][chart_of_account_id]'" x-model="item.account_id" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
<option value="">{{ __('Select account...') }}</option>
<option value="">選擇帳戶...</option>
@foreach($expenseAccounts as $account)
<option value="{{ $account->id }}">{{ $account->account_code }} - {{ $account->account_name_zh }}</option>
@endforeach
</select>
</div>
<div class="w-48">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('Amount') }}</label>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">金額</label>
<input type="number" :name="'budget_items[expense_' + index + '][budgeted_amount]'" x-model="item.amount" step="0.01" min="0" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100">
</div>
@@ -113,15 +113,15 @@
</div>
</template>
<div x-show="expenseItems.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
{{ __('No expense items. Click "Add Expense Item" to get started.') }}
無支出項目。點擊「新增支出項目」開始。
</div>
</div>
</div>
<!-- Actions -->
<div class="flex items-center justify-end gap-x-4">
<a href="{{ route('admin.budgets.show', $budget) }}" class="btn-secondary">{{ __('Cancel') }}</a>
<button type="submit" class="btn-primary">{{ __('Save Budget') }}</button>
<a href="{{ route('admin.budgets.show', $budget) }}" class="btn-secondary">取消</a>
<button type="submit" class="btn-primary">儲存預算</button>
</div>
</form>
</div>