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:
@@ -4,10 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\BrandContract;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientCategory;
|
||||
use App\Models\ContentBrief;
|
||||
use App\Models\Event;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\MediaFile;
|
||||
use App\Models\SocialStat;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -21,7 +24,12 @@ final class DashboardService
|
||||
'upcoming_events' => Event::upcoming(24 * 90)->count(), // 3 months
|
||||
'pending_invoices' => Invoice::where('status', 'sent')->count(),
|
||||
'total_revenue' => Invoice::where('status', 'paid')->sum('total'),
|
||||
'content_in_progress' => ContentBrief::whereNotIn('status', ['posted', 'archived'])->count(),
|
||||
'content_in_progress' => ContentBrief::whereNotIn('status', ['posted', 'archived'])->count(),
|
||||
'total_categories' => ClientCategory::whereNull('parent_id')->count(),
|
||||
'total_subcategories' => ClientCategory::whereNotNull('parent_id')->count(),
|
||||
'total_files' => MediaFile::count(),
|
||||
'active_contracts' => BrandContract::where('is_active', true)->where('end_date', '>=', today())->count(),
|
||||
'total_contracts' => BrandContract::count(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Services;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceItem;
|
||||
use App\Models\InvoiceSettings;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@@ -22,7 +23,8 @@ final class InvoiceService
|
||||
]);
|
||||
|
||||
foreach ($items as $item) {
|
||||
$item['total'] = $item['quantity'] * $item['unit_price'];
|
||||
$item['total'] = $item['quantity'] * $item['unit_price'];
|
||||
$item['is_taxable'] = isset($item['is_taxable']) && $item['is_taxable'] ? true : false;
|
||||
$invoice->items()->create($item);
|
||||
}
|
||||
|
||||
@@ -37,7 +39,8 @@ final class InvoiceService
|
||||
$invoice->items()->delete();
|
||||
|
||||
foreach ($items as $item) {
|
||||
$item['total'] = $item['quantity'] * $item['unit_price'];
|
||||
$item['total'] = $item['quantity'] * $item['unit_price'];
|
||||
$item['is_taxable'] = isset($item['is_taxable']) && $item['is_taxable'] ? true : false;
|
||||
$invoice->items()->create($item);
|
||||
}
|
||||
|
||||
@@ -49,8 +52,9 @@ final class InvoiceService
|
||||
public function generatePdf(Invoice $invoice): Response
|
||||
{
|
||||
$invoice->load('client', 'items');
|
||||
$invoiceSettings = InvoiceSettings::instance();
|
||||
|
||||
$pdf = Pdf::loadView('invoices.pdf', compact('invoice'));
|
||||
$pdf = Pdf::loadView('invoices.pdf', compact('invoice', 'invoiceSettings'));
|
||||
$pdf->setPaper('A4', 'portrait');
|
||||
|
||||
return $pdf->download("invoice-{$invoice->invoice_number}.pdf");
|
||||
|
||||
@@ -30,26 +30,33 @@ class WahaNotifier
|
||||
|
||||
private function send(NotificationSetting $setting, string $text): void
|
||||
{
|
||||
$config = $setting->config ?? [];
|
||||
$url = rtrim($config['waha_url'] ?? '', '/');
|
||||
$session = $config['waha_session'] ?? 'default';
|
||||
$phone = $config['phone'] ?? null;
|
||||
$config = $setting->config ?? [];
|
||||
$endpoint = $config['waha_endpoint'] ?? null;
|
||||
$session = $config['waha_session'] ?? 'default';
|
||||
$chatId = $config['chat_id'] ?? null;
|
||||
$apiKey = $config['api_key'] ?? null;
|
||||
|
||||
if (! $url || ! $phone) {
|
||||
throw new \RuntimeException('WAHA waha_url and phone are required.');
|
||||
if (! $endpoint || ! $chatId) {
|
||||
throw new \RuntimeException('WAHA waha_endpoint and chat_id are required.');
|
||||
}
|
||||
|
||||
// Normalise phone: strip non-digits, ensure no leading +
|
||||
$chatId = preg_replace('/[^0-9]/', '', $phone) . '@c.us';
|
||||
$http = Http::timeout(15)->acceptJson()->contentType('application/json');
|
||||
|
||||
$response = Http::timeout(15)->post("{$url}/api/sendText", [
|
||||
'session' => $session,
|
||||
if ($apiKey) {
|
||||
$http = $http->withHeaders(['X-Api-Key' => $apiKey]);
|
||||
}
|
||||
|
||||
$response = $http->post($endpoint, [
|
||||
'chatId' => $chatId,
|
||||
'text' => $text,
|
||||
'session' => $session,
|
||||
]);
|
||||
|
||||
if (! $response->successful()) {
|
||||
throw new \RuntimeException('WAHA API error: ' . $response->body());
|
||||
$keyHint = $apiKey ? '(api_key set, length=' . strlen($apiKey) . ')' : '(no api_key)';
|
||||
throw new \RuntimeException(
|
||||
"WAHA API error {$response->status()} {$keyHint} → " . $response->body()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user