229 lines
8.4 KiB
PHP
Executable File
229 lines
8.4 KiB
PHP
Executable File
<?php
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Documento;
|
|
use App\Models\FatturaElettronica;
|
|
use App\Models\Fornitore;
|
|
use App\Models\User;
|
|
use App\Services\FattureElettroniche\FatturaElettronicaImporter;
|
|
use App\Services\FattureElettroniche\FatturaElettronicaProtocolloService;
|
|
use App\Services\FattureElettroniche\FatturaElettronicaXmlParser;
|
|
use App\Support\StabileContext;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class FattureElettronicheRicevuteController extends Controller
|
|
{
|
|
public function downloadXml(FatturaElettronica $fattura): Response
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
if (! $this->canAccessFattura($user, $fattura)) {
|
|
abort(403);
|
|
}
|
|
|
|
$filename = $fattura->nome_file_xml ?: ('fattura-' . $fattura->id . '.xml');
|
|
$filename = str_replace(["\r", "\n", '"'], '', $filename);
|
|
|
|
if (is_string($fattura->xml_path) && $fattura->xml_path !== '' && Storage::disk('local')->exists($fattura->xml_path)) {
|
|
$fullPath = Storage::disk('local')->path($fattura->xml_path);
|
|
return response()->download($fullPath, $filename, [
|
|
'Content-Type' => 'application/xml; charset=utf-8',
|
|
]);
|
|
}
|
|
|
|
if (is_string($fattura->xml_content) && $fattura->xml_content !== '') {
|
|
return response($fattura->xml_content, 200, [
|
|
'Content-Type' => 'application/xml; charset=utf-8',
|
|
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
|
]);
|
|
}
|
|
|
|
abort(404, 'XML non disponibile');
|
|
}
|
|
|
|
public function downloadPdf(FatturaElettronica $fattura): Response
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
if (! $this->canAccessFattura($user, $fattura)) {
|
|
abort(403);
|
|
}
|
|
|
|
$path = is_string($fattura->allegato_pdf_path) ? $fattura->allegato_pdf_path : '';
|
|
|
|
if ($path === '' || ! Storage::disk('local')->exists($path)) {
|
|
// Best-effort: ripara record e protocollazione.
|
|
try {
|
|
app(FatturaElettronicaProtocolloService::class)->ensureDocumentoProtocollato($fattura, (int) $user->id);
|
|
$fattura->refresh();
|
|
} catch (\Throwable) {
|
|
// ignore
|
|
}
|
|
|
|
// Best-effort: se i file sono stati puliti dallo storage ma abbiamo xml_content, rigeneriamo allegati.
|
|
try {
|
|
$xml = is_string($fattura->xml_content) ? trim($fattura->xml_content) : '';
|
|
if ($xml !== '') {
|
|
$importer = new FatturaElettronicaImporter(new FatturaElettronicaXmlParser());
|
|
$importer->importXml($xml, (int) $fattura->stabile_id, is_string($fattura->nome_file_xml) ? $fattura->nome_file_xml : null, [
|
|
'force_update' => true,
|
|
'auto_repair_duplicate' => true,
|
|
'allow_fallback_stabile' => true,
|
|
'force_stabile_id' => (int) $fattura->stabile_id,
|
|
'user_id' => (int) $user->id,
|
|
]);
|
|
|
|
try {
|
|
app(FatturaElettronicaProtocolloService::class)->ensureDocumentoProtocollato($fattura, (int) $user->id);
|
|
$fattura->refresh();
|
|
} catch (\Throwable) {
|
|
// ignore
|
|
}
|
|
}
|
|
} catch (\Throwable) {
|
|
// ignore
|
|
}
|
|
|
|
$path = is_string($fattura->allegato_pdf_path) ? $fattura->allegato_pdf_path : '';
|
|
|
|
if ($path === '' || ! Storage::disk('local')->exists($path)) {
|
|
$doc = Documento::query()
|
|
->where('documentable_type', FatturaElettronica::class)
|
|
->where('documentable_id', (int) $fattura->id)
|
|
->first();
|
|
|
|
$docPath = $doc ? (string) ($doc->percorso_file ?: ($doc->path_file ?: '')): '';
|
|
if ($docPath !== '' && Storage::disk('local')->exists($docPath) && str_ends_with(strtolower($docPath), '.pdf')) {
|
|
$path = $docPath;
|
|
}
|
|
}
|
|
|
|
if ($path === '' || ! Storage::disk('local')->exists($path)) {
|
|
abort(404, 'PDF non disponibile');
|
|
}
|
|
}
|
|
|
|
$filename = $fattura->allegato_pdf_nome ?: ('fattura-' . $fattura->id . '.pdf');
|
|
$filename = str_replace(["\r", "\n", '"'], '', $filename);
|
|
|
|
$fullPath = Storage::disk('local')->path($path);
|
|
|
|
if (request()->boolean('inline')) {
|
|
return response()->file($fullPath, [
|
|
'Content-Type' => 'application/pdf',
|
|
'Content-Disposition' => 'inline; filename="' . $filename . '"',
|
|
]);
|
|
}
|
|
|
|
return response()->download($fullPath, $filename, [
|
|
'Content-Type' => 'application/pdf',
|
|
]);
|
|
}
|
|
|
|
private function canAccessFattura(User $user, FatturaElettronica $fattura): bool
|
|
{
|
|
$allowedStabili = StabileContext::accessibleStabili($user)->pluck('id')->map(fn($value) => (int) $value)->all();
|
|
if (in_array((int) $fattura->stabile_id, $allowedStabili, true)) {
|
|
return true;
|
|
}
|
|
|
|
if (! $user->hasRole('fornitore')) {
|
|
return false;
|
|
}
|
|
|
|
$supplier = $this->resolveSupplierForUser($user);
|
|
|
|
if (! $supplier instanceof Fornitore) {
|
|
return false;
|
|
}
|
|
|
|
$allowedSupplierIds = $this->resolveAccessibleSupplierIds($supplier);
|
|
if (in_array((int) ($fattura->fornitore_id ?? 0), $allowedSupplierIds, true)) {
|
|
return true;
|
|
}
|
|
|
|
$supplierIds = array_values(array_unique(array_filter([
|
|
$this->normalizeTaxId((string) ($supplier->partita_iva ?? '')),
|
|
$this->normalizeTaxId((string) ($supplier->codice_fiscale ?? '')),
|
|
])));
|
|
|
|
foreach ($allowedSupplierIds as $allowedSupplierId) {
|
|
$linkedSupplier = Fornitore::query()->find($allowedSupplierId);
|
|
if (! $linkedSupplier instanceof Fornitore) {
|
|
continue;
|
|
}
|
|
|
|
$supplierIds = array_values(array_unique(array_filter(array_merge($supplierIds, [
|
|
$this->normalizeTaxId((string) ($linkedSupplier->partita_iva ?? '')),
|
|
$this->normalizeTaxId((string) ($linkedSupplier->codice_fiscale ?? '')),
|
|
]))));
|
|
}
|
|
|
|
if ($supplierIds === []) {
|
|
return false;
|
|
}
|
|
|
|
$invoiceIds = array_values(array_unique(array_filter([
|
|
$this->normalizeTaxId((string) ($fattura->destinatario_piva ?? '')),
|
|
$this->normalizeTaxId((string) ($fattura->destinatario_cf ?? '')),
|
|
$this->normalizeTaxId((string) ($fattura->fornitore_piva ?? '')),
|
|
$this->normalizeTaxId((string) ($fattura->fornitore_cf ?? '')),
|
|
])));
|
|
|
|
foreach ($invoiceIds as $invoiceId) {
|
|
if (in_array($invoiceId, $supplierIds, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return array<int>
|
|
*/
|
|
private function resolveAccessibleSupplierIds(Fornitore $supplier): array
|
|
{
|
|
$configuredIds = collect(data_get($supplier->operational_config ?? [], 'catalog_import.fe_supplier_ids', []))
|
|
->map(fn($value): int => (int) $value)
|
|
->filter(fn(int $value): bool => $value > 0)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
return array_values(array_unique(array_merge([(int) $supplier->id], $configuredIds)));
|
|
}
|
|
|
|
private function resolveSupplierForUser(User $user): ?Fornitore
|
|
{
|
|
$email = mb_strtolower(trim((string) $user->email));
|
|
if ($email === '') {
|
|
return null;
|
|
}
|
|
|
|
return Fornitore::query()
|
|
->whereRaw('LOWER(email) = ?', [$email])
|
|
->orderByDesc('id')
|
|
->first();
|
|
}
|
|
|
|
private function normalizeTaxId(string $value): string
|
|
{
|
|
$normalized = strtoupper(str_replace(' ', '', trim($value)));
|
|
if (str_starts_with($normalized, 'IT') && strlen($normalized) > 11) {
|
|
$normalized = substr($normalized, 2);
|
|
}
|
|
|
|
return preg_replace('/[^A-Z0-9]/', '', $normalized) ?? '';
|
|
}
|
|
}
|