Files
usher-manage-stack/resources/views/admin/budgets/edit.blade.php
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

155 lines
11 KiB
PHP
Raw Blame History

<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
編輯預算 - {{ $budget->fiscal_year }}
</h2>
</x-slot>
<div class="py-12" x-data="budgetEditor()">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<form method="POST" action="{{ route('admin.budgets.update', $budget) }}" class="space-y-6">
@csrf
@method('PATCH')
<!-- 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">基本資訊</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">預算名稱 *</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">期間開始 *</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">期間結束 *</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">備註</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>
</div>
<!-- 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">收入 </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">帳戶</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="">選擇帳戶...</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">金額</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>
<div class="pt-6">
<button type="button" @click="removeItem('income', index)" class="text-red-600 hover:text-red-800 dark:text-red-400">
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>
</button>
</div>
</div>
</template>
<div x-show="incomeItems.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
無收入項目。點擊「新增收入項目」開始。
</div>
</div>
</div>
<!-- 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">支出 (/<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">帳戶</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="">選擇帳戶...</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">金額</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>
<div class="pt-6">
<button type="button" @click="removeItem('expense', index)" class="text-red-600 hover:text-red-800 dark:text-red-400">
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>
</button>
</div>
</div>
</template>
<div x-show="expenseItems.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
無支出項目。點擊「新增支出項目」開始。
</div>
</div>
</div>
<!-- Actions -->
<div class="flex items-center justify-end gap-x-4">
<a href="{{ route('admin.budgets.show', $budget) }}" class="btn-secondary">取消</a>
<button type="submit" class="btn-primary">儲存預算</button>
</div>
</form>
</div>
</div>
<script>
function budgetEditor() {
return {
incomeItems: @json($budget->budgetItems->filter(fn($item) => $item->chartOfAccount->isIncome())->map(fn($item) => ['account_id' => $item->chart_of_account_id, 'amount' => $item->budgeted_amount])->values()),
expenseItems: @json($budget->budgetItems->filter(fn($item) => $item->chartOfAccount->isExpense())->map(fn($item) => ['account_id' => $item->chart_of_account_id, 'amount' => $item->budgeted_amount])->values()),
addItem(type) {
if (type === 'income') {
this.incomeItems.push({ account_id: '', amount: 0 });
} else {
this.expenseItems.push({ account_id: '', amount: 0 });
}
},
removeItem(type, index) {
if (type === 'income') {
this.incomeItems.splice(index, 1);
} else {
this.expenseItems.splice(index, 1);
}
}
}
}
</script>
</x-app-layout>