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

318 lines
9.7 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\Import\FinancialFlowImportService;
use Illuminate\Support\Facades\Log;
/**
* COMMAND: Import Flusso Finanziario Completo
*
* Importa tutti i dati finanziari da Gescon:
* - Conti bancari
* - Incassi
* - Movimenti bancari
* - Riconciliazione automatica
*/
class ImportFinancialFlowCommand extends Command
{
protected $signature = 'import:financial-flow
{tenant : Tenant ID}
{--anagr-casse= : Path to Anagr_casse.csv}
{--incassi= : Path to incassi.csv}
{--unicredit-wri= : Path to Unicredit WRI file}
{--auto-reconcile : Enable automatic reconciliation}
{--dry-run : Simulate import without saving}';
protected $description = 'Import complete financial flow from Gescon (accounts, payments, bank movements)';
public function handle()
{
$tenantId = $this->argument('tenant');
$this->info("🏦 Starting Financial Flow Import for tenant: {$tenantId}");
$this->newLine();
// Validation
if (!$this->validateInputs()) {
return Command::FAILURE;
}
// Prepare file paths
$filePaths = $this->prepareFilePaths();
// Show import plan
$this->showImportPlan($filePaths);
if (!$this->option('dry-run') && !$this->confirm('Proceed with import?')) {
$this->info('Import cancelled by user.');
return Command::SUCCESS;
}
// Execute import
$service = new FinancialFlowImportService($tenantId);
if ($this->option('dry-run')) {
$this->info('🔍 DRY RUN - No data will be saved');
$this->simulateImport($filePaths);
} else {
$result = $service->importCompleteFinancialFlow($filePaths);
$this->displayResults($result);
}
return Command::SUCCESS;
}
private function validateInputs(): bool
{
$errors = [];
// Check tenant exists
$tenantId = $this->argument('tenant');
if (empty($tenantId)) {
$errors[] = 'Tenant ID is required';
}
// Check at least one file is provided
$hasFiles = $this->option('anagr-casse') ||
$this->option('incassi') ||
$this->option('unicredit-wri');
if (!$hasFiles) {
$errors[] = 'At least one input file must be specified';
}
// Check file existence
foreach (['anagr-casse', 'incassi', 'unicredit-wri'] as $option) {
$file = $this->option($option);
if ($file && !file_exists($file)) {
$errors[] = "File not found: {$file}";
}
}
if (!empty($errors)) {
$this->error('❌ Validation errors:');
foreach ($errors as $error) {
$this->line("{$error}");
}
return false;
}
return true;
}
private function prepareFilePaths(): array
{
$filePaths = [];
if ($file = $this->option('anagr-casse')) {
$filePaths['anagr_casse'] = $file;
}
if ($file = $this->option('incassi')) {
$filePaths['incassi'] = $file;
}
if ($file = $this->option('unicredit-wri')) {
$filePaths['unicredit_wri'] = $file;
}
return $filePaths;
}
private function showImportPlan(array $filePaths): void
{
$this->info('📋 Import Plan:');
$this->newLine();
$steps = [
'anagr_casse' => '1. Import Conti Bancari',
'incassi' => '2. Import Incassi/Pagamenti',
'unicredit_wri' => '3. Import Movimenti Bancari'
];
foreach ($steps as $key => $description) {
if (isset($filePaths[$key])) {
$file = basename($filePaths[$key]);
$size = $this->formatFileSize(filesize($filePaths[$key]));
$this->line("{$description} from {$file} ({$size})");
} else {
$this->line(" ⏭️ {$description} - SKIPPED");
}
}
if ($this->option('auto-reconcile')) {
$this->line(" 🔄 4. Automatic Reconciliation - ENABLED");
} else {
$this->line(" ⏭️ 4. Automatic Reconciliation - SKIPPED");
}
$this->newLine();
}
private function simulateImport(array $filePaths): void
{
foreach ($filePaths as $type => $file) {
$this->info("📁 Analyzing {$type}: " . basename($file));
switch ($type) {
case 'anagr_casse':
$this->simulateContiBancari($file);
break;
case 'incassi':
$this->simulateIncassi($file);
break;
case 'unicredit_wri':
$this->simulateMovimentiBancari($file);
break;
}
$this->newLine();
}
}
private function simulateContiBancari(string $file): void
{
$lines = count(file($file));
$this->line(" • Would import " . ($lines - 1) . " bank accounts");
// Sample first few records
if (($handle = fopen($file, "r")) !== FALSE) {
$headers = fgetcsv($handle);
$count = 0;
while (($row = fgetcsv($handle)) !== FALSE && $count < 3) {
$data = array_combine($headers, $row);
$this->line(" - {$data['Codice']}: {$data['Descrizione']}");
$count++;
}
fclose($handle);
}
}
private function simulateIncassi(string $file): void
{
$lines = count(file($file));
$this->line(" • Would import " . ($lines - 1) . " payments");
// Count by year
if (($handle = fopen($file, "r")) !== FALSE) {
$headers = fgetcsv($handle);
$years = [];
while (($row = fgetcsv($handle)) !== FALSE) {
$data = array_combine($headers, $row);
$year = $data['anno_rif'] ?? 'unknown';
$years[$year] = ($years[$year] ?? 0) + 1;
}
fclose($handle);
foreach ($years as $year => $count) {
$this->line(" - Year {$year}: {$count} payments");
}
}
}
private function simulateMovimentiBancari(string $file): void
{
$content = file_get_contents($file);
$lines = explode("\n", $content);
// Find CSV start
$csvStart = null;
foreach ($lines as $index => $line) {
if (strpos($line, 'Data;Valuta;Descrizione;Euro;Caus.') !== false) {
$csvStart = $index + 1;
break;
}
}
if (!$csvStart) {
$this->error(" ❌ Cannot find CSV data in WRI file");
return;
}
$movements = 0;
$entrate = 0;
$uscite = 0;
for ($i = $csvStart; $i < count($lines); $i++) {
$line = trim($lines[$i]);
if (empty($line) || !preg_match('/^\d{2}\/\d{2}\/\d{4}/', $line)) {
continue;
}
$parts = explode(';', $line);
if (count($parts) >= 4) {
$movements++;
$importo = (float)str_replace(',', '.', $parts[3]);
if ($importo > 0) {
$entrate++;
} else {
$uscite++;
}
}
}
$this->line(" • Would import {$movements} bank movements");
$this->line(" - Entrate: {$entrate}");
$this->line(" - Uscite: {$uscite}");
}
private function displayResults(array $result): void
{
if ($result['success']) {
$this->info('✅ Financial Flow Import Completed Successfully!');
$this->newLine();
$stats = $result['stats'];
$this->info('📊 Import Statistics:');
foreach ($stats as $category => $data) {
if (is_array($data) && isset($data['imported'])) {
$this->line(" {$category}:");
$this->line(" ✅ Imported: {$data['imported']}");
if ($data['errors'] > 0) {
$this->line(" ❌ Errors: {$data['errors']}");
}
}
}
if (isset($stats['riconciliazioni'])) {
$recon = $stats['riconciliazioni'];
$this->newLine();
$this->info('🔄 Reconciliation Results:');
$this->line(" ✅ Automatic: {$recon['automatiche']}");
$this->line(" ✋ Manual: {$recon['manuali']}");
$this->line(" ⏳ Pending: {$recon['sospese']}");
}
} else {
$this->error('❌ Import Failed: ' . $result['message']);
if (isset($result['stats'])) {
$this->newLine();
$this->info('📊 Partial Results:');
foreach ($result['stats'] as $category => $data) {
if (is_array($data) && isset($data['imported'])) {
$this->line(" {$category}: {$data['imported']} imported, {$data['errors']} errors");
}
}
}
}
}
private function formatFileSize(int $bytes): string
{
$units = ['B', 'KB', 'MB', 'GB'];
$unitIndex = 0;
while ($bytes >= 1024 && $unitIndex < count($units) - 1) {
$bytes /= 1024;
$unitIndex++;
}
return round($bytes, 1) . ' ' . $units[$unitIndex];
}
}