CluPilotCloud/app/Jobs/ArchiveInvoice.php

63 lines
1.9 KiB
PHP

<?php
namespace App\Jobs;
use App\Models\Invoice;
use App\Services\Billing\InvoiceArchive;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;
/**
* Put one invoice on the archive, and keep trying if the archive is not there.
*
* Queued rather than done during the request that issued the invoice: a network
* mount can block for as long as its timeout allows, and an invoice must not
* fail to be issued because a NAS in an office is rebooting.
*
* Retries with a backoff and, when it finally gives up, records why on the
* invoice itself. An export that fails silently is the failure mode that
* matters here — nobody looks at an archive to check whether last Tuesday is
* in it, and the sweep needs something to find.
*/
class ArchiveInvoice implements ShouldQueue
{
use Queueable;
public int $tries = 5;
/** Minutes, roughly: a mount that is gone is usually gone for a while. */
public array $backoff = [60, 300, 900, 3600];
public function __construct(public Invoice $invoice) {}
public function handle(InvoiceArchive $archive): void
{
if (! InvoiceArchive::enabled()) {
return;
}
// Already there. A retry after a timeout that actually succeeded must
// not write it twice, and the file name is the same either way.
if ($this->invoice->exported_at !== null) {
return;
}
$archive->store($this->invoice);
$this->invoice->forceFill([
'exported_at' => now(),
'export_error' => null,
])->save();
}
public function failed(?Throwable $e): void
{
// On the invoice, not only in the log. The console shows it there, and
// a log line nobody reads is not a report.
$this->invoice->forceFill([
'export_error' => mb_substr((string) $e?->getMessage(), 0, 250),
])->save();
}
}