netgescon-day0/app/Console/Commands/GesconFixFattureFornitoriLinkCommand.php

146 lines
4.5 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\FatturaElettronica;
use App\Models\Fornitore;
use App\Models\Stabile;
use App\Modules\Contabilita\Models\FatturaFornitore;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class GesconFixFattureFornitoriLinkCommand extends Command
{
protected $signature = 'gescon:fatture-fix-fornitore-link
{stabile_id : ID dello stabile}
{--dry-run : Non scrive, mostra solo cosa aggiornerebbe}';
protected $description = 'Backfill: collega le fatture fornitori contabili al fornitore corretto usando PIVA/CF della Fattura Elettronica.';
public function handle(): int
{
$stabileId = (int) $this->argument('stabile_id');
if ($stabileId <= 0) {
$this->error('stabile_id non valido');
return self::FAILURE;
}
$dry = (bool) $this->option('dry-run');
$stabile = Stabile::query()->select(['id', 'amministratore_id'])->find($stabileId);
if (! $stabile) {
$this->error('Stabile non trovato');
return self::FAILURE;
}
$adminId = (int) ($stabile->amministratore_id ?: 0);
if ($adminId <= 0) {
$this->error('Stabile senza amministratore_id');
return self::FAILURE;
}
$fatture = FatturaFornitore::query()
->where('stabile_id', $stabileId)
->whereNull('fornitore_id')
->orderBy('id')
->get(['id', 'fattura_elettronica_id', 'fornitore_id']);
$this->info("Fatture contabili senza fornitore_id: " . $fatture->count());
$updated = 0;
$skipped = 0;
foreach ($fatture as $fattura) {
$feId = (int) ($fattura->fattura_elettronica_id ?: 0);
if ($feId <= 0) {
$skipped++;
continue;
}
$fe = FatturaElettronica::query()->find($feId);
if (! $fe) {
$skipped++;
continue;
}
$piva = $this->normalizeId($fe->fornitore_piva);
$cf = $this->normalizeId($fe->fornitore_cf);
$fornitoreId = $this->matchFornitoreIdForAdmin($adminId, $piva, $cf);
if (! $fornitoreId) {
$skipped++;
continue;
}
$updated++;
if ($dry) {
$this->line("[dry-run] fattura_id={$fattura->id} -> fornitore_id={$fornitoreId}");
continue;
}
DB::transaction(function () use ($fattura, $fe, $fornitoreId): void {
$fattura->fornitore_id = $fornitoreId;
$fattura->save();
if ((int) ($fe->fornitore_id ?: 0) <= 0) {
$fe->fornitore_id = $fornitoreId;
$fe->save();
}
});
}
$this->info('✅ Completato' . ($dry ? ' (dry-run)' : ''));
$this->line(json_encode([
'stabile_id' => $stabileId,
'amministratore_id' => $adminId,
'updated' => $updated,
'skipped' => $skipped,
], JSON_UNESCAPED_UNICODE));
return self::SUCCESS;
}
private function normalizeId(?string $value): string
{
$value = strtoupper(trim((string) ($value ?? '')));
$value = str_replace(' ', '', $value);
return $value;
}
private function matchFornitoreIdForAdmin(int $adminId, string $pivaNorm, string $cfNorm): ?int
{
$base = Fornitore::query()->where('amministratore_id', $adminId);
if ($pivaNorm !== '' && $pivaNorm !== 'ND') {
$match = (clone $base)
->whereRaw("REPLACE(UPPER(partita_iva), ' ', '') = ?", [$pivaNorm])
->value('id');
if ($match) {
return (int) $match;
}
if (str_starts_with($pivaNorm, 'IT') && strlen($pivaNorm) > 2) {
$pivaNoIt = substr($pivaNorm, 2);
$match = (clone $base)
->whereRaw("REPLACE(UPPER(partita_iva), ' ', '') = ?", [$pivaNoIt])
->value('id');
if ($match) {
return (int) $match;
}
}
}
if ($cfNorm !== '' && $cfNorm !== 'ND') {
$match = (clone $base)
->whereRaw("REPLACE(UPPER(codice_fiscale), ' ', '') = ?", [$cfNorm])
->value('id');
if ($match) {
return (int) $match;
}
}
return null;
}
}