318 lines
9.7 KiB
PHP
Executable File
318 lines
9.7 KiB
PHP
Executable File
<?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];
|
|
}
|
|
}
|