209 lines
8.3 KiB
PHP
Executable File
209 lines
8.3 KiB
PHP
Executable File
<?php
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Amministratore;
|
|
use App\Models\Stabile;
|
|
use App\Services\FattureElettroniche\CassettoFiscaleDownloadService;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Str;
|
|
|
|
class FeCassettoImportSoggettoCommand extends Command
|
|
{
|
|
protected $signature = 'fe:cassetto-import-soggetto
|
|
{amministratore_id : ID amministratore proprietario del contenitore separato}
|
|
{codice_fiscale : CF/P.IVA del soggetto da interrogare sul cassetto fiscale}
|
|
{--label= : Denominazione del contenitore separato}
|
|
{--from= : Data inizio YYYY-MM-DD}
|
|
{--to= : Data fine YYYY-MM-DD}
|
|
{--quarterly=1 : 1 per spezzare automaticamente per trimestre}
|
|
{--metadati=0 : 1 per richiedere anche i metadati}
|
|
{--user_id=0 : User ID audit}
|
|
{--force_update=0 : 1 per forzare update/import anche su periodo già eseguito}
|
|
{--create_fornitore_if_missing=1 : 1 per creare i fornitori mancanti da FE}
|
|
{--importa_righe=auto : auto|si|no}';
|
|
|
|
protected $description = 'Scarica e importa FE dal Cassetto Fiscale per un soggetto fiscale in un contenitore separato dedicato.';
|
|
|
|
public function handle(CassettoFiscaleDownloadService $service): int
|
|
{
|
|
$amministratore = Amministratore::query()->find((int) $this->argument('amministratore_id'));
|
|
if (! $amministratore instanceof Amministratore) {
|
|
$this->error('Amministratore non trovato.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$cf = $this->normalizeTaxId((string) $this->argument('codice_fiscale'));
|
|
if ($cf === '') {
|
|
$this->error('Codice fiscale / P.IVA non valido.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$from = $this->parseYmd((string) $this->option('from'));
|
|
$to = $this->parseYmd((string) $this->option('to'));
|
|
if (! $from || ! $to) {
|
|
$this->error('Specificare --from=YYYY-MM-DD e --to=YYYY-MM-DD');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
if ($from > $to) {
|
|
$this->error('Intervallo date non valido.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$label = trim((string) $this->option('label'));
|
|
if ($label === '') {
|
|
$label = 'Archivio FE separato ' . $cf;
|
|
}
|
|
|
|
$stabile = $this->ensureDedicatedStabile($amministratore, $cf, $label);
|
|
|
|
$periods = (string) $this->option('quarterly') === '1'
|
|
? $this->splitQuarterly($from, $to)
|
|
: [[$from, $to]];
|
|
|
|
$rows = [];
|
|
$hasErrors = false;
|
|
|
|
foreach ($periods as [$dal, $al]) {
|
|
$result = $service->downloadAndImport(
|
|
$amministratore,
|
|
$stabile,
|
|
$dal,
|
|
$al,
|
|
(bool) ((int) $this->option('metadati') === 1),
|
|
(int) $this->option('user_id'),
|
|
[
|
|
'force_update' => (int) $this->option('force_update') === 1,
|
|
'create_fornitore_if_missing' => (int) $this->option('create_fornitore_if_missing') === 1,
|
|
'importa_righe' => (string) $this->option('importa_righe'),
|
|
'no_skip' => (int) $this->option('force_update') === 1,
|
|
],
|
|
);
|
|
|
|
$rows[] = [
|
|
'periodo' => $dal->format('Y-m-d') . ' -> ' . $al->format('Y-m-d'),
|
|
'status' => (string) ($result['status'] ?? 'error'),
|
|
'imported' => (string) ((int) ($result['imported'] ?? 0)),
|
|
'duplicates' => (string) ((int) ($result['duplicates'] ?? 0)),
|
|
'errors' => (string) ((int) ($result['errors'] ?? 0)),
|
|
'files' => (string) ((int) ($result['files_total'] ?? 0)),
|
|
'message' => Str::limit((string) ($result['message'] ?? ''), 120, '...'),
|
|
];
|
|
|
|
if (($result['status'] ?? 'error') === 'error') {
|
|
$hasErrors = true;
|
|
}
|
|
}
|
|
|
|
$this->line('Contenitore separato: stabile #' . (int) $stabile->id . ' [' . (string) $stabile->codice_stabile . '] ' . (string) $stabile->denominazione);
|
|
$this->table(['Periodo', 'Status', 'Importate', 'Duplicate', 'Errori', 'File', 'Messaggio'], $rows);
|
|
|
|
return $hasErrors ? self::FAILURE : self::SUCCESS;
|
|
}
|
|
|
|
private function ensureDedicatedStabile(Amministratore $amministratore, string $cf, string $label): Stabile
|
|
{
|
|
$existing = Stabile::query()
|
|
->where('amministratore_id', (int) $amministratore->id)
|
|
->where('codice_fiscale', $cf)
|
|
->orderBy('id')
|
|
->first();
|
|
|
|
if ($existing instanceof Stabile) {
|
|
$dirty = false;
|
|
if (! filled($existing->denominazione)) {
|
|
$existing->denominazione = $label;
|
|
$dirty = true;
|
|
}
|
|
if (! filled($existing->cod_fisc_amministratore) && filled($amministratore->codice_fiscale_studio)) {
|
|
$existing->cod_fisc_amministratore = (string) $amministratore->codice_fiscale_studio;
|
|
$dirty = true;
|
|
}
|
|
if ($dirty) {
|
|
$existing->save();
|
|
}
|
|
|
|
return $existing;
|
|
}
|
|
|
|
return Stabile::query()->create([
|
|
'amministratore_id' => (int) $amministratore->id,
|
|
'codice_stabile' => $this->nextDedicatedCode($amministratore, $cf),
|
|
'denominazione' => $label,
|
|
'codice_fiscale' => $cf,
|
|
'cod_fisc_amministratore' => (string) ($amministratore->codice_fiscale_studio ?? ''),
|
|
'indirizzo' => (string) ($amministratore->indirizzo_studio ?? 'Archivio separato FE'),
|
|
'cap' => (string) ($amministratore->cap_studio ?? '00000'),
|
|
'citta' => (string) ($amministratore->citta_studio ?? 'Roma'),
|
|
'provincia' => (string) ($amministratore->provincia_studio ?? 'RM'),
|
|
'attivo' => true,
|
|
'stato' => 'attivo',
|
|
'note' => 'Contenitore separato creato per import FE soggetto fiscale ' . $cf,
|
|
'amministratore_nome' => trim((string) (($amministratore->nome ?? '') . ' ' . ($amministratore->cognome ?? ''))),
|
|
'amministratore_email' => (string) ($amministratore->email_studio ?? ''),
|
|
'configurazione_avanzata' => ['scope' => 'fe_soggetto_separato'],
|
|
]);
|
|
}
|
|
|
|
private function nextDedicatedCode(Amministratore $amministratore, string $cf): string
|
|
{
|
|
$base = 'FE' . substr(preg_replace('/\D+/', '', $cf) ?: '000000', -6);
|
|
$base = str_pad(substr($base, 0, 8), 8, '0');
|
|
$code = $base;
|
|
$suffix = 1;
|
|
|
|
while (Stabile::query()->where('codice_stabile', $code)->exists()) {
|
|
$suffixText = str_pad((string) $suffix, 2, '0', STR_PAD_LEFT);
|
|
$code = substr($base, 0, 6) . $suffixText;
|
|
$suffix++;
|
|
}
|
|
|
|
return $code;
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array{0:\DateTimeImmutable,1:\DateTimeImmutable}>
|
|
*/
|
|
private function splitQuarterly(\DateTimeImmutable $from, \DateTimeImmutable $to): array
|
|
{
|
|
$periods = [];
|
|
$cursor = $from;
|
|
|
|
while ($cursor <= $to) {
|
|
$quarter = intdiv(((int) $cursor->format('n')) - 1, 3) + 1;
|
|
$quarterEndMonth = $quarter * 3;
|
|
$quarterEnd = new \DateTimeImmutable($cursor->format('Y') . '-' . str_pad((string) $quarterEndMonth, 2, '0', STR_PAD_LEFT) . '-01');
|
|
$quarterEnd = $quarterEnd->modify('last day of this month');
|
|
|
|
if ($quarterEnd > $to) {
|
|
$quarterEnd = $to;
|
|
}
|
|
|
|
$periods[] = [$cursor, $quarterEnd];
|
|
$cursor = $quarterEnd->modify('+1 day');
|
|
}
|
|
|
|
return $periods;
|
|
}
|
|
|
|
private function parseYmd(string $value): ?\DateTimeImmutable
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return null;
|
|
}
|
|
|
|
$date = \DateTimeImmutable::createFromFormat('Y-m-d', $value);
|
|
|
|
return $date instanceof \DateTimeImmutable ? $date : null;
|
|
}
|
|
|
|
private function normalizeTaxId(string $value) : string
|
|
{
|
|
return strtoupper(trim(preg_replace('/\s+/', '', $value) ?? ''));
|
|
}
|
|
}
|