Roberto Santini 3 weeks ago
parent
commit
c6bc131acf

+ 71
- 0
app/Models/ContrattoServizioRiga.php View File

@@ -339,6 +339,77 @@ class ContrattoServizioRiga extends \App\Models\AbstractModels\AbstractContratto
339 339
         return $periodo !== '' ? $periodo : 'una_tantum';
340 340
     }
341 341
 
342
+    /**
343
+     * Cadenza della fattura. Sulle ore il periodo della riga è solo il monte
344
+     * (1 ora al mese): se il contratto è più lungo, si fattura il suo ciclo
345
+     * (un annuale = 12 ore all'apertura dell'anno).
346
+     */
347
+    public function periodoFatturazione(?string $periodoContratto = null): string
348
+    {
349
+        $ciclo = $this->periodoCiclo($periodoContratto);
350
+        if (! $this->isUnitaOra()) {
351
+            return $ciclo;
352
+        }
353
+
354
+        $mesiRiga = self::mesiPeriodo((string) $this->periodo);
355
+        $mesiContratto = self::mesiPeriodo((string) $periodoContratto);
356
+        if ($mesiRiga > 0 && $mesiContratto > $mesiRiga) {
357
+            return (string) $periodoContratto;
358
+        }
359
+
360
+        return $ciclo;
361
+    }
362
+
363
+    /**
364
+     * Ore comprese in un ciclo di fattura. 1 ora mensile in un annuale = 12.
365
+     */
366
+    public function quantitaFatturata(?string $periodoContratto = null): float
367
+    {
368
+        $qty = (float) ($this->quantita ?? 1);
369
+        if (! $this->isUnitaOra()) {
370
+            return $qty;
371
+        }
372
+
373
+        $mesiRiga = self::mesiPeriodo((string) $this->periodo);
374
+        $mesiFattura = self::mesiPeriodo($this->periodoFatturazione($periodoContratto));
375
+        if ($mesiRiga < 1 || $mesiFattura < 1) {
376
+            return $qty;
377
+        }
378
+
379
+        $volte = intdiv($mesiFattura, $mesiRiga);
380
+
381
+        return $volte > 1 ? round($qty * $volte, 2) : $qty;
382
+    }
383
+
384
+    public function importoFatturazione(?string $periodoContratto = null): float
385
+    {
386
+        if ($this->isAConsumo() || $this->prezzo_netto === null) {
387
+            return 0.0;
388
+        }
389
+
390
+        return round($this->quantitaFatturata($periodoContratto) * (float) $this->prezzo_netto, 2);
391
+    }
392
+
393
+    public function labelQuantita(float $quantita): string
394
+    {
395
+        $nome = optional($this->prodotto)->nome ?: 'Servizio';
396
+        $qty = rtrim(rtrim(number_format($quantita, 2, ',', ''), '0'), ',');
397
+        $um = $this->unita_misura ?: optional($this->prodotto)->unita_misura;
398
+
399
+        return trim($nome.' ('.$qty.($um ? ' '.$um : '').')');
400
+    }
401
+
402
+    public static function mesiPeriodo(?string $periodo): int
403
+    {
404
+        return match ($periodo) {
405
+            'mensile' => 1,
406
+            'trimestrale' => 3,
407
+            'semestrale' => 6,
408
+            'annuale' => 12,
409
+            default => 0,
410
+        };
411
+    }
412
+
342 413
     /**
343 414
      * Mese, anno e forfait non hanno un periodo separato: non va persistito.
344 415
      */

+ 22
- 9
app/Services/FatturazioneGeneraService.php View File

@@ -651,7 +651,7 @@ class FatturazioneGeneraService
651 651
             }
652 652
 
653 653
             foreach ($contratto->righe as $riga) {
654
-                $periodoKey = $riga->periodoCiclo($contratto->periodo);
654
+                $periodoKey = $riga->periodoFatturazione($contratto->periodo);
655 655
                 foreach ($this->dateOccorrenzeRigaNelPeriodo($contratto, $riga, $from, $to) as $occurrence) {
656 656
                     [$pDa, $pA] = $this->finestraCiclo($periodoKey, $occurrence, $this->decorrenzaRiga($contratto, $riga));
657 657
                     if ($this->cicloPrecedenteAllaSoglia($occurrence, $pDa, $rinnoviDa)) {
@@ -660,11 +660,16 @@ class FatturazioneGeneraService
660 660
                     if ($this->canoneGiaFatturato($contratto->id, $riga->id, $pDa, $pA)) {
661 661
                         continue;
662 662
                     }
663
-                    $importo = $riga->importo_ciclo;
663
+                    $importo = $riga->importoFatturazione($contratto->periodo);
664 664
                     if ($importo <= 0) {
665 665
                         continue;
666 666
                     }
667
-                    $this->aggiungiCanoneCliente($clienti[$key], $contratto, $riga, $importo, $pDa, $pA);
667
+                    $quantita = $riga->isUnitaOra() ? $riga->quantitaFatturata($contratto->periodo) : 1.0;
668
+                    $prezzo = $riga->isUnitaOra() ? (float) $riga->prezzo_netto : $importo;
669
+                    $label = ($riga->isUnitaOra() && $quantita > (float) ($riga->quantita ?? 1))
670
+                        ? $riga->labelQuantita($quantita)
671
+                        : null;
672
+                    $this->aggiungiCanoneCliente($clienti[$key], $contratto, $riga, $importo, $pDa, $pA, $quantita, $prezzo, $label);
668 673
                 }
669 674
             }
670 675
 
@@ -740,7 +745,7 @@ class FatturazioneGeneraService
740 745
             return [];
741 746
         }
742 747
 
743
-        $periodo = $riga->periodoCiclo($contratto->periodo);
748
+        $periodo = $riga->periodoFatturazione($contratto->periodo);
744 749
         if ($periodo === 'a_consumo') {
745 750
             return [];
746 751
         }
@@ -1129,11 +1134,19 @@ class FatturazioneGeneraService
1129 1134
         ?ContrattoServizioRiga $riga,
1130 1135
         float $importo,
1131 1136
         Carbon $pDa,
1132
-        Carbon $pA
1137
+        Carbon $pA,
1138
+        float $quantita = 1,
1139
+        ?float $prezzoUnitario = null,
1140
+        ?string $labelRiga = null
1133 1141
     ): void {
1134 1142
         $importo = round($importo, 2);
1135
-        $labelRiga = $riga ? $riga->label : 'Canone contratto';
1136
-        $periodoLabel = $riga ? $riga->periodo_label : $contratto->periodo_label;
1143
+        $quantita = $quantita > 0 ? $quantita : 1;
1144
+        $prezzoUnitario = round($prezzoUnitario ?? $importo, 2);
1145
+        $labelRiga = $labelRiga ?: ($riga ? $riga->label : 'Canone contratto');
1146
+        $periodoFattura = $riga?->periodoFatturazione($contratto->periodo);
1147
+        $periodoLabel = $riga && $periodoFattura && $periodoFattura !== (string) $riga->periodo
1148
+            ? (\App\Models\Prodotto::PERIODI[$periodoFattura] ?? $contratto->periodo_label)
1149
+            : ($riga ? $riga->periodo_label : $contratto->periodo_label);
1137 1150
         $descrizione = $contratto->label.' — '.$labelRiga.' ('.$pDa->format('d/m/Y').' – '.$pA->format('d/m/Y').')';
1138 1151
         $cliente['rinnovi'][] = [
1139 1152
             'tipo' => 'canone',
@@ -1152,9 +1165,9 @@ class FatturazioneGeneraService
1152 1165
         $cliente['voci'][] = [
1153 1166
             'tipo' => FatturaRiga::TIPO_CANONE,
1154 1167
             'descrizione' => $descrizione,
1155
-            'quantita' => 1,
1168
+            'quantita' => $quantita,
1156 1169
             'unita_misura' => $riga?->unita_misura ?: 'forfait',
1157
-            'prezzo_unitario' => $importo,
1170
+            'prezzo_unitario' => $prezzoUnitario,
1158 1171
             'importo' => $importo,
1159 1172
             'minuti' => null,
1160 1173
             'annotazione_id' => null,

+ 1
- 0
resources/views/fatturazione/genera.blade.php View File

@@ -106,6 +106,7 @@
106 106
   L'ordine collegato a una segnalazione del periodo si fattura una volta, con le sue righe.
107 107
   Le scadenze sono segnalate ma non sommate (importo suggerito a parte).
108 108
   I rinnovi si fatturano solo se il ciclo si apre dal {{ Config::rinnoviDa()->format('d/m/Y') }} in poi: un annuale partito il 01/01/2026 non entra nel 2026, un mensile sì dalla rata di quel giorno.
109
+  Le ore seguono il periodo del contratto: 1 ora al mese dentro un annuale è una sola riga da 12 ore all'apertura dell'anno. Il monte di 1 ora al mese resta sulle segnalazioni.
109 110
   Il costo d'uscita ({{ Config::formatCosto(Config::costoUscitaDefault()) }}) si aggiunge se spuntato in registrazione tempo.
110 111
 </p>
111 112
 

Loading…
Cancel
Save