Browse Source

fatture in cloud

Roberto Santini 3 weeks ago
parent
commit
bac6e66c09

+ 1
- 1
app/Http/Controllers/AziendaFicController.php View File

@@ -36,7 +36,7 @@ class AziendaFicController extends Controller implements HasMiddleware
36 36
             'confronto' => null,
37 37
             'message' => $fic->configured()
38 38
                 ? null
39
-                : 'Inserisci Company ID e access token in Configurazione → Fatture in Cloud. Il token deve avere gli scope entity.clients:r e entity.clients:a.',
39
+                : 'Inserisci Company ID e access token in Configurazione → Fatture in Cloud. Il token deve avere gli scope entity.clients:r, entity.clients:a, settings:r e issued_documents.invoices:a.',
40 40
         ];
41 41
 
42 42
         if ($fic->configured() && $azienda->ficCollegamento) {

+ 13
- 1
app/Http/Controllers/FatturazioneController.php View File

@@ -6,6 +6,7 @@ use App\DataTables\FatturaDataTable;
6 6
 use App\Models\Config;
7 7
 use App\Models\Fattura;
8 8
 use App\Models\FatturaRiga;
9
+use App\Services\FatturaFicService;
9 10
 use App\Services\FatturazioneGeneraService;
10 11
 use Carbon\Carbon;
11 12
 use Illuminate\Http\Request;
@@ -34,7 +35,7 @@ class FatturazioneController extends Controller implements HasMiddleware
34 35
       new Middleware('permission:view-fatturazione', only: ['admin_index', 'admin_genera', 'admin_show']),
35 36
       new Middleware('permission:genera-fatturazione', only: ['admin_genera']),
36 37
       new Middleware('permission:create-fattura', only: ['admin_crea']),
37
-      new Middleware('permission:edit-fattura', only: ['admin_update', 'admin_emetti', 'admin_annulla']),
38
+      new Middleware('permission:edit-fattura', only: ['admin_update', 'admin_emetti', 'admin_annulla', 'admin_invia_fic']),
38 39
       new Middleware('permission:delete-fattura', only: ['admin_destroy']),
39 40
     ];
40 41
   }
@@ -239,6 +240,17 @@ class FatturazioneController extends Controller implements HasMiddleware
239 240
     return redirect()->back()->with('success', 'Fattura emessa.');
240 241
   }
241 242
 
243
+  public function admin_invia_fic($id, FatturaFicService $fic)
244
+  {
245
+    try {
246
+      $message = $fic->invia(Fattura::findOrFail($id));
247
+    } catch (RuntimeException $e) {
248
+      return redirect()->back()->with('error', $e->getMessage());
249
+    }
250
+
251
+    return redirect()->back()->with('success', $message);
252
+  }
253
+
242 254
   public function admin_annulla($id)
243 255
   {
244 256
     $fattura = Fattura::findOrFail($id);

+ 12
- 0
app/Models/Fattura.php View File

@@ -30,6 +30,11 @@ class Fattura extends \App\Models\AbstractModels\AbstractFattura
30 30
     public function getNumeroDocumentoAttribute(): string
31 31
     {
32 32
         $n = $this->numero ?: $this->id;
33
+        $numeration = trim((string) data_get($this->meta, 'fic.numeration'));
34
+        if ($this->ficDocumentId() && $numeration !== '') {
35
+            return $n.(str_starts_with($numeration, '/') ? '' : '/').$numeration;
36
+        }
37
+
33 38
         $year = $this->data
34 39
             ? Carbon::parse($this->data)->format('y')
35 40
             : now()->format('y');
@@ -57,6 +62,13 @@ class Fattura extends \App\Models\AbstractModels\AbstractFattura
57 62
         return $this->stato !== self::STATO_ANNULLATA;
58 63
     }
59 64
 
65
+    public function ficDocumentId(): ?int
66
+    {
67
+        $id = data_get($this->meta, 'fic.document_id');
68
+
69
+        return $id ? (int) $id : null;
70
+    }
71
+
60 72
     public function scopeNonAnnullate($query)
61 73
     {
62 74
         return $query->where('stato', '!=', self::STATO_ANNULLATA);

+ 191
- 0
app/Services/FatturaFicService.php View File

@@ -0,0 +1,191 @@
1
+<?php
2
+
3
+namespace App\Services;
4
+
5
+use App\Models\Config;
6
+use App\Models\Fattura;
7
+use App\Models\FatturaRiga;
8
+use App\Models\FicCollegamento;
9
+use App\Models\Prodotto;
10
+use Carbon\Carbon;
11
+use RuntimeException;
12
+
13
+class FatturaFicService
14
+{
15
+    public function __construct(protected FattureInCloudClient $fic) {}
16
+
17
+    public function invia(Fattura $fattura): string
18
+    {
19
+        $fattura->loadMissing(['righe', 'user']);
20
+        if ($fattura->isAnnullata()) {
21
+            throw new RuntimeException('Una fattura annullata non si invia.');
22
+        }
23
+        if ($fattura->ficDocumentId()) {
24
+            throw new RuntimeException('Questa fattura è già su Fatture in Cloud.');
25
+        }
26
+        if ($fattura->righe->isEmpty()) {
27
+            throw new RuntimeException('La fattura non ha righe.');
28
+        }
29
+
30
+        $collegamento = $this->collegamento($fattura);
31
+        if (! $collegamento) {
32
+            throw new RuntimeException('Collega il cliente a Fatture in Cloud dalla scheda azienda, poi riprova.');
33
+        }
34
+
35
+        try {
36
+            $client = $this->fic->getClient((int) $collegamento->fic_client_id);
37
+            $vatId = $this->vatId((float) ($fattura->iva_percentuale ?? Config::ivaPercentualeDefault()));
38
+            $document = $this->fic->createIssuedDocument($this->payload($fattura, $client, $vatId));
39
+            if (! isset($document['number']) || $document['number'] === '' || $document['number'] === null) {
40
+                $document = $this->fic->getIssuedDocument((int) $document['id']);
41
+            }
42
+        } catch (RuntimeException $e) {
43
+            throw new RuntimeException($this->conScope($e->getMessage()));
44
+        }
45
+
46
+        $numeroFic = (int) $document['number'];
47
+        if ($numeroFic < 1) {
48
+            throw new RuntimeException('Fatture in Cloud ha creato il documento ma non ha assegnato un numero.');
49
+        }
50
+
51
+        $meta = is_array($fattura->meta) ? $fattura->meta : [];
52
+        $meta['fic'] = [
53
+            'document_id' => (int) $document['id'],
54
+            'number' => $numeroFic,
55
+            'numeration' => $document['numeration'] ?? null,
56
+            'date' => isset($document['date']) ? (string) $document['date'] : null,
57
+            'sent_at' => now()->toIso8601String(),
58
+        ];
59
+        $fattura->meta = $meta;
60
+        $fattura->numero = $numeroFic;
61
+        if (! empty($document['date'])) {
62
+            $fattura->data = Carbon::parse($document['date'])->toDateString();
63
+        }
64
+        if ($fattura->isBozza()) {
65
+            $fattura->stato = Fattura::STATO_EMESSA;
66
+            if (! $fattura->data) {
67
+                $fattura->data = now()->toDateString();
68
+            }
69
+        }
70
+        $fattura->save();
71
+
72
+        return 'Fattura '.$fattura->numero_documento.' allineata a Fatture in Cloud. L\'invio allo SDI lo fai da lì.';
73
+    }
74
+
75
+    protected function collegamento(Fattura $fattura): ?FicCollegamento
76
+    {
77
+        $ids = array_values(array_filter([
78
+            (int) $fattura->user_id,
79
+            (int) ($fattura->user->azienda_id ?? 0),
80
+        ]));
81
+
82
+        return FicCollegamento::query()->whereIn('user_id', $ids)->first();
83
+    }
84
+
85
+    /**
86
+     * @param  array<string, mixed>  $client
87
+     * @return array<string, mixed>
88
+     */
89
+    protected function payload(Fattura $fattura, array $client, int $vatId): array
90
+    {
91
+        $data = $fattura->data ? Carbon::parse($fattura->data)->toDateString() : now()->toDateString();
92
+        $entity = ['id' => (int) $client['id']];
93
+        foreach ([
94
+            'name',
95
+            'vat_number',
96
+            'tax_code',
97
+            'address_street',
98
+            'address_postal_code',
99
+            'address_city',
100
+            'address_province',
101
+            'country',
102
+            'email',
103
+        ] as $field) {
104
+            $value = trim((string) ($client[$field] ?? ''));
105
+            if ($value !== '') {
106
+                $entity[$field] = $value;
107
+            }
108
+        }
109
+        if (empty($entity['name'])) {
110
+            $entity['name'] = $fattura->ragione_sociale ?: 'Cliente';
111
+        }
112
+
113
+        $items = [];
114
+        foreach ($fattura->righe as $riga) {
115
+            $items[] = $this->item($riga, $vatId);
116
+        }
117
+
118
+        $payload = [
119
+            'type' => 'invoice',
120
+            'entity' => $entity,
121
+            'date' => $data,
122
+            'items_list' => $items,
123
+            'e_invoice' => false,
124
+        ];
125
+
126
+        if ($fattura->periodo_da && $fattura->periodo_a) {
127
+            $payload['visible_subject'] = 'Periodo '
128
+                .Carbon::parse($fattura->periodo_da)->format('d/m/Y')
129
+                .' – '
130
+                .Carbon::parse($fattura->periodo_a)->format('d/m/Y');
131
+        }
132
+
133
+        return $payload;
134
+    }
135
+
136
+    /**
137
+     * @return array<string, mixed>
138
+     */
139
+    protected function item(FatturaRiga $riga, int $vatId): array
140
+    {
141
+        $qty = (float) ($riga->quantita ?? 1);
142
+        if ($qty <= 0) {
143
+            $qty = 1;
144
+        }
145
+        $name = trim((string) $riga->descrizione);
146
+        if ($name === '') {
147
+            $name = $riga->tipo_label;
148
+        }
149
+        if (mb_strlen($name) > 255) {
150
+            $name = mb_substr($name, 0, 252).'…';
151
+        }
152
+
153
+        $item = [
154
+            'name' => $name,
155
+            'qty' => $qty,
156
+            'net_price' => round((float) ($riga->importo ?? 0) / $qty, 5),
157
+            'vat' => ['id' => $vatId],
158
+        ];
159
+        $measure = Prodotto::UNITA_MISURA[$riga->unita_misura] ?? $riga->unita_misura;
160
+        if (filled($measure)) {
161
+            $item['measure'] = mb_substr((string) $measure, 0, 20);
162
+        }
163
+
164
+        return $item;
165
+    }
166
+
167
+    protected function vatId(float $percent): int
168
+    {
169
+        foreach ($this->fic->vatTypes() as $type) {
170
+            if (! is_array($type) || ! empty($type['is_disabled'])) {
171
+                continue;
172
+            }
173
+            if (abs((float) ($type['value'] ?? -1) - $percent) < 0.001 && isset($type['id'])) {
174
+                return (int) $type['id'];
175
+            }
176
+        }
177
+
178
+        $label = rtrim(rtrim(number_format($percent, 2, ',', ''), '0'), ',');
179
+
180
+        throw new RuntimeException('Su Fatture in Cloud non c\'è un\'aliquota IVA del '.$label.'%.');
181
+    }
182
+
183
+    protected function conScope(string $message): string
184
+    {
185
+        if (preg_match('/403|permission|permess|scope/i', $message)) {
186
+            return $message.' Ricrea il token con anche gli scope settings:r e issued_documents.invoices:a.';
187
+        }
188
+
189
+        return $message;
190
+    }
191
+}

+ 1
- 1
app/Services/FatturazioneGeneraService.php View File

@@ -498,7 +498,7 @@ class FatturazioneGeneraService
498 498
             'importo_tempo' => round(($minuti / 60) * $tariffaDefault, 2),
499 499
             'importo_spesa' => $fatturaSpesa,
500 500
             'tariffa_mancante' => $mancante,
501
-            'note' => 'Senza contratto né ordine',
501
+            'note' => null,
502 502
         ], $annotazione, $segnalazione);
503 503
     }
504 504
 

+ 45
- 0
app/Services/FattureInCloudClient.php View File

@@ -379,6 +379,51 @@ class FattureInCloudClient
379 379
         return $fromEnv !== '' ? $fromEnv : null;
380 380
     }
381 381
 
382
+    /**
383
+     * @return array<int, array<string, mixed>>
384
+     */
385
+    public function vatTypes(): array
386
+    {
387
+        $json = $this->decode($this->http()->get($this->url('/info/vat_types')));
388
+
389
+        return is_array($json['data'] ?? null) ? $json['data'] : [];
390
+    }
391
+
392
+    /**
393
+     * @param  array<string, mixed>  $data
394
+     * @return array<string, mixed>
395
+     */
396
+    public function createIssuedDocument(array $data): array
397
+    {
398
+        $json = $this->decode($this->http()->post($this->url('/issued_documents'), [
399
+            'data' => $data,
400
+        ]));
401
+
402
+        return $this->documentOrFail($json['data'] ?? null);
403
+    }
404
+
405
+    /**
406
+     * @return array<string, mixed>
407
+     */
408
+    public function getIssuedDocument(int $id): array
409
+    {
410
+        $json = $this->decode($this->http()->get($this->url('/issued_documents/'.$id)));
411
+
412
+        return $this->documentOrFail($json['data'] ?? null);
413
+    }
414
+
415
+    /**
416
+     * @return array<string, mixed>
417
+     */
418
+    private function documentOrFail(mixed $document): array
419
+    {
420
+        if (! is_array($document) || empty($document['id'])) {
421
+            throw new RuntimeException('Fatture in Cloud non ha restituito la fattura creata.');
422
+        }
423
+
424
+        return $document;
425
+    }
426
+
382 427
     private function url(string $path): string
383 428
     {
384 429
         return '/c/'.$this->companyId().$path;

+ 1
- 1
resources/views/azienda/fic-scripts.blade.php View File

@@ -159,7 +159,7 @@ $(function () {
159 159
         if (!data.configured) {
160 160
           const el = document.getElementById('ficConfigAlert');
161 161
           el.classList.remove('d-none');
162
-          el.innerHTML = 'Inserisci Company ID e access token nella <a href="' + @json(route('admin.config.index')) + '">configurazione</a>, scheda Fatture in Cloud. Il token va creato come da <a href="https://developers.fattureincloud.it/docs/getting-started/" target="_blank" rel="noopener">guida Fatture in Cloud</a> e deve avere gli scope <code>entity.clients:r</code> e <code>entity.clients:a</code>.';
162
+          el.innerHTML = 'Inserisci Company ID e access token nella <a href="' + @json(route('admin.config.index')) + '">configurazione</a>, scheda Fatture in Cloud. Il token va creato come da <a href="https://developers.fattureincloud.it/docs/getting-started/" target="_blank" rel="noopener">guida Fatture in Cloud</a> e deve avere gli scope <code>entity.clients:r</code>, <code>entity.clients:a</code>, <code>settings:r</code> e <code>issued_documents.invoices:a</code>.';
163 163
           document.getElementById('ficSearchWrap').classList.add('d-none');
164 164
           document.getElementById('ficCreate').classList.add('d-none');
165 165
           return;

+ 1
- 1
resources/views/config/index.blade.php View File

@@ -186,7 +186,7 @@ $groupKeys = array_keys($groups);
186 186
               @case('password')
187 187
               <input type="password" class="form-control" name="value[{{ $config->key }}]" id="{{ $fid }}-control" value="" autocomplete="new-password" placeholder="{{ $config->value ? 'Token già salvato' : '' }}" {{ $disabled }}>
188 188
               @if($config->key === Config::FIC_ACCESS_TOKEN)
189
-              <div class="form-text">Token manuale di <a href="https://developers.fattureincloud.it/docs/getting-started/" target="_blank" rel="noopener">Fatture in Cloud</a> con scope <code>entity.clients:r</code> e <code>entity.clients:a</code>. Lascia vuoto per non sostituirlo.</div>
189
+              <div class="form-text">Token manuale di <a href="https://developers.fattureincloud.it/docs/getting-started/" target="_blank" rel="noopener">Fatture in Cloud</a> con scope <code>entity.clients:r</code>, <code>entity.clients:a</code>, <code>settings:r</code> e <code>issued_documents.invoices:a</code>. Lascia vuoto per non sostituirlo.</div>
190 190
               @endif
191 191
               @break
192 192
 

+ 24
- 28
resources/views/fatturazione/show.blade.php View File

@@ -35,6 +35,17 @@
35 35
     </button>
36 36
   </form>
37 37
   @endif
38
+  @if($fattura->ficDocumentId())
39
+  <span class="badge bg-label-success">Su Fatture in Cloud n. {{ data_get($fattura->meta, 'fic.number') }}{{ data_get($fattura->meta, 'fic.numeration') }}</span>
40
+  @elseif(! $fattura->isAnnullata())
41
+  <form method="POST" action="{{ route('admin.fatturazione.fic', $fattura->id) }}" class="d-inline"
42
+    onsubmit="return confirm('Creare questa fattura su Fatture in Cloud?');">
43
+    @csrf
44
+    <button type="submit" class="btn btn-label-info btn-sm">
45
+      <i class="bx bx-cloud-upload me-1"></i>Invia a Fatture in Cloud
46
+    </button>
47
+  </form>
48
+  @endif
38 49
   @if(! $fattura->isAnnullata())
39 50
   <form method="POST" action="{{ route('admin.fatturazione.annulla', $fattura->id) }}" class="d-inline"
40 51
     onsubmit="return confirm('Annullare questa fattura? Le voci torneranno disponibili al genera.');">
@@ -202,7 +213,6 @@
202 213
       <table class="table mb-0" id="righeTable">
203 214
         <thead>
204 215
           <tr>
205
-            <th style="width: 160px;">Tipo</th>
206 216
             <th>Descrizione</th>
207 217
             <th style="width: 90px;">Qty</th>
208 218
             <th style="width: 120px;">U.M.</th>
@@ -217,16 +227,10 @@
217 227
           <tr class="riga-row">
218 228
             <td>
219 229
               <input type="hidden" name="righe[{{ $i }}][id]" value="{{ $riga['id'] ?? '' }}">
220
-              <select name="righe[{{ $i }}][tipo]" class="form-select form-select-sm">
221
-                @foreach(\App\Models\FatturaRiga::TIPI as $val => $label)
222
-                <option value="{{ $val }}" @selected(($riga['tipo'] ?? '') === $val)>{{ $label }}</option>
223
-                @endforeach
224
-              </select>
225
-              <input type="number" min="0" step="1" name="righe[{{ $i }}][minuti]" class="form-control form-control-sm mt-1" value="{{ $riga['minuti'] ?? '' }}" placeholder="Minuti">
226
-            </td>
227
-            <td>
230
+              <input type="hidden" name="righe[{{ $i }}][tipo]" value="{{ $riga['tipo'] ?? \App\Models\FatturaRiga::TIPO_MANUALE }}">
231
+              <input type="hidden" name="righe[{{ $i }}][minuti]" value="{{ $riga['minuti'] ?? '' }}">
228 232
               <input type="text" name="righe[{{ $i }}][descrizione]" class="form-control form-control-sm" value="{{ $riga['descrizione'] ?? '' }}" placeholder="Descrizione">
229
-              <textarea name="righe[{{ $i }}][nota]" class="form-control form-control-sm mt-1" rows="1" placeholder="Nota">{{ $riga['nota'] ?? '' }}</textarea>
233
+              <textarea name="righe[{{ $i }}][nota]" class="form-control form-control-sm mt-1" rows="1" placeholder="Nota">{{ ($riga['nota'] ?? '') === 'Senza contratto né ordine' ? '' : ($riga['nota'] ?? '') }}</textarea>
230 234
               @if(!empty($riga['segnalazione_id']))
231 235
               <div class="small mt-1"><a href="{{ route('admin.segnalazione.show', $riga['segnalazione_id']) }}">Apri ticket</a></div>
232 236
               @endif
@@ -258,21 +262,21 @@
258 262
         </tbody>
259 263
         <tfoot>
260 264
           <tr>
261
-            <th colspan="6" class="text-end align-middle">Imponibile</th>
265
+            <th colspan="5" class="text-end align-middle">Imponibile</th>
262 266
             <th>
263 267
               <input type="number" step="0.01" name="imponibile" id="imponibile" class="form-control form-control-sm text-end totale-input" value="{{ old('imponibile', $numInput($fattura->imponibile)) }}">
264 268
             </th>
265 269
             <th></th>
266 270
           </tr>
267 271
           <tr>
268
-            <th colspan="6" class="text-end align-middle">IVA</th>
272
+            <th colspan="5" class="text-end align-middle">IVA</th>
269 273
             <th>
270 274
               <input type="number" step="0.01" name="iva" id="iva" class="form-control form-control-sm text-end totale-input" value="{{ old('iva', $numInput($fattura->iva)) }}">
271 275
             </th>
272 276
             <th></th>
273 277
           </tr>
274 278
           <tr>
275
-            <th colspan="6" class="text-end align-middle">Totale</th>
279
+            <th colspan="5" class="text-end align-middle">Totale</th>
276 280
             <th>
277 281
               <input type="number" step="0.01" name="totale" id="totale" class="form-control form-control-sm text-end fw-semibold totale-input" value="{{ old('totale', $numInput($fattura->totale)) }}">
278 282
             </th>
@@ -292,14 +296,8 @@
292 296
   <tr class="riga-row">
293 297
     <td>
294 298
       <input type="hidden" name="righe[__IDX__][id]" value="">
295
-      <select name="righe[__IDX__][tipo]" class="form-select form-select-sm">
296
-        @foreach(\App\Models\FatturaRiga::TIPI as $val => $label)
297
-        <option value="{{ $val }}" @selected($val === \App\Models\FatturaRiga::TIPO_MANUALE)>{{ $label }}</option>
298
-        @endforeach
299
-      </select>
300
-      <input type="number" min="0" step="1" name="righe[__IDX__][minuti]" class="form-control form-control-sm mt-1" placeholder="Minuti">
301
-    </td>
302
-    <td>
299
+      <input type="hidden" name="righe[__IDX__][tipo]" value="{{ \App\Models\FatturaRiga::TIPO_MANUALE }}">
300
+      <input type="hidden" name="righe[__IDX__][minuti]" value="">
303 301
       <input type="text" name="righe[__IDX__][descrizione]" class="form-control form-control-sm" placeholder="Descrizione">
304 302
       <textarea name="righe[__IDX__][nota]" class="form-control form-control-sm mt-1" rows="1" placeholder="Nota"></textarea>
305 303
     </td>
@@ -358,7 +356,6 @@
358 356
         <table class="table mb-0">
359 357
           <thead>
360 358
             <tr>
361
-              <th>Tipo</th>
362 359
               <th>Descrizione</th>
363 360
               <th>Qty</th>
364 361
               <th>Prezzo</th>
@@ -368,7 +365,6 @@
368 365
           <tbody>
369 366
             @forelse($fattura->righe as $riga)
370 367
             <tr>
371
-              <td><span class="badge bg-label-secondary">{{ $riga->tipo_label }}</span></td>
372 368
               <td>
373 369
                 {{ $riga->descrizione }}
374 370
                 @if($riga->segnalazione_id)
@@ -380,7 +376,7 @@
380 376
                 @if($riga->ordine_lavoro_id)
381 377
                 <div class="small"><a href="{{ route('admin.ordine_lavoro.show', $riga->ordine_lavoro_id) }}">Apri ordine</a></div>
382 378
                 @endif
383
-                @if($riga->nota)
379
+                @if($riga->nota && $riga->nota !== 'Senza contratto né ordine')
384 380
                 <div class="text-muted small">{{ $riga->nota }}</div>
385 381
                 @endif
386 382
               </td>
@@ -392,20 +388,20 @@
392 388
               <td class="text-end">{{ \App\Models\Config::formatCosto($riga->importo ?? 0) }}</td>
393 389
             </tr>
394 390
             @empty
395
-            <tr><td colspan="5" class="text-muted">Nessuna voce</td></tr>
391
+            <tr><td colspan="4" class="text-muted">Nessuna voce</td></tr>
396 392
             @endforelse
397 393
           </tbody>
398 394
           <tfoot>
399 395
             <tr>
400
-              <th colspan="4" class="text-end">Imponibile</th>
396
+              <th colspan="3" class="text-end">Imponibile</th>
401 397
               <th class="text-end">{{ \App\Models\Config::formatCosto($fattura->imponibile ?? 0) }}</th>
402 398
             </tr>
403 399
             <tr>
404
-              <th colspan="4" class="text-end">IVA {{ rtrim(rtrim(number_format((float) $fattura->iva_percentuale, 2, ',', ''), '0'), ',') }}%</th>
400
+              <th colspan="3" class="text-end">IVA {{ rtrim(rtrim(number_format((float) $fattura->iva_percentuale, 2, ',', ''), '0'), ',') }}%</th>
405 401
               <th class="text-end">{{ \App\Models\Config::formatCosto($fattura->iva ?? 0) }}</th>
406 402
             </tr>
407 403
             <tr>
408
-              <th colspan="4" class="text-end">Totale</th>
404
+              <th colspan="3" class="text-end">Totale</th>
409 405
               <th class="text-end">{{ \App\Models\Config::formatCosto($fattura->totale ?? 0) }}</th>
410 406
             </tr>
411 407
           </tfoot>

+ 1
- 0
routes/web.php View File

@@ -122,6 +122,7 @@ Route::middleware(['auth:sanctum', config('jetstream.auth_session')])->prefix('a
122 122
     Route::get('fatturazione/{id}', [FatturazioneController::class, 'admin_show'])->name('admin.fatturazione.show')->whereNumber('id');
123 123
     Route::put('fatturazione/{id}', [FatturazioneController::class, 'admin_update'])->name('admin.fatturazione.update')->whereNumber('id');
124 124
     Route::post('fatturazione/{id}/emetti', [FatturazioneController::class, 'admin_emetti'])->name('admin.fatturazione.emetti')->whereNumber('id');
125
+    Route::post('fatturazione/{id}/fic', [FatturazioneController::class, 'admin_invia_fic'])->name('admin.fatturazione.fic')->whereNumber('id');
125 126
     Route::post('fatturazione/{id}/annulla', [FatturazioneController::class, 'admin_annulla'])->name('admin.fatturazione.annulla')->whereNumber('id');
126 127
     Route::delete('fatturazione/{id}', [FatturazioneController::class, 'admin_destroy'])->name('admin.fatturazione.destroy')->whereNumber('id');
127 128
 

Loading…
Cancel
Save