195 lines
7.1 KiB
PHP
195 lines
7.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Repositories;
|
|
|
|
use App\Models\Invoice;
|
|
use App\Models\Quote;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
use App\Repositories\ClientActivityTimelineRepositoryInterface;
|
|
|
|
class InvoiceRepository extends BaseRepository implements InvoiceRepositoryInterface
|
|
{
|
|
public function __construct(
|
|
Invoice $model,
|
|
protected InvoiceLineRepositoryInterface $invoiceLineRepository,
|
|
protected readonly ClientActivityTimelineRepositoryInterface $timelineRepository
|
|
) {
|
|
parent::__construct($model);
|
|
}
|
|
|
|
public function createFromQuote(int|string $quoteId): Invoice
|
|
{
|
|
return DB::transaction(function () use ($quoteId) {
|
|
// Resolve Quote directly to avoid circular dependency
|
|
$quote = Quote::with(['client', 'lines'])->find($quoteId);
|
|
|
|
if (!$quote) {
|
|
throw new \Exception("Quote not found");
|
|
}
|
|
|
|
// Create Invoice
|
|
$invoiceData = [
|
|
'client_id' => $quote->client_id,
|
|
'group_id' => $quote->group_id,
|
|
'source_quote_id' => $quote->id,
|
|
'status' => 'brouillon', // Start as draft
|
|
'invoice_date' => now(),
|
|
'currency' => $quote->currency,
|
|
'total_ht' => $quote->total_ht,
|
|
'total_tva' => $quote->total_tva,
|
|
'total_ttc' => $quote->total_ttc,
|
|
];
|
|
|
|
$invoice = parent::create($invoiceData);
|
|
|
|
// Copy Lines
|
|
foreach ($quote->lines as $line) {
|
|
$this->invoiceLineRepository->create([
|
|
'invoice_id' => $invoice->id,
|
|
'product_id' => $line->product_id,
|
|
'packaging_id' => $line->packaging_id,
|
|
'packages_qty' => $line->packages_qty,
|
|
'units_qty' => $line->units_qty,
|
|
'description' => $line->description,
|
|
'qty_base' => $line->qty_base,
|
|
'unit_price' => $line->unit_price,
|
|
'unit_price_per_package' => $line->unit_price_per_package,
|
|
'tva_rate_id' => $line->tva_rate_id,
|
|
'discount_pct' => $line->discount_pct,
|
|
'total_ht' => $line->total_ht,
|
|
]);
|
|
}
|
|
|
|
// Record history
|
|
$this->recordHistory($invoice->id, null, 'brouillon', 'Created from Quote ' . $quote->reference);
|
|
|
|
try {
|
|
$this->timelineRepository->logActivity([
|
|
'client_id' => $invoice->client_id,
|
|
'actor_type' => 'user',
|
|
'actor_user_id' => auth()->id(),
|
|
'event_type' => 'invoice_created',
|
|
'entity_type' => 'invoice',
|
|
'entity_id' => $invoice->id,
|
|
'title' => 'Nouvelle facture créée',
|
|
'description' => "Une facture a été créée à partir du devis #{$quote->id}.",
|
|
'created_at' => now(),
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error("Failed to log invoice creation activity: " . $e->getMessage());
|
|
}
|
|
|
|
return $invoice;
|
|
});
|
|
}
|
|
|
|
|
|
|
|
public function all(array $columns = ['*']): \Illuminate\Support\Collection
|
|
{
|
|
return $this->model->with(['client', 'lines.product'])->get($columns);
|
|
}
|
|
|
|
public function create(array $data): Invoice
|
|
{
|
|
return DB::transaction(function () use ($data) {
|
|
try {
|
|
// Create the invoice
|
|
$invoice = parent::create($data);
|
|
|
|
// Create the invoice lines
|
|
if (isset($data['lines']) && is_array($data['lines'])) {
|
|
foreach ($data['lines'] as $lineData) {
|
|
$lineData['invoice_id'] = $invoice->id;
|
|
$this->invoiceLineRepository->create($lineData);
|
|
}
|
|
}
|
|
|
|
// Record initial status history
|
|
$this->recordHistory($invoice->id, null, $invoice->status, 'Invoice created');
|
|
|
|
try {
|
|
$this->timelineRepository->logActivity([
|
|
'client_id' => $invoice->client_id,
|
|
'actor_type' => 'user',
|
|
'actor_user_id' => auth()->id(),
|
|
'event_type' => 'invoice_created',
|
|
'entity_type' => 'invoice',
|
|
'entity_id' => $invoice->id,
|
|
'title' => 'Nouvelle facture créée',
|
|
'description' => "La facture #{$invoice->id} a été créée.",
|
|
'created_at' => now(),
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error("Failed to log manual invoice creation activity: " . $e->getMessage());
|
|
}
|
|
|
|
return $invoice;
|
|
} catch (\Exception $e) {
|
|
Log::error('Error creating invoice with lines: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'data' => $data,
|
|
]);
|
|
throw $e;
|
|
}
|
|
});
|
|
}
|
|
|
|
public function update(int|string $id, array $attributes): bool
|
|
{
|
|
return DB::transaction(function () use ($id, $attributes) {
|
|
try {
|
|
$invoice = $this->find($id);
|
|
if (!$invoice) {
|
|
return false;
|
|
}
|
|
|
|
$oldStatus = $invoice->status;
|
|
|
|
// Update the invoice
|
|
$updated = parent::update($id, $attributes);
|
|
|
|
if ($updated) {
|
|
$newStatus = $attributes['status'] ?? $oldStatus;
|
|
|
|
// If status changed, record history
|
|
if ($oldStatus !== $newStatus) {
|
|
$this->recordHistory((int) $id, $oldStatus, $newStatus, 'Invoice status updated');
|
|
}
|
|
}
|
|
|
|
return $updated;
|
|
} catch (\Exception $e) {
|
|
Log::error('Error updating invoice: ' . $e->getMessage(), [
|
|
'id' => $id,
|
|
'attributes' => $attributes,
|
|
'exception' => $e,
|
|
]);
|
|
throw $e;
|
|
}
|
|
});
|
|
}
|
|
|
|
public function find(int|string $id, array $columns = ['*']): ?Invoice
|
|
{
|
|
return $this->model->with(['client', 'lines.product', 'history.user'])->find($id, $columns);
|
|
}
|
|
|
|
private function recordHistory(int $invoiceId, ?string $oldStatus, string $newStatus, ?string $comment = null): void
|
|
{
|
|
\App\Models\DocumentStatusHistory::create([
|
|
'document_type' => 'invoice',
|
|
'document_id' => $invoiceId,
|
|
'old_status' => $oldStatus,
|
|
'new_status' => $newStatus,
|
|
'changed_by' => auth()->id(), // Assuming authenticated user
|
|
'comment' => $comment,
|
|
'changed_at' => now(),
|
|
]);
|
|
}
|
|
}
|