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>
347 lines
11 KiB
PHP
347 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Announcement;
|
|
use App\Models\AuditLog;
|
|
use Illuminate\Http\Request;
|
|
|
|
class AnnouncementController extends Controller
|
|
{
|
|
public function __construct()
|
|
{
|
|
$this->middleware('can:view_announcements')->only(['index', 'show']);
|
|
$this->middleware('can:create_announcements')->only(['create', 'store']);
|
|
$this->middleware('can:edit_announcements')->only(['edit', 'update']);
|
|
$this->middleware('can:delete_announcements')->only(['destroy']);
|
|
$this->middleware('can:publish_announcements')->only(['publish', 'archive']);
|
|
}
|
|
|
|
/**
|
|
* Display a listing of announcements
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = Announcement::with(['creator', 'lastUpdatedBy'])
|
|
->orderByDesc('is_pinned')
|
|
->orderByDesc('created_at');
|
|
|
|
// Filter by status
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
// Filter by access level
|
|
if ($request->filled('access_level')) {
|
|
$query->where('access_level', $request->access_level);
|
|
}
|
|
|
|
// Filter by pinned
|
|
if ($request->filled('pinned')) {
|
|
$query->where('is_pinned', $request->pinned === 'yes');
|
|
}
|
|
|
|
// Search
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where(function($q) use ($search) {
|
|
$q->where('title', 'like', "%{$search}%")
|
|
->orWhere('content', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
$announcements = $query->paginate(20);
|
|
|
|
// Statistics
|
|
$stats = [
|
|
'total' => Announcement::count(),
|
|
'draft' => Announcement::draft()->count(),
|
|
'published' => Announcement::published()->count(),
|
|
'archived' => Announcement::archived()->count(),
|
|
'pinned' => Announcement::pinned()->count(),
|
|
];
|
|
|
|
return view('admin.announcements.index', compact('announcements', 'stats'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new announcement
|
|
*/
|
|
public function create()
|
|
{
|
|
return view('admin.announcements.create');
|
|
}
|
|
|
|
/**
|
|
* Store a newly created announcement
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'title' => 'required|string|max:255',
|
|
'content' => 'required|string',
|
|
'access_level' => 'required|in:public,members,board,admin',
|
|
'published_at' => 'nullable|date',
|
|
'expires_at' => 'nullable|date|after:published_at',
|
|
'is_pinned' => 'boolean',
|
|
'display_order' => 'nullable|integer',
|
|
'save_action' => 'required|in:draft,publish',
|
|
]);
|
|
|
|
$announcement = Announcement::create([
|
|
'title' => $validated['title'],
|
|
'content' => $validated['content'],
|
|
'access_level' => $validated['access_level'],
|
|
'status' => $validated['save_action'] === 'publish' ? Announcement::STATUS_PUBLISHED : Announcement::STATUS_DRAFT,
|
|
'published_at' => $validated['save_action'] === 'publish' ? ($validated['published_at'] ?? now()) : null,
|
|
'expires_at' => $validated['expires_at'] ?? null,
|
|
'is_pinned' => $validated['is_pinned'] ?? false,
|
|
'display_order' => $validated['display_order'] ?? 0,
|
|
'created_by_user_id' => auth()->id(),
|
|
'last_updated_by_user_id' => auth()->id(),
|
|
]);
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.created',
|
|
'description' => "建立公告:{$announcement->title} (狀態:{$announcement->getStatusLabel()})",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
$message = $validated['save_action'] === 'publish' ? '公告已成功發布' : '公告已儲存為草稿';
|
|
|
|
return redirect()
|
|
->route('admin.announcements.show', $announcement)
|
|
->with('status', $message);
|
|
}
|
|
|
|
/**
|
|
* Display the specified announcement
|
|
*/
|
|
public function show(Announcement $announcement)
|
|
{
|
|
// Check if user can view this announcement
|
|
if (!$announcement->canBeViewedBy(auth()->user())) {
|
|
abort(403, '您沒有權限查看此公告');
|
|
}
|
|
|
|
$announcement->load(['creator', 'lastUpdatedBy']);
|
|
|
|
// Increment view count if viewing published announcement
|
|
if ($announcement->isPublished()) {
|
|
$announcement->incrementViewCount();
|
|
}
|
|
|
|
return view('admin.announcements.show', compact('announcement'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified announcement
|
|
*/
|
|
public function edit(Announcement $announcement)
|
|
{
|
|
// Check if user can edit this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限編輯此公告');
|
|
}
|
|
|
|
return view('admin.announcements.edit', compact('announcement'));
|
|
}
|
|
|
|
/**
|
|
* Update the specified announcement
|
|
*/
|
|
public function update(Request $request, Announcement $announcement)
|
|
{
|
|
// Check if user can edit this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限編輯此公告');
|
|
}
|
|
|
|
$validated = $request->validate([
|
|
'title' => 'required|string|max:255',
|
|
'content' => 'required|string',
|
|
'access_level' => 'required|in:public,members,board,admin',
|
|
'published_at' => 'nullable|date',
|
|
'expires_at' => 'nullable|date|after:published_at',
|
|
'is_pinned' => 'boolean',
|
|
'display_order' => 'nullable|integer',
|
|
]);
|
|
|
|
$announcement->update([
|
|
'title' => $validated['title'],
|
|
'content' => $validated['content'],
|
|
'access_level' => $validated['access_level'],
|
|
'published_at' => $validated['published_at'],
|
|
'expires_at' => $validated['expires_at'] ?? null,
|
|
'is_pinned' => $validated['is_pinned'] ?? false,
|
|
'display_order' => $validated['display_order'] ?? 0,
|
|
'last_updated_by_user_id' => auth()->id(),
|
|
]);
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.updated',
|
|
'description' => "更新公告:{$announcement->title}",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
return redirect()
|
|
->route('admin.announcements.show', $announcement)
|
|
->with('status', '公告已成功更新');
|
|
}
|
|
|
|
/**
|
|
* Remove the specified announcement (soft delete)
|
|
*/
|
|
public function destroy(Announcement $announcement)
|
|
{
|
|
// Check if user can delete this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限刪除此公告');
|
|
}
|
|
|
|
$title = $announcement->title;
|
|
$announcement->delete();
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.deleted',
|
|
'description' => "刪除公告:{$title}",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
return redirect()
|
|
->route('admin.announcements.index')
|
|
->with('status', '公告已成功刪除');
|
|
}
|
|
|
|
/**
|
|
* Publish a draft announcement
|
|
*/
|
|
public function publish(Announcement $announcement)
|
|
{
|
|
// Check permission
|
|
if (!auth()->user()->can('publish_announcements')) {
|
|
abort(403, '您沒有權限發布公告');
|
|
}
|
|
|
|
// Check if user can edit this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限發布此公告');
|
|
}
|
|
|
|
if ($announcement->isPublished()) {
|
|
return back()->with('error', '此公告已經發布');
|
|
}
|
|
|
|
$announcement->publish(auth()->user());
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.published',
|
|
'description' => "發布公告:{$announcement->title}",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
return back()->with('status', '公告已成功發布');
|
|
}
|
|
|
|
/**
|
|
* Archive an announcement
|
|
*/
|
|
public function archive(Announcement $announcement)
|
|
{
|
|
// Check permission
|
|
if (!auth()->user()->can('publish_announcements')) {
|
|
abort(403, '您沒有權限歸檔公告');
|
|
}
|
|
|
|
// Check if user can edit this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限歸檔此公告');
|
|
}
|
|
|
|
if ($announcement->isArchived()) {
|
|
return back()->with('error', '此公告已經歸檔');
|
|
}
|
|
|
|
$announcement->archive(auth()->user());
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.archived',
|
|
'description' => "歸檔公告:{$announcement->title}",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
return back()->with('status', '公告已成功歸檔');
|
|
}
|
|
|
|
/**
|
|
* Pin an announcement
|
|
*/
|
|
public function pin(Request $request, Announcement $announcement)
|
|
{
|
|
// Check permission
|
|
if (!auth()->user()->can('edit_announcements')) {
|
|
abort(403, '您沒有權限置頂公告');
|
|
}
|
|
|
|
// Check if user can edit this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限置頂此公告');
|
|
}
|
|
|
|
$validated = $request->validate([
|
|
'display_order' => 'nullable|integer',
|
|
]);
|
|
|
|
$announcement->pin($validated['display_order'] ?? 0, auth()->user());
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.pinned',
|
|
'description' => "置頂公告:{$announcement->title}",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
return back()->with('status', '公告已成功置頂');
|
|
}
|
|
|
|
/**
|
|
* Unpin an announcement
|
|
*/
|
|
public function unpin(Announcement $announcement)
|
|
{
|
|
// Check permission
|
|
if (!auth()->user()->can('edit_announcements')) {
|
|
abort(403, '您沒有權限取消置頂公告');
|
|
}
|
|
|
|
// Check if user can edit this announcement
|
|
if (!$announcement->canBeEditedBy(auth()->user())) {
|
|
abort(403, '您沒有權限取消置頂此公告');
|
|
}
|
|
|
|
$announcement->unpin(auth()->user());
|
|
|
|
// Audit log
|
|
AuditLog::create([
|
|
'user_id' => auth()->id(),
|
|
'action' => 'announcement.unpinned',
|
|
'description' => "取消置頂公告:{$announcement->title}",
|
|
'ip_address' => request()->ip(),
|
|
]);
|
|
|
|
return back()->with('status', '公告已取消置頂');
|
|
}
|
|
}
|