netgescon-day0/app/Http/Controllers/Admin/FattureElettronicheRicevuteController.php

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) ?? '';
}
}