Add contract management and invoice settings pages

- Created a new Blade view for managing brand contracts with forms for adding and editing contracts, including client and category selection.
- Implemented success message notifications for contract actions.
- Added a new Blade view for invoice settings, allowing customization of invoice number format, header branding, and default values for new invoices.
- Introduced a new PDF view for generating rate cards with customizable styles based on user profile settings.
- Developed a profile management page for rate cards, enabling users to update their identity, appearance, and visibility settings.
This commit is contained in:
I-Fun
2026-04-01 21:47:34 +07:00
parent d3c35ad730
commit 22f03ccca7
48 changed files with 2415 additions and 296 deletions

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\BrandContract;
use App\Models\Client;
use App\Models\ClientCategory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class BrandContractController extends Controller
{
private function categoryTree(): \Illuminate\Support\Collection
{
return ClientCategory::with('children')
->whereNull('parent_id')
->orderBy('sort_order')
->get();
}
public function index(): View
{
$contracts = BrandContract::with(['client', 'category'])
->orderByDesc('end_date')
->get();
$clients = Client::orderBy('name')->get();
$categoryTree = $this->categoryTree();
return view('contracts.index', compact('contracts', 'clients', 'categoryTree'));
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'client_id' => 'nullable|exists:clients,id',
'category_id' => 'required|exists:client_categories,id',
'start_date' => 'required|date',
'end_date' => 'required|date|after_or_equal:start_date',
'notes' => 'nullable|string',
]);
$validated['is_active'] = true;
BrandContract::create($validated);
return back()->with('success', '📝 Contract added!');
}
public function update(Request $request, BrandContract $contract): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'client_id' => 'nullable|exists:clients,id',
'category_id' => 'required|exists:client_categories,id',
'start_date' => 'required|date',
'end_date' => 'required|date|after_or_equal:start_date',
'notes' => 'nullable|string',
'is_active' => 'boolean',
]);
$validated['is_active'] = $request->boolean('is_active', $contract->is_active);
$contract->update($validated);
return back()->with('success', '📝 Contract updated!');
}
public function destroy(BrandContract $contract): RedirectResponse
{
$contract->delete();
return back()->with('success', '📝 Contract removed.');
}
public function toggle(BrandContract $contract): RedirectResponse
{
$contract->update(['is_active' => ! $contract->is_active]);
return back()->with('success', $contract->is_active ? '✅ Contract activated.' : '⏸️ Contract paused.');
}
/**
* AJAX: check if a category has an active exclusive contract.
* Returns null if clear, or contract details if blocked.
*/
public function check(Request $request): JsonResponse
{
$categoryId = $request->integer('category_id');
$clientId = $request->integer('client_id');
if (! $categoryId) {
return response()->json(null);
}
// Collect the category and its parent chain (so blocking a parent also blocks children)
$category = ClientCategory::find($categoryId);
$checkIds = [$categoryId];
if ($category?->parent_id) {
$checkIds[] = $category->parent_id;
}
$contract = BrandContract::with(['client', 'category'])
->active()
->whereIn('category_id', $checkIds)
->first();
if (! $contract) {
return response()->json(null);
}
// Allow if it's the contracted brand itself
if ($clientId && $contract->client_id && $contract->client_id === $clientId) {
return response()->json(null);
}
return response()->json([
'id' => $contract->id,
'title' => $contract->title,
'client_name' => $contract->client?->name ?? 'Unknown',
'category' => $contract->category?->name ?? 'Unknown',
'end_date' => $contract->end_date->format('d M Y'),
'days_left' => $contract->days_remaining,
]);
}
}