diff --git a/.gitignore b/.gitignore
index 10d75a6f27..85e42d1b49 100755
--- a/.gitignore
+++ b/.gitignore
@@ -101,4 +101,5 @@ settings.json
codeception.yml
!.gitkeep
.vscode
-.php-cs-fixer.cache
\ No newline at end of file
+.php-cs-fixer.cache
+.phpunit.*
\ No newline at end of file
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 5533414b6a..5efed0280a 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -5,7 +5,6 @@
->exclude('.couscous')
->exclude('node_modules')
->exclude('vendor')
- ->exclude('tests')
->ignoreDotFiles(true)
->ignoreVCS(true)
->in(__DIR__);
diff --git a/composer.json b/composer.json
index 387dc0be34..084c5c7b5b 100755
--- a/composer.json
+++ b/composer.json
@@ -68,8 +68,9 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.53",
- "phpunit/phpunit": "^11.5",
- "rector/rector": "^2.0"
+ "mockery/mockery": "^1.6",
+ "phpunit/phpunit": "^10|^11",
+ "rector/rector": "^1.0"
},
"autoload": {
"psr-4": {
@@ -151,5 +152,9 @@
"allow-plugins": {
"kylekatarnls/update-helper": true
}
+ },
+ "scripts": {
+ "unit-tests": "phpunit --configuration phpunit.xml",
+ "format": "php-cs-fixer fix"
}
}
diff --git a/modules/fatture/src/Gestori/Scadenze.php b/modules/fatture/src/Gestori/Scadenze.php
index 0ab90adc11..a5f31a5345 100644
--- a/modules/fatture/src/Gestori/Scadenze.php
+++ b/modules/fatture/src/Gestori/Scadenze.php
@@ -21,6 +21,7 @@
namespace Modules\Fatture\Gestori;
use Modules\Fatture\Fattura;
+use Modules\Pagamenti\Pagamento;
use Modules\Scadenzario\Scadenza;
use Plugins\AssicurazioneCrediti\AssicurazioneCrediti;
use Plugins\ImportFE\FatturaElettronica as FatturaElettronicaImport;
@@ -33,8 +34,9 @@
*/
class Scadenze
{
- public function __construct(private readonly Fattura $fattura)
+ public function __construct(private readonly Fattura $fattura, $database = null)
{
+ $this->database = $database ?: database(); // Allow mocking
}
/**
@@ -49,11 +51,11 @@ public function registra($is_pagato = false, $ignora_fe = false)
$this->rimuovi();
if (!$ignora_fe && $this->fattura->module == 'Fatture di acquisto' && $this->fattura->isFE()) {
- $scadenze_fe = $this->registraScadenzeFE($is_pagato);
+ $scadenze = $this->registraScadenzeFE($is_pagato);
}
- if (empty($scadenze_fe)) {
- $this->registraScadenzeTradizionali($is_pagato);
+ if (empty($scadenze)) {
+ $scadenze = $this->registraScadenzeTradizionali($is_pagato);
}
// Registrazione scadenza per Ritenuta d'Acconto
@@ -78,8 +80,10 @@ public function registra($is_pagato = false, $ignora_fe = false)
$id_banca_controparte = $this->fattura->id_banca_controparte;
$importo = -$ritenuta_acconto;
- self::registraScadenza($this->fattura, $importo, $scadenza, $is_pagato, $id_pagamento, $id_banca_azienda, $id_banca_controparte, 'ritenutaacconto');
+ $scadenze[] = self::registraScadenza($this->fattura, $importo, $scadenza, $is_pagato, $id_pagamento, $id_banca_azienda, $id_banca_controparte, 'ritenutaacconto');
}
+
+ return $scadenze;
}
/**
@@ -88,14 +92,15 @@ public function registra($is_pagato = false, $ignora_fe = false)
public function rimuovi()
{
$scadenze = $this->fattura->scadenze;
+ $assicurazioni = [];
foreach ($scadenze as $scadenza) {
- $assicurazione_crediti = AssicurazioneCrediti::where('id_anagrafica', $scadenza->idanagrafica)->where('data_inizio', '<=', $scadenza->scadenza)->where('data_fine', '>=', $scadenza->scadenza)->first();
+ $assicurazione_crediti = this->trovaAssicurazioneCrediti($scadenza->idanagrafica, $scadenza->scadenza);
if (!empty($assicurazione_crediti)) {
$assicurazioni[] = $assicurazione_crediti;
}
}
- database()->delete('co_scadenziario', ['iddocumento' => $this->fattura->id]);
+ $this->database->delete('co_scadenziario', ['iddocumento' => $this->fattura->id]);
foreach ($assicurazioni as $assicurazione) {
$assicurazione->fixTotale();
@@ -117,18 +122,35 @@ protected function registraScadenza(Fattura $fattura, $importo, $data_scadenza,
$descrizione = $fattura->tipo->getTranslation('title').' numero '.$numero;
$idanagrafica = $fattura->idanagrafica;
- $scadenza = Scadenza::build($idanagrafica, $descrizione, $importo, $data_scadenza, $id_pagamento, $id_banca_azienda, $id_banca_controparte, $type, $is_pagato);
+ $scadenza = $this->generaScadenza($idanagrafica, $descrizione, $importo, $data_scadenza, $id_pagamento, $id_banca_azienda, $id_banca_controparte, $type, $is_pagato);
$scadenza->documento()->associate($fattura);
$scadenza->data_emissione = $fattura->data;
$scadenza->save();
- $assicurazione_crediti = AssicurazioneCrediti::where('id_anagrafica', $scadenza->idanagrafica)->where('data_inizio', '<=', $scadenza->scadenza)->where('data_fine', '>=', $scadenza->scadenza)->first();
+ $assicurazione_crediti = $this->trovaAssicurazioneCrediti($scadenza->idanagrafica, $scadenza->scadenza);
if (!empty($assicurazione_crediti)) {
$assicurazione_crediti->fixTotale();
$assicurazione_crediti->save();
}
+
+ return $scadenza;
+ }
+
+ protected function trovaPagamento($idpagamento): ?Pagamento
+ {
+ return Pagamento::where('id', $idpagamento)->first();
+ }
+
+ protected function trovaAssicurazioneCrediti($idanagrafica, $data_scadenza): ?AssicurazioneCrediti
+ {
+ return AssicurazioneCrediti::where('id_anagrafica', $idanagrafica)->where('data_inizio', '<=', $data_scadenza)->where('data_fine', '>=', $data_scadenza)->first();
+ }
+
+ protected function generaScadenza($idanagrafica, $descrizione, $importo, $data_scadenza, $id_pagamento, $id_banca_azienda, $id_banca_controparte, $type, $is_pagato): Scadenza
+ {
+ return Scadenza::build($idanagrafica, $descrizione, $importo, $data_scadenza, $id_pagamento, $id_banca_azienda, $id_banca_controparte, $type, $is_pagato);
}
/**
@@ -151,6 +173,7 @@ protected function registraScadenzeFE($is_pagato = false)
$pagamenti = isset($pagamenti[0]) ? $pagamenti : [$pagamenti];
}
+ $results = [];
foreach ($pagamenti as $pagamento) {
$rate = $pagamento['DettaglioPagamento'];
$rate = isset($rate[0]) ? $rate : [$rate];
@@ -162,11 +185,11 @@ protected function registraScadenzeFE($is_pagato = false)
$scadenza = !empty($rata['DataScadenzaPagamento']) ? FatturaElettronicaImport::parseDate($rata['DataScadenzaPagamento']) : $this->fattura->data;
$importo = $this->fattura->isNota() ? $rata['ImportoPagamento'] : -$rata['ImportoPagamento'];
- self::registraScadenza($this->fattura, $importo, $scadenza, $is_pagato, $id_pagamento, $id_banca_azienda, $id_banca_controparte);
+ $results[] = self::registraScadenza($this->fattura, $importo, $scadenza, $is_pagato, $id_pagamento, $id_banca_azienda, $id_banca_controparte);
}
}
- return !empty($pagamenti);
+ return $results;
}
/**
@@ -181,9 +204,10 @@ protected function registraScadenzeTradizionali($is_pagato = false)
$netto = $this->fattura->isNota() ? -$netto : $netto;
// Calcolo delle rate
- $rate = ($this->fattura->pagamento ?: \Modules\Pagamenti\Pagamento::where('id', $this->fattura->idpagamento)->first())->calcola($netto, $this->fattura->data, $this->fattura->idanagrafica);
+ $rate = ($this->fattura->pagamento ?: $this->trovaPagamento($this->fattura->idpagamento))->calcola($netto, $this->fattura->data, $this->fattura->idanagrafica, $this->database);
$direzione = $this->fattura->tipo->dir;
+ $results = [];
foreach ($rate as $rata) {
$scadenza = $rata['scadenza'];
$importo = $direzione == 'uscita' ? -$rata['importo'] : $rata['importo'];
@@ -191,7 +215,9 @@ protected function registraScadenzeTradizionali($is_pagato = false)
$id_banca_azienda = $this->fattura->id_banca_azienda;
$id_banca_controparte = $this->fattura->id_banca_controparte;
- self::registraScadenza($this->fattura, $importo, $scadenza, $is_pagato, $id_pagamento, $id_banca_azienda, $id_banca_controparte);
+ $results[] = self::registraScadenza($this->fattura, $importo, $scadenza, $is_pagato, $id_pagamento, $id_banca_azienda, $id_banca_controparte);
}
+
+ return $results;
}
}
diff --git a/modules/fatture/tests/GenerazioneScadenzeTest.php b/modules/fatture/tests/GenerazioneScadenzeTest.php
new file mode 100644
index 0000000000..5a66cd0b9a
--- /dev/null
+++ b/modules/fatture/tests/GenerazioneScadenzeTest.php
@@ -0,0 +1,187 @@
+getFatturaConRate('2025-03-30', 500, [
+ $this->mockModel(Pagamento::class, [
+ 'giorno' => 0,
+ 'num_giorni' => 90,
+ 'prc' => 100,
+ ]),
+ ]);
+
+ $gestore = $this->getGestore($fattura);
+ $gestore->shouldReceive('trovaPagamento')->andReturn(null);
+ $gestore->shouldReceive('trovaAssicurazioneCrediti')->andReturn(null);
+
+ $scadenze = $gestore->registra(false, true);
+
+ $this->assertEquals(1, count($scadenze));
+
+ $this->assertEquals('2025-03-30', $scadenze[0]->data_emissione);
+ $this->assertEquals('2025-06-28', $scadenze[0]->scadenza);
+ $this->assertEquals(500, $scadenze[0]->da_pagare);
+ }
+
+ public function testRimessaDiretta90GiorniAl15()
+ {
+ $fattura = $this->getFatturaConRate('2025-03-30', 500, [
+ $this->mockModel(Pagamento::class, [
+ 'giorno' => 15,
+ 'num_giorni' => 90,
+ 'prc' => 100,
+ ]),
+ ]);
+
+ $gestore = $this->getGestore($fattura);
+ $gestore->shouldReceive('trovaPagamento')->andReturn(null);
+ $gestore->shouldReceive('trovaAssicurazioneCrediti')->andReturn(null);
+
+ $scadenze = $gestore->registra(false, true);
+
+ $this->assertEquals(1, count($scadenze));
+
+ $this->assertEquals('2025-03-30', $scadenze[0]->data_emissione);
+ $this->assertEquals('2025-06-15', $scadenze[0]->scadenza);
+ $this->assertEquals(500, $scadenze[0]->da_pagare);
+ }
+
+ public function test3RateStatiche()
+ {
+ $fattura = $this->getFatturaConRate('2025-03-30', 600, [
+ $this->mockModel(Pagamento::class, [
+ 'giorno' => 0,
+ 'num_giorni' => 90,
+ 'prc' => 33,
+ ]),
+ $this->mockModel(Pagamento::class, [
+ 'giorno' => 0,
+ 'num_giorni' => 180,
+ 'prc' => 33,
+ ]),
+ $this->mockModel(Pagamento::class, [
+ 'giorno' => 0,
+ 'num_giorni' => 270,
+ 'prc' => 34,
+ ]),
+ ]);
+
+ $gestore = $this->getGestore($fattura);
+ $gestore->shouldReceive('trovaPagamento')->andReturn(null);
+ $gestore->shouldReceive('trovaAssicurazioneCrediti')->andReturn(null);
+
+ $scadenze = $gestore->registra(false, true);
+
+ $this->assertEquals(3, count($scadenze));
+
+ $this->assertEquals('2025-03-30', $scadenze[0]->data_emissione);
+ $this->assertEquals('2025-06-28', $scadenze[0]->scadenza);
+ $this->assertEquals(198, $scadenze[0]->da_pagare);
+
+ $this->assertEquals('2025-03-30', $scadenze[1]->data_emissione);
+ $this->assertEquals('2025-09-26', $scadenze[1]->scadenza);
+ $this->assertEquals(198, $scadenze[1]->da_pagare);
+
+ $this->assertEquals('2025-03-30', $scadenze[2]->data_emissione);
+ $this->assertEquals('2025-12-25', $scadenze[2]->scadenza);
+ $this->assertEquals(204, $scadenze[2]->da_pagare);
+ }
+
+ protected function mockModel($class, $attributes = null)
+ {
+ $ref = Mockery::mock($class)->shouldAllowMockingProtectedMethods()->makePartial();
+
+ $ref->shouldReceive('save')->andReturn(null);
+ $ref->shouldReceive('delete')->andReturn(null);
+ $ref->shouldReceive('getDateFormat')->andReturn('Y-m-d');
+
+ // Fix per gestione attributi su modello mocked di Eloquent
+ $ref->shouldReceive('getMutatedAttributes')->andReturnUsing(function () use ($class) {
+ preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($class)), $matches);
+
+ $ref = $matches[1];
+
+ return collect($ref)
+ ->merge($attributeMutatorMethods)
+ ->map(function ($match) {
+ return lcfirst(static::$snakeAttributes ? Str::snake($match) : $match);
+ })->all();
+ });
+
+ if (!empty($attributes)) {
+ foreach ($attributes as $key => $value) {
+ $ref->shouldReceive('getAttribute')->with($key)->andReturn($value);
+ }
+ }
+
+ return $ref;
+ }
+
+ protected function getFatturaConRate($data, $netto, $rate): Fattura
+ {
+ $tipo = $this->mockModel(Tipo::class);
+ $tipo->shouldReceive('getAttribute')->with('dir')->andReturn('entrata');
+ $tipo->shouldReceive('getTranslation')->andReturn('Fattura di vendita');
+
+ $pagamento = $this->mockModel(Pagamento::class);
+ $pagamento->shouldReceive('trovaRate')->andReturn($rate);
+
+ $fattura = $this->mockModel(Fattura::class, [
+ 'numero_esterno' => '2025-01',
+ 'idpagamento' => null,
+ 'id_banca_controparte' => null,
+ 'id_banca_azienda' => null,
+ 'idanagrafica' => 'test-an-1',
+ 'data' => $data,
+ 'id' => 'test-1',
+ 'netto' => $netto,
+ 'scadenze' => [],
+ 'pagamento' => $pagamento,
+ 'tipo' => $tipo,
+ 'ritenuta_acconto' => null,
+ ]);
+ $fattura->shouldReceive('associate')->andReturn(null);
+ $fattura->shouldReceive('isNota')->andReturn(false);
+
+ return $fattura;
+ }
+
+ protected function getGestore($fattura): Scadenze
+ {
+ $database = $this->mockModel(Database::class);
+ $database->shouldReceive('delete')->andReturn(null);
+ $database->shouldReceive('selectOne')->andReturn(null);
+
+ $gestore = Mockery::mock(Scadenze::class, [$fattura, $database])->shouldAllowMockingProtectedMethods()->makePartial();
+ $gestore->shouldReceive('generaScadenza')->andReturnUsing(function ($idanagrafica, $descrizione, $importo, $data_scadenza, $id_pagamento, $id_banca_azienda, $id_banca_controparte, $type, $is_pagato) use ($fattura) {
+ $scadenza = $this->mockModel(Scadenza::class, [
+ 'idanagrafica' => $idanagrafica,
+ 'descrizione' => $descrizione,
+ 'scadenza' => $data_scadenza,
+ 'da_pagare' => $importo,
+ 'tipo' => $type,
+ 'id_pagamento' => $id_pagamento,
+ 'id_banca_azienda' => $id_banca_azienda,
+ 'id_banca_controparte' => $id_banca_controparte,
+
+ 'pagato' => $is_pagato ? $importo : 0,
+ 'data_pagamento' => $is_pagato ? $data_scadenza : null,
+ ]);
+
+ $scadenza->shouldReceive('documento')->andReturn($fattura);
+
+ return $scadenza;
+ });
+
+ return $gestore;
+ }
+}
diff --git a/modules/pagamenti/src/Pagamento.php b/modules/pagamenti/src/Pagamento.php
index 2bfafa9b2e..e054226cbf 100755
--- a/modules/pagamenti/src/Pagamento.php
+++ b/modules/pagamenti/src/Pagamento.php
@@ -54,10 +54,17 @@ public function rate()
{
return $this->hasMany(Pagamento::class, 'id');
}
+
+ protected function trovaRate()
+ {
+ return Pagamento::where('name', '=', $this->name)->get()->sortBy('num_giorni')->toArray();
+ }
- public function calcola($importo, $data, $id_anagrafica)
+ public function calcola($importo, $data, $id_anagrafica, $database = null)
{
- $rate = Pagamento::where('name', '=', $this->name)->get()->sortBy('num_giorni')->pluck('id')->toArray();
+ $database = $database ?: database(); // Allow mocking
+
+ $rate = $this->trovaRate();
$number = count($rate);
$totale = 0.0;
@@ -66,7 +73,6 @@ public function calcola($importo, $data, $id_anagrafica)
$count = 0;
foreach ($rate as $key => $rata) {
$date = new Carbon($data);
- $rata = Pagamento::find($rata);
// X giorni esatti
if ($rata->giorno == 0) {
// Offset della rata
@@ -112,7 +118,7 @@ public function calcola($importo, $data, $id_anagrafica)
}
// Posticipo la scadenza in base alle regole pagamenti dell'anagrafica
- $regola_pagamento = database()->selectOne('an_pagamenti_anagrafiche', '*', ['idanagrafica' => $id_anagrafica, 'mese' => $date->format('m')]);
+ $regola_pagamento = $database->selectOne('an_pagamenti_anagrafiche', '*', ['idanagrafica' => $id_anagrafica, 'mese' => $date->format('m')]);
if (!empty($regola_pagamento)) {
$date->modify('last day of this month');
$date->addDay();
diff --git a/package.json b/package.json
index a38b54126e..46d4f8e066 100644
--- a/package.json
+++ b/package.json
@@ -96,9 +96,11 @@
"build-OSM": "gulp",
"dump-OSM": "php composer.phar dump-autoload",
"windows-fix": "yarn global add windows-build-tools",
- "php-cs-fix": "vendor/bin/php-cs-fixer fix",
+ "php-cs-fix": "php composer.phar run format",
"rector": "vendor/bin/rector process",
- "setup-corepack": "npm install -g corepack && corepack enable"
+ "setup-corepack": "npm install -g corepack && corepack enable",
+ "unit-tests": "php composer.phar run unit-tests"
},
"packageManager": "yarn@4.6.0"
}
+
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000000..3b387e9a57
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ tests/
+
+
+
+ ./modules/*/tests/
+
+
+
\ No newline at end of file
diff --git a/src/Util/Singleton.php b/src/Util/Singleton.php
index 9cd2c72a66..9445155b5d 100755
--- a/src/Util/Singleton.php
+++ b/src/Util/Singleton.php
@@ -47,7 +47,7 @@ private function __clone()
/**
* Private unserialize method to prevent unserializing of the Singleton instance.
*/
- private function __wakeup()
+ public function __wakeup()
{
}
diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php
deleted file mode 100644
index cbcb837df3..0000000000
--- a/tests/_bootstrap.php
+++ /dev/null
@@ -1,10 +0,0 @@
- $namespace) {
- Autoload::addNamespace($namespace.'\\', __DIR__.'/../'.$path.'/custom/src');
- Autoload::addNamespace($namespace.'\\', __DIR__.'/../'.$path.'/src');
-}
\ No newline at end of file