Explorar el Código

Merge branch 'fabio_develop' into fatture-xml

FabioFratini hace 10 meses
padre
commit
8aad65bc0c

+ 332 - 0
app/Http/Livewire/Azienda.php

@@ -0,0 +1,332 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+use Livewire\WithFileUploads;
+use Illuminate\Support\Str;
+
+use App\Models\Azienda as AziendaModel;
+
+class Azienda extends Component
+{
+    use WithFileUploads;
+
+    public $update = false;
+    public $azienda;
+    public $ragione_sociale;
+    public $nome_associazione;
+    public $tipologia;
+    public $discipline;
+    public $logo;
+    public $temp_logo;
+    public $sede_legale_nazione;
+    public $sede_legale_provincia;
+    public $sede_legale_comune;
+    public $sede_legale_indirizzo;
+    public $sede_legale_cap;
+    public $same_address = false;
+    public $sede_operativa_nazione;
+    public $sede_operativa_provincia;
+    public $sede_operativa_comune;
+    public $sede_operativa_indirizzo;
+    public $sede_operativa_cap;
+    public $email;
+    public $pec;
+    public $telefono;
+    public $cellulare;
+    public $partita_iva;
+    public $codice_fiscale;
+    public $codice_sdi;
+    public $chiusura_anno_fiscale;
+    public $scadenza_abbonamenti;
+    public $scadenza_pagamenti_uscita;
+
+    public $search = '';
+    public $selectedDisciplines = [];
+    public $disciplineId = '';
+    public $disciplines = [];
+    public $activeTab = 'generale';
+
+    protected $rules = [
+        'ragione_sociale' => 'required|string|max:255',
+        'email' => 'required|email|max:255',
+        'pec' => 'required|email|max:255',
+        'cellulare' => 'required|string|max:20',
+    ];
+
+    public function resetFields()
+    {
+        $this->ragione_sociale = null;
+        $this->nome_associazione = null;
+        $this->tipologia = null;
+        $this->discipline = null;
+        $this->temp_logo = null;
+
+        $this->sede_legale_nazione = null;
+        $this->sede_legale_provincia = null;
+        $this->sede_legale_comune = null;
+        $this->sede_legale_indirizzo = null;
+        $this->sede_legale_cap = null;
+
+        $this->same_address = false;
+        $this->sede_operativa_nazione = null;
+        $this->sede_operativa_provincia = null;
+        $this->sede_operativa_comune = null;
+        $this->sede_operativa_indirizzo = null;
+        $this->sede_operativa_cap = null;
+
+        $this->email = null;
+        $this->pec = null;
+        $this->telefono = null;
+        $this->cellulare = null;
+
+        $this->partita_iva = null;
+        $this->codice_fiscale = null;
+        $this->codice_sdi = null;
+
+        $this->chiusura_anno_fiscale = null;
+        $this->scadenza_abbonamenti = null;
+        $this->scadenza_pagamenti_uscita = null;
+    }
+
+    public function mount()
+    {
+        $this->azienda = AziendaModel::first();
+
+        if ($this->azienda) {
+            $this->ragione_sociale = $this->azienda->ragione_sociale;
+            $this->nome_associazione = $this->azienda->nome_associazione;
+            $this->tipologia = $this->azienda->tipologia;
+            $this->discipline = $this->azienda->discipline;
+            $this->logo = $this->azienda->logo;
+
+            $this->sede_legale_nazione = $this->azienda->sede_legale_nazione;
+            $this->sede_legale_provincia = $this->azienda->sede_legale_provincia;
+            $this->sede_legale_comune = $this->azienda->sede_legale_comune;
+            $this->sede_legale_indirizzo = $this->azienda->sede_legale_indirizzo;
+            $this->sede_legale_cap = $this->azienda->sede_legale_cap;
+
+            $this->sede_operativa_nazione = $this->azienda->sede_operativa_nazione;
+            $this->sede_operativa_provincia = $this->azienda->sede_operativa_provincia;
+            $this->sede_operativa_comune = $this->azienda->sede_operativa_comune;
+            $this->sede_operativa_indirizzo = $this->azienda->sede_operativa_indirizzo;
+            $this->sede_operativa_cap = $this->azienda->sede_operativa_cap;
+
+            $this->email = $this->azienda->email;
+            $this->pec = $this->azienda->pec;
+            $this->telefono = $this->azienda->telefono;
+            $this->cellulare = $this->azienda->cellulare;
+
+            $this->partita_iva = $this->azienda->partita_iva;
+            $this->codice_fiscale = $this->azienda->codice_fiscale;
+            $this->codice_sdi = $this->azienda->codice_sdi;
+
+            $this->chiusura_anno_fiscale = $this->azienda->chiusura_anno_fiscale;
+            $this->scadenza_abbonamenti = $this->azienda->scadenza_abbonamenti;
+            $this->scadenza_pagamenti_uscita = $this->azienda->scadenza_pagamenti_uscita;
+
+            if (
+                $this->sede_legale_nazione == $this->sede_operativa_nazione &&
+                $this->sede_legale_provincia == $this->sede_operativa_provincia &&
+                $this->sede_legale_comune == $this->sede_operativa_comune &&
+                $this->sede_legale_indirizzo == $this->sede_operativa_indirizzo &&
+                $this->sede_legale_cap == $this->sede_operativa_cap
+            ) {
+                $this->same_address = true;
+            }
+
+            if ($this->azienda && $this->azienda->disciplines) {
+                $this->selectedDisciplines = array_map('trim', explode(';', $this->azienda->disciplines));
+            }
+        } else {
+            $this->update = true;
+            $this->resetFields();
+        }
+        $this->loadDisciplines();
+    }
+
+    public function render()
+    {
+        return view('livewire.azienda');
+    }
+
+    public function updatedSameAddress()
+    {
+        if ($this->same_address) {
+            $this->sede_operativa_nazione = $this->sede_legale_nazione;
+            $this->sede_operativa_provincia = $this->sede_legale_provincia;
+            $this->sede_operativa_comune = $this->sede_legale_comune;
+            $this->sede_operativa_indirizzo = $this->sede_legale_indirizzo;
+            $this->sede_operativa_cap = $this->sede_legale_cap;
+        } else {
+            $this->sede_operativa_nazione = null;
+            $this->sede_operativa_provincia = null;
+            $this->sede_operativa_comune = null;
+            $this->sede_operativa_indirizzo = null;
+            $this->sede_operativa_cap = null;
+        }
+    }
+
+    public function updatedSedeOperativaNazione()
+    {
+        $this->checkAddressDifference();
+    }
+
+    public function updatedSedeOperativaProvincia()
+    {
+        $this->checkAddressDifference();
+    }
+
+    public function updatedSedeOperativaComune()
+    {
+        $this->checkAddressDifference();
+    }
+
+    public function updatedSedeOperativaIndirizzo()
+    {
+        $this->checkAddressDifference();
+    }
+
+    public function updatedSedeOperativaCap()
+    {
+        $this->checkAddressDifference();
+    }
+
+    public function checkAddressDifference()
+    {
+        if (
+            $this->sede_legale_nazione == $this->sede_operativa_nazione &&
+            $this->sede_legale_provincia == $this->sede_operativa_provincia &&
+            $this->sede_legale_comune == $this->sede_operativa_comune &&
+            $this->sede_legale_indirizzo == $this->sede_operativa_indirizzo &&
+            $this->sede_legale_cap == $this->sede_operativa_cap
+        ) {
+            $this->same_address = true;
+        } else {
+            $this->same_address = false;
+        }
+    }
+
+    public function setTab($tab)
+    {
+        $this->activeTab = $tab;
+    }
+
+    public function edit()
+    {
+        $this->update = true;
+    }
+
+    public function save()
+    {
+        $this->validate();
+
+        try {
+            $data = [
+                'ragione_sociale' => $this->ragione_sociale,
+                'nome_associazione' => $this->nome_associazione,
+                'tipologia' => $this->tipologia,
+                'disciplines' => implode('; ', $this->selectedDisciplines),
+
+                'sede_legale_nazione' => $this->sede_legale_nazione,
+                'sede_legale_provincia' => $this->sede_legale_provincia,
+                'sede_legale_comune' => $this->sede_legale_comune,
+                'sede_legale_indirizzo' => $this->sede_legale_indirizzo,
+                'sede_legale_cap' => $this->sede_legale_cap,
+
+                'sede_operativa_nazione' => $this->sede_operativa_nazione,
+                'sede_operativa_provincia' => $this->sede_operativa_provincia,
+                'sede_operativa_comune' => $this->sede_operativa_comune,
+                'sede_operativa_indirizzo' => $this->sede_operativa_indirizzo,
+                'sede_operativa_cap' => $this->sede_operativa_cap,
+
+                'email' => $this->email,
+                'pec' => $this->pec,
+                'telefono' => $this->telefono,
+                'cellulare' => $this->cellulare,
+
+                'partita_iva' => $this->partita_iva,
+                'codice_fiscale' => $this->codice_fiscale,
+                'codice_sdi' => $this->codice_sdi,
+
+                'chiusura_anno_fiscale' => $this->chiusura_anno_fiscale,
+                'scadenza_abbonamenti' => $this->scadenza_abbonamenti,
+                'scadenza_pagamenti_uscita' => $this->scadenza_pagamenti_uscita,
+            ];
+
+            if ($this->temp_logo) {
+                $folderName = Str::slug($this->nome_associazione);
+
+                $path = 'img/' . $folderName;
+                $fullPath = storage_path('app/public/' . $path);
+
+                if (!file_exists($fullPath)) {
+                    mkdir($fullPath, 0755, true);
+                }
+
+                $logoPath = $this->temp_logo->store($path, 'public');
+                $data['logo'] = $logoPath;
+            }
+            if ($this->azienda) {
+                $this->azienda->update($data);
+                session()->flash('message', 'Dati aziendali aggiornati con successo!');
+            } else {
+                $this->azienda = AziendaModel::create($data);
+                session()->flash('message', 'Dati aziendali creati con successo!');
+            }
+
+            $this->update = false;
+        } catch (\Exception $ex) {
+            session()->flash('error', 'Errore: ' . $ex->getMessage());
+        }
+    }
+
+    public function getSelectedDisciplineNamesProperty()
+    {
+        if (empty($this->selectedDisciplines)) {
+            return [];
+        }
+
+        return \App\Models\Discipline::whereIn('id', $this->selectedDisciplines)
+            ->pluck('name')
+            ->toArray();
+    }
+
+    public function cancel()
+    {
+        $this->resetFields();
+        $this->mount();
+        $this->update = false;
+    }
+
+    public function loadDisciplines()
+    {
+        $this->disciplines = \App\Models\Discipline::select('id', 'name')->get();
+    }
+
+    public function addDiscipline()
+    {
+        if (!empty($this->disciplineId)) {
+            $discipline = \App\Models\Discipline::find($this->disciplineId);
+
+            if ($discipline && !in_array($discipline->name, $this->selectedDisciplines)) {
+                $this->selectedDisciplines[] = $discipline->name;
+                $this->disciplineId = '';
+            }
+        }
+    }
+
+    public function removeDiscipline($index)
+    {
+        if (isset($this->selectedDisciplines[$index])) {
+            unset($this->selectedDisciplines[$index]);
+            $this->selectedDisciplines = array_values($this->selectedDisciplines);
+        }
+    }
+
+    public function updatedSearch()
+    {
+        $this->loadDisciplines();
+    }
+}

+ 2 - 2
app/Http/Livewire/Member.php

@@ -2006,10 +2006,10 @@ class Member extends Component
                                     $payed += $row->amount / $tot;
                                 }
                             }
-                            if ($payed < $price)
-                                $class = 'orange half';
                         }
                     }
+                    if ($payed < $price)
+                        $class = 'orange half';
                     //$class .= $extraC;
                 }
 

+ 13 - 1
app/Http/Livewire/Receipt.php

@@ -6,6 +6,7 @@ use Livewire\Component;
 use Barryvdh\DomPDF\Facade\Pdf;
 use Illuminate\Support\Facades\Log;
 use SimpleXMLElement;
+use App\Models\Member;
 
 
 class Receipt extends Component
@@ -15,6 +16,14 @@ class Receipt extends Component
     public $filterStatus = '';
     public $hasFilter = false;
     public $filterFrom = '', $filterTo = '';
+    public $filteredMemberId = '';
+    public $members = [];
+
+    public function mount()
+    {
+        // Load members for the dropdown
+        $this->members = Member::orderBy('last_name')->orderBy('first_name')->get();
+    }
 
     public function updatedFilterFrom()
     {
@@ -38,6 +47,8 @@ class Receipt extends Component
                 $r = $r->where('date', '>=', $this->filterFrom);
             if ($this->filterTo != '')
                 $r = $r->where('date', '<=', $this->filterTo);
+            if ($this->filteredMemberId != '')
+                $r = $r->where('member_id', $this->filteredMemberId);
 
             $this->records = $r->get();
         }
@@ -75,7 +86,7 @@ class Receipt extends Component
             // Extract data from XML and create receipts
             foreach ($xml->receipt as $receiptData) {
                 Log::info('Receipt Data: ' . print_r($receiptData, true));
-                $member = \App\Models\Member::where('fiscal_code', (string)$receiptData->fiscal_code)->first();
+                $member = Member::where('fiscal_code', (string)$receiptData->fiscal_code)->first();
 
                 if ($member) {
                     $receipt = new \App\Models\Receipt();
@@ -107,6 +118,7 @@ class Receipt extends Component
         $this->filterStatus = "";
         $this->filterTo = '';
         $this->filterFrom = '';
+        $this->filteredMemberId = '';
         $this->hasFilter = false;
     }
 

+ 19 - 5
app/Http/Livewire/RecordIN.php

@@ -13,7 +13,7 @@ class RecordIN extends Component
     use WithPagination;
     protected $paginationTheme = 'bootstrap';
 
-    protected $listeners = ['setCausal' => 'setCausal'];
+    protected $listeners = ['setCausal' => 'setCausal','refreshMembers' => 'refreshMembers'];
 
     public $sortField ='date';
     public $sortAsc = false;
@@ -167,6 +167,11 @@ class RecordIN extends Component
         $this->canSave = $this->checkCanSave();
     }
 
+    public function updatedCommercial($value)
+    {
+        $this->emitSelf('refreshMembers');
+    }
+
     public function checkCanSave()
     {
         $ret = true;
@@ -187,6 +192,15 @@ class RecordIN extends Component
         return $ret;
     }
 
+    public function refreshMembers()
+    {
+        if($this->commercial){
+            $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->orderBy('last_name')->orderBy('first_name')->get();
+        } else {
+            $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->where('current_status', 2)->orderBy('last_name')->orderBy('first_name')->get();
+        }
+    }
+
     public function setAmount()
     {
         $tot = 0;
@@ -315,7 +329,7 @@ class RecordIN extends Component
 
         //$this->buildTree(\App\Models\Causal::all(), null);
 
-        $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->orderBy('last_name')->orderBy('first_name')->get();
+        $this->refreshMembers();
         $this->payments = \App\Models\PaymentMethod::select('id', 'name')->where('enabled', true)->whereIn('type', array('ALL', 'IN'))->orderBy('name')->get();
         $this->vats = \App\Models\Vat::select('id', 'name', 'value')->orderBy('value')->get();
 
@@ -388,7 +402,7 @@ class RecordIN extends Component
 
             }
 
-            
+
             if (isset($_GET["createSubscription"]) && $_GET["createSubscription"] == 1)
             {
 
@@ -407,7 +421,7 @@ class RecordIN extends Component
                     $this->rows[$count]["causal_id"] = $_GET["subCausalId"];
                 }
             }
-            
+
         }
         $this->first = false;
 
@@ -795,7 +809,7 @@ class RecordIN extends Component
     public function rigenerate()
     {
         $this->emit('refresh');
-        
+
         try {
 
             $record = \App\Models\Record::findOrFail($this->dataId);

+ 26 - 19
app/Http/Livewire/RecordINOUT.php

@@ -137,24 +137,24 @@ class RecordINOUT extends Component
             $aChilds[] = $first_parent_id;
         }
 
-        $childs = \App\Models\Causal::where('parent_id', $id)->get();            
+        $childs = \App\Models\Causal::where('parent_id', $id)->get();
         foreach($childs as $child)
-        {            
+        {
             $aChilds[] = $child->id;
-            $childs2 = \App\Models\Causal::where('parent_id', $child->id)->get();            
+            $childs2 = \App\Models\Causal::where('parent_id', $child->id)->get();
             foreach($childs2 as $child2)
             {
                 $aChilds[] = $child2->id;
-                $childs3 = \App\Models\Causal::where('parent_id', $child2->id)->get();            
+                $childs3 = \App\Models\Causal::where('parent_id', $child2->id)->get();
                 foreach($childs3 as $child3)
                 {
                     $aChilds[] = $child3->id;
-                    
-                }   
+
+                }
             }
         }
         return $aChilds;
-        
+
     }
 
 
@@ -422,13 +422,13 @@ class RecordINOUT extends Component
 
     public function remove($idx)
     {
-        
+
         if (sizeof($this->datas) > 1)
             array_splice($this->datas, $idx, 1);
             //unset($this->datas[$idx]);
         else
             $this->datas = array();
-        
+
         $this->show('', '');
     }
 
@@ -452,7 +452,7 @@ class RecordINOUT extends Component
             $rows_in = $this->rows_in;
         }
 
-        $path = $this->generateExcel($this->columns, $rows_in, $this->records_in, $this->rows_out, $this->records_out);
+        $path = $this->generateExcel($this->columns, $rows_in, $this->records_in, $this->rows_out, $this->records_out,false);
 
         return response()->download($path)->deleteFileAfterSend();
 
@@ -553,12 +553,12 @@ class RecordINOUT extends Component
 
         }
 
-        $path = $this->generateExcel($columns, $this->rows_in, $records_in, $this->rows_out, $records_out);
+        $path = $this->generateExcel($columns, $this->rows_in, $records_in, $this->rows_out, $records_out, true);
         return response()->download($path)->deleteFileAfterSend();
 
     }
 
-    public function generateExcel($columns, $rows_in, $records_in, $rows_out, $records_out)
+    public function generateExcel($columns, $rows_in, $records_in, $rows_out, $records_out, $isYearExport)
     {
         $letters = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N');
 
@@ -572,7 +572,7 @@ class RecordINOUT extends Component
         }
 
         $activeWorksheet->getStyle('A1:N1')->getFont()->setBold(true);
-        $activeWorksheet->getStyle('A1:N1')->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('00ff00');
+        $activeWorksheet->getStyle('A1:N1')->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('C6E0B4'); // Lighter green
 
         $count = 2;
 
@@ -612,10 +612,11 @@ class RecordINOUT extends Component
         {
             $activeWorksheet->setCellValue($letters[$idx + 1] . $count, formatPrice($total));
             $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFont()->setBold(true);
-            $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('00ff00');
+            $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('C6E0B4'); // Lighter green
         }
 
 
+
         $count += 2;
         $activeWorksheet->setCellValue('A' . $count, "Uscite");
         foreach($columns as $idx => $column)
@@ -624,7 +625,7 @@ class RecordINOUT extends Component
         }
 
         $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFont()->setBold(true);
-        $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('ff0000');
+        $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('F8CBAD'); // Lighter red
 
         $count += 1;
 
@@ -664,14 +665,20 @@ class RecordINOUT extends Component
         {
             $activeWorksheet->setCellValue($letters[$idx + 1] . $count, formatPrice($total));
             $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFont()->setBold(true);
-            $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('ff0000');
+            $activeWorksheet->getStyle('A' . $count . ':N' . $count)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('F8CBAD'); // Lighter red
         }
 
-        foreach($letters as $l)
-            $activeWorksheet->getColumnDimension($l)->setWidth(20);
+        $activeWorksheet->getColumnDimension('A')->setWidth(35);
+
+        for($i = 1; $i < count($letters); $i++) {
+            $activeWorksheet->getColumnDimension($letters[$i])->setWidth(20);
+        }
+
+
+        $fileSuffix = $isYearExport ? 'AnnoFiscale' : 'Selezione';
 
         $writer = new Xlsx($spreadsheet);
-        $writer->save($path = storage_path('entrate_uscite_' . date("YmdHis") . '.xlsx'));
+        $writer->save($path = storage_path(date("Ymd") .'_Gestionale_' . $fileSuffix .  '.xlsx'));
 
         return $path;
 

+ 444 - 0
app/Http/Livewire/Reports.php

@@ -0,0 +1,444 @@
+<?php
+
+namespace App\Http\Livewire;
+
+use Livewire\Component;
+use Illuminate\Support\Facades\Auth;
+use Carbon\Carbon;
+use App\Models\Receipt;
+use App\Models\ReceiptRow;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use App\Models\Course;
+use App\Models\MemberCard;
+
+class Reports extends Component
+{
+    public $type = 'anagrafica';
+    public $yearFilter;
+
+    public $courses = [];
+    public $selectedCourse = null;
+    protected $listeners = ['refreshData' => '$refresh'];
+
+    public function mount()
+    {
+        if (Auth::user()->level != env('LEVEL_ADMIN', 0))
+            return redirect()->to('/reports');
+
+        if (isset($_GET["type"]))
+            $this->type = $_GET["type"];
+
+        $this->yearFilter = Carbon::now()->year;
+        $this->courses = $this->getCoursesForSelect();
+        $this->emit('dataUpdated');
+    }
+
+    public function render()
+    {
+        return view('livewire.reports');
+    }
+    public function updateCourseChart()
+    {
+        $this->emit('courseDataUpdated');
+    }
+
+    public function updatedSelectedCourse($value)
+    {
+        Log::info('Selected course changed to: ' . $value);
+        $this->emit('courseDataUpdated', $value);
+    }
+
+    public function getTesseratiData()
+    {
+        $endYear = $this->yearFilter;
+        return self::getMemberCountChartData($endYear);
+    }
+
+    public function change($type)
+    {
+        $this->type = $type;
+    }
+
+    public function setYearFilter($year)
+    {
+        $this->yearFilter = $year;
+        $this->emit('dataUpdated');
+    }
+
+    public function getMonthlyTotals()
+    {
+        $year = $this->yearFilter;
+
+        $months = range(1, 12);
+        $monthNames = [];
+        $incomeData = array_fill(0, 12, 0);
+        $expenseData = array_fill(0, 12, 0);
+
+        foreach ($months as $month) {
+            $date = Carbon::createFromDate($year, $month, 1);
+            $monthNames[] = ucfirst($date->locale('it')->monthName);
+        }
+
+        $incomeReceipts = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'IN')
+            ->select(DB::raw('MONTH(receipts.date) as month_num'), DB::raw('SUM(receipts_rows.amount) as total'))
+            ->groupBy('month_num')
+            ->get();
+
+        $expenseReceipts = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'OUT')
+            ->select(DB::raw('MONTH(receipts.date) as month_num'), DB::raw('SUM(receipts_rows.amount) as total'))
+            ->groupBy('month_num')
+            ->get();
+
+        foreach ($incomeReceipts as $receipt) {
+            $index = $receipt->month_num - 1;
+            if (isset($incomeData[$index])) {
+                $incomeData[$index] = $receipt->total;
+            }
+        }
+
+        foreach ($expenseReceipts as $receipt) {
+            $index = $receipt->month_num - 1;
+            if (isset($expenseData[$index])) {
+                $expenseData[$index] = $receipt->total;
+            }
+        }
+
+
+        return [
+            'labels' => $monthNames,
+            'datasets' => [
+                [
+                    'label' => 'Entrate',
+                    'data' => $incomeData,
+                    'backgroundColor' => 'rgba(54, 162, 235, 0.5)'
+                ],
+                [
+                    'label' => 'Uscite',
+                    'data' => $expenseData,
+                    'backgroundColor' => 'rgba(255, 99, 132, 0.5)'
+                ],
+            ]
+        ];
+    }
+
+    public function getYearlySummary()
+    {
+        $year = $this->yearFilter;
+
+        $totalIncome = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'IN')
+            ->sum('receipts_rows.amount');
+
+        $totalExpenses = DB::table('receipts')
+            ->join('receipts_rows', 'receipts.id', '=', 'receipts_rows.receip_id')
+            ->where('receipts.year', $year)
+            ->where('receipts.type', 'OUT')
+            ->sum('receipts_rows.amount');
+
+        $delta = $totalIncome - $totalExpenses;
+
+        return [
+            'totalIncome' => $totalIncome,
+            'totalExpenses' => $totalExpenses,
+            'delta' => $delta
+        ];
+    }
+
+    public function getTopCausalsByAmount($limit = 10)
+    {
+        $year = $this->yearFilter;
+
+        $query = DB::table('receipts_rows')
+            ->join('receipts', 'receipts_rows.receip_id', '=', 'receipts.id')
+            ->join('causals', 'receipts_rows.causal_id', '=', 'causals.id')
+            ->where('receipts.year', $year);
+
+        $query->where('receipts.type', 'IN');
+
+        Log::info('Query: ' . $query->toSql());
+
+        $causals = $query->select(
+            'causals.id',
+            'causals.name',
+            'causals.parent_id',
+            DB::raw('SUM(receipts_rows.amount) as total_amount')
+        )
+            ->groupBy('causals.id', 'causals.name', 'causals.parent_id')
+            ->orderBy('total_amount', 'desc')
+            ->limit($limit)
+            ->get();
+
+        Log::info('Causals: ' . json_encode($causals));
+
+        $inData = [];
+
+        foreach ($causals as $causal) {
+            $tempCausal = new \App\Models\Causal();
+            $tempCausal->id = $causal->id;
+            $tempCausal->name = $causal->name;
+            $tempCausal->parent_id = $causal->parent_id;
+
+            $treeName = $tempCausal->getTree();
+
+            $displayName = strlen($treeName) > 30 ? substr($treeName, 0, 27) . '...' : $treeName;
+
+            $inData[] = [
+                'label' => $displayName,
+                'value' => $causal->total_amount,
+                'fullName' => $treeName
+            ];
+        }
+
+        usort($inData, function ($a, $b) {
+            return $b['value'] <=> $a['value'];
+        });
+
+        $inData = array_slice($inData, 0, $limit);
+
+        return [
+            'inLabels' => array_column($inData, 'label'),
+            'inData' => $inData,
+            'datasets' => [
+                [
+                    'label' => 'Entrate per Causale',
+                    'data' => array_column($inData, 'value'),
+                ]
+            ]
+        ];
+    }
+
+    public function getCoursesForSelect()
+    {
+        $currentYear = date('Y');
+
+        $courses = Course::with(['level', 'type', 'frequency'])
+            ->where('active', true)
+            ->where('year', 'like', '%' . $currentYear . '%')
+            ->orderBy('name')
+            ->get()
+            ->map(function ($course) {
+                if (!empty($course->course_type_id)) {
+                    $type = \App\Models\CourseType::find($course->course_type_id);
+                    if ($type) {
+                        $typeName = $type->name;
+                    }
+                }
+
+                $levelName = is_object($course->level) ? $course->level->name : 'No Level';
+                $typeName = is_object($type) ? $type->name : 'No Type';
+                $frequencyName = is_object($course->frequency) ? $course->frequency->name : 'No Frequency';
+                $year = $course->year ?? '';
+
+                return [
+                    'id' => $course->id,
+                    'name' => $course->name,
+                    'full_name' => "{$course->name} - {$levelName} - {$typeName} - {$frequencyName} ({$year})",
+                    'level_name' => $levelName,
+                    'type_name' => $typeName,
+                    'frequency_name' => $frequencyName,
+                    'year' => $year
+                ];
+            })->toArray();
+
+        return $courses;
+    }
+
+
+    public function getCourseMonthlyEarnings()
+    {
+        $courseId = $this->selectedCourse;
+        Log::info('Getting earnings for course ID: ' . $courseId);
+
+        if (empty($courseId)) {
+
+            return [
+                'labels' => ['Set', 'Ott', 'Nov', 'Dic', 'Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago'],
+                'datasets' => [
+                    [
+                        'label' => 'Pagamenti Effettuati',
+                        'backgroundColor' => 'rgba(0, 184, 148, 1)',
+                        'data' => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+                        'type' => 'bar',
+                        'order' => 3
+                    ],
+                    [
+                        'label' => 'Pagamenti Totali',
+                        'backgroundColor' => 'transparent',
+                        'borderColor' => 'rgba(48, 51, 107, 1)',
+                        'borderWidth' => 3,
+                        'pointBackgroundColor' => 'rgba(48, 51, 107, 1)',
+                        'pointRadius' => 5,
+                        'data' => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+                        'type' => 'line',
+                        'tension' => 0.2,
+                        'order' => 2
+                    ]
+                ]
+            ];
+        }
+
+        $monthOrder = [9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8];
+
+        $monthlyData = [];
+        foreach ($monthOrder as $i) {
+            $monthlyData[$i] = [
+                'earned' => 0,
+                'total' => 0,
+                'participants' => 0
+            ];
+        }
+
+        $memberCourses = \App\Models\MemberCourse::where('course_id', $courseId)
+            ->with('member')
+            ->get();
+
+        foreach ($memberCourses as $memberCourse) {
+            $price = (float)($memberCourse->price ?? 0);
+
+            if ($memberCourse->months) {
+                $monthsData = json_decode($memberCourse->months, true);
+
+                if (is_array($monthsData)) {
+                    foreach ($monthsData as $monthData) {
+                        $month = $monthData['m'] ?? null;
+                        $status = $monthData['status'] ?? '';
+
+                        if ($month !== null && isset($monthlyData[$month])) {
+                            $monthlyData[$month]['total'] += $price;
+
+                            if ($status === 1) {
+                                $monthlyData[$month]['earned'] += $price;
+                            }
+
+                            $monthlyData[$month]['participants']++;
+                        }
+                    }
+                }
+            }
+        }
+
+        $monthNames = [
+            9 => 'Set',
+            10 => 'Ott',
+            11 => 'Nov',
+            12 => 'Dic',
+            1 => 'Gen',
+            2 => 'Feb',
+            3 => 'Mar',
+            4 => 'Apr',
+            5 => 'Mag',
+            6 => 'Giu',
+            7 => 'Lug',
+            8 => 'Ago',
+        ];
+
+        $labels = [];
+        $earnedData = [];
+        $totalData = [];
+        $participantData = [];
+        $missingData = [];
+
+        foreach ($monthOrder as $month) {
+            $labels[] = $monthNames[$month];
+            $earnedData[] = round($monthlyData[$month]['earned'], 2);
+            $totalData[] = round($monthlyData[$month]['total'], 2);
+            $participantData[] = $monthlyData[$month]['participants'];
+            $missingData[] = round($monthlyData[$month]['total'] - $monthlyData[$month]['earned'], 2);
+        }
+
+        return [
+            'labels' => $labels,
+            'datasets' => [
+                [
+                    'label' => 'Pagamenti Effettuati',
+                    'backgroundColor' => 'rgba(0, 184, 148, 1)',
+                    'data' => $earnedData,
+                    'type' => 'bar',
+                    'order' => 3
+                ],
+                [
+                    'label' => 'Pagamenti Attesi',
+                    'backgroundColor' => 'transparent',
+                    'borderColor' => 'rgba(48, 51, 107, 1)',
+                    'borderWidth' => 3,
+                    'pointBackgroundColor' => 'rgba(48, 51, 107, 1)',
+                    'pointRadius' => 5,
+                    'data' => $totalData,
+                    'type' => 'line',
+                    'tension' => 0.2,
+                    'order' => 2,
+                    'participants' => $participantData,
+                    'missing' => $missingData
+                ]
+            ]
+        ];
+    }
+
+    public static function getMemberCountChartData($endYear = null, $span = 5)
+    {
+        if ($endYear === null) {
+            $endYear = date('Y');
+        }
+
+        $startYear = $endYear - $span + 1;
+
+        $memberCards = MemberCard::select('member_id', 'expire_date')
+            ->whereNotNull('expire_date')
+            ->whereNotNull('member_id')
+            ->where('status', '!=', 'cancelled')
+            ->whereRaw('YEAR(expire_date) >= ?', [$startYear])
+            ->whereRaw('YEAR(expire_date) <= ?', [$endYear])
+            ->get();
+
+        $yearCounts = [];
+        for ($year = $startYear; $year <= $endYear; $year++) {
+            $yearPeriod = ($year - 1) . '-' . $year;
+            $yearCounts[$yearPeriod] = [];
+        }
+
+        foreach ($memberCards as $card) {
+            $expireYear = date('Y', strtotime($card->expire_date));
+
+            $previousYear = $expireYear - 1;
+            $yearPeriod = $previousYear . '-' . $expireYear;
+
+            if (isset($yearCounts[$yearPeriod])) {
+                $yearCounts[$yearPeriod][$card->member_id] = true;
+            }
+        }
+
+        $yearLabels = [];
+        $memberCountData = [];
+
+        foreach ($yearCounts as $yearPeriod => $members) {
+            $yearLabels[] = $yearPeriod;
+            $memberCountData[] = count($members);
+        }
+
+        return [
+            'labels' => $yearLabels,
+            'datasets' => [
+                [
+                    'label' => 'Membri Tesserati',
+                    'data' => $memberCountData,
+                    'backgroundColor' => 'rgba(54, 162, 235, 0.2)',
+                    'borderColor' => 'rgba(54, 162, 235, 1)',
+                    'borderWidth' => 2,
+                    'pointBackgroundColor' => 'rgba(54, 162, 235, 1)',
+                    'pointRadius' => 4,
+                    'tension' => 0.3,
+                    'fill' => true
+                ]
+            ]
+        ];
+    }
+}

+ 74 - 0
app/Models/Azienda.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Azienda extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        'ragione_sociale',
+        'nome_associazione',
+        'tipologia',
+        'logo',
+
+        'sede_legale_nazione',
+        'sede_legale_provincia',
+        'sede_legale_comune',
+        'sede_legale_indirizzo',
+        'sede_legale_cap',
+
+        'sede_operativa_nazione',
+        'sede_operativa_provincia',
+        'sede_operativa_comune',
+        'sede_operativa_indirizzo',
+        'sede_operativa_cap',
+
+        'email',
+        'pec',
+        'telefono',
+        'cellulare',
+
+        'partita_iva',
+        'codice_fiscale',
+        'codice_sdi',
+
+        'chiusura_anno_fiscale',
+        'scadenza_abbonamenti',
+        'scadenza_pagamenti_uscita',
+    ];
+
+    protected $casts = [
+        'chiusura_anno_fiscale' => 'date',
+        'scadenza_abbonamenti' => 'date',
+        'scadenza_pagamenti_uscita' => 'date',
+    ];
+
+
+    /**
+     * Get the logo URL attribute.
+     *
+     * @return string|null
+     */
+    public function getLogoUrlAttribute()
+    {
+        if ($this->logo) {
+            return asset('storage/' . $this->logo);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get a formatted list of discipline names.
+     *
+     * @return string
+     */
+    public function getDisciplineListAttribute()
+    {
+        return $this->disciplines->pluck('name')->implode(', ');
+    }
+}

+ 12 - 0
app/Models/Discipline.php

@@ -12,8 +12,20 @@ class Discipline extends Model
     protected $fillable = [
         'name',
         'enabled',
+        'code',
+        'sport'
     ];
 
+    public function scopeOfSport($query, $sport)
+    {
+        return $query->where('sport', $sport);
+    }
+
+    public function getFullNameAttribute()
+    {
+        return "{$this->sport} - {$this->name} ({$this->code})";
+    }
+
     public function canDelete()
     {
         return true;

+ 0 - 36
database/migrations/2025_03_07_173000_ass_fields_to_create_course_table.php

@@ -1,36 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\Schema;
-
-return new class extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::table('courses', function (Blueprint $table) {
-            $table->json('when')->nullable();
-            $table->json('prices')->nullable();
-            $table->enum('type', ['standard', 'custom']);
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::table('courses', function (Blueprint $table) {
-            $table->dropColumn('when');
-            $table->dropColumn('prices');
-            $table->dropColumn('type');
-        });
-    }
-};

+ 39 - 0
database/migrations/2025_04_04_101800_add_fields_discipline_table.php

@@ -0,0 +1,39 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('disciplines', function (Blueprint $table) {
+            if (!Schema::hasColumn('disciplines', 'code')) {
+                $table->string('code')->nullable()->after('id');
+
+            }
+
+            if (!Schema::hasColumn('disciplines', 'sport')) {
+                $table->string('sport')->nullable()->after('code');
+            }
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('disciplines', function (Blueprint $table) {
+            $table->dropColumn('code', 'sport');
+        });
+    }
+};

+ 68 - 0
database/migrations/2025_04_04_130150_create_aziendas_table.php

@@ -0,0 +1,68 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('aziendas', function (Blueprint $table) {
+            $table->id();
+
+            // General information
+            $table->string('ragione_sociale');
+            $table->string('nome_associazione')->nullable();
+            $table->string('tipologia')->nullable();
+            $table->string('logo')->nullable();
+
+            // Legal address
+            $table->string('sede_legale_nazione')->nullable();
+            $table->string('sede_legale_provincia')->nullable();
+            $table->string('sede_legale_comune')->nullable();
+            $table->string('sede_legale_indirizzo')->nullable();
+            $table->string('sede_legale_cap')->nullable();
+
+            // Operational address
+            $table->string('sede_operativa_nazione')->nullable();
+            $table->string('sede_operativa_provincia')->nullable();
+            $table->string('sede_operativa_comune')->nullable();
+            $table->string('sede_operativa_indirizzo')->nullable();
+            $table->string('sede_operativa_cap')->nullable();
+
+            // Contacts
+            $table->string('email');
+            $table->string('pec');
+            $table->string('telefono')->nullable();
+            $table->string('cellulare');
+
+            // Fiscal data
+            $table->string('partita_iva')->nullable();
+            $table->string('codice_fiscale')->nullable();
+            $table->string('codice_sdi')->nullable();
+
+            // Accounting configuration
+            $table->date('chiusura_anno_fiscale')->nullable();
+            $table->date('scadenza_abbonamenti')->nullable();
+            $table->date('scadenza_pagamenti_uscita')->nullable();
+
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('aziendas');
+    }
+};

+ 28 - 0
database/migrations/2025_04_04_135831_add_field_discipline_to_aziendas_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('aziendas', function (Blueprint $table) {
+            if (Schema::hasColumn('aziendas', 'discipline')) {
+                $table->dropColumn('discipline');
+            }
+
+            $table->text('discipline')->nullable();
+        });
+    }
+    public function down()
+    {
+
+    }
+};

+ 2 - 2
public/css/extra.css

@@ -3,7 +3,7 @@
     border-color: #FF0000 !important;
 }
 .numericCol{
-    text-align: right !important;
+    text-align: left !important;
 }
 .paddingLeftSelect0
 {
@@ -27,4 +27,4 @@ table.tableHead thead {
 ::-webkit-scrollbar-thumb {
     background: #e4e4e4 !important;
     border-radius: 10px;
-}
+}

+ 0 - 4
public/css/style.css

@@ -16277,10 +16277,6 @@ table.tablesaw tbody tr td .primary {
   text-align: center;
   padding-right: 30px;
 }
-.table--lista_ricevute tbody tr td:nth-child(7) {
-  text-align: right !important;
-  padding-right: 30px;
-}
 
 .tabella--corsi {
   min-width: unset !important;

+ 17 - 5
resources/views/layouts/app.blade.php

@@ -103,9 +103,7 @@
     {
         z-index:9999 !important;
     }
-    .numericCol{
-        text-align: right !important;
-    }
+
   </style>
 
     <link rel="stylesheet" href="/css/style.css?v={{date('YmdHis')}}">
@@ -313,7 +311,7 @@
                             <div class="accordion-body">
                                 <ul class="nav nav-pills flex-column align-items-center align-items-sm-start w-100" id="menu-contabilita" style="margin-top:0px;">
                                     <li class="nav-item" style="{{Request::is('course_member_one') || Request::is('course_member_two') || Request::is('course_member') ? 'background-color: #c5d9e6;' : ''}}">
-                                        <a href="/{{env('MANAGE_COURSE', false) ? 'course_member_one' : 'course_member'}}" class="nav-link d-flex align-items-center linkMenu">
+                                        <a href="/course_member_one" class="nav-link d-flex align-items-center linkMenu">
                                             <span class="ms-3 d-md-inline">Iscritti</span>
                                         </a>
                                     </li>
@@ -353,13 +351,22 @@
                             </div>
                         @endif
                     @endif
+                    @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+                        <div class="accordion-item " style="{{Request::is('reports') || Request::is('categories') || Request::is('disciplines') || Request::is('cards') || Request::is('course_subscriptions') || Request::is('courses') || Request::is('course_durations') || Request::is('course_frequencies') || Request::is('course_levels') || Request::is('course_types') || Request::is('banks') || Request::is('causals') || Request::is('vats') || Request::is('payment_methods') || Request::is('users') ? 'background-color: #c5d9e6;' : ''}}">
+                            <h2 class="accordion-header linkMenu">
+                                <a class="accordion-button collapsed" href="/reports">
+                                    Reports
+                                </a>
+                            </h2>
+                        </div>
+                    @endif
                 </div>
                 </div>
             </div>
         </div>
 
         <button id="open-filter" onclick="pcsh1()"></button>
-        
+
 
         <div class="col">
             <div class="row h-100">
@@ -377,6 +384,11 @@
         <li class="user--boxlist_item">
             <a href="/profile">Profilo</a>
         </li>
+        @if(Auth::user()->level == env('LEVEL_ADMIN', 0))
+            <li class="user--boxlist_item">
+                <a href="/azienda">Azienda</a>
+            </li>
+        @endif
         <li class="user--boxlist_item">
             <a href="/logout">Logout</a>
         </li>

+ 220 - 0
resources/views/livewire/azienda.blade.php

@@ -0,0 +1,220 @@
+<div>
+
+    <div class="row mt-4">
+        <div class="col-md-12">
+            <div class="card">
+                <div class="card-body">
+                    @if (session()->has('message'))
+                        <div class="alert alert-success" role="alert">
+                            {{ session()->get('message') }}
+                        </div>
+                    @endif
+
+                    @if (session()->has('error'))
+                        <div class="alert alert-danger" role="alert">
+                            {{ session()->get('error') }}
+                        </div>
+                    @endif
+
+                    <div class="tab-content">
+                        <form wire:submit.prevent="save">
+                            <div class="row mb-4">
+                                <div class="col-md-4">
+                                    <div class="logo-container mb-3">
+                                        @if($azienda && $azienda->logo_url)
+                                            <img src="{{ $azienda->logo_url }}" alt="Logo" class="img-thumbnail" style="max-width: 200px;">
+                                        @else
+                                            <div class="mb-3">
+                                                <label class="form-label">Logo</label>
+                                                <input type="file" class="form-control" wire:model="temp_logo">
+                                            </div>
+                                        @endif
+                                    </div>
+
+                                </div>
+                            </div>
+
+                            <div class="mb-4">
+
+                                <div class="row">
+                                    <div class="col-md-6 mb-3">
+                                        <label class="form-label">Ragione sociale*</label>
+                                        <input type="text" class="form-control @error('ragione_sociale') is-invalid @enderror"
+                                                wire:model="ragione_sociale">
+                                        @error('ragione_sociale') <span class="text-danger">{{ $message }}</span> @enderror
+                                    </div>
+                                    <div class="col-md-6 mb-3">
+                                        <label class="form-label">Nome associazione/società</label>
+                                        <input type="text" class="form-control" wire:model="nome_associazione">
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="col-md-6 mb-3">
+                                        <label class="form-label">Tipologia* (ASD/SSD/Polisportiva ecc.)</label>
+                                        <input type="text" class="form-control" wire:model="tipologia">
+                                    </div>
+                                    <div class="col-md-6  mb-3">
+                                        <label class="form-label">Discipline</label>
+                                        <div wire:ignore>
+                                            <select class="form-select discipline-select" multiple wire:model="selectedDisciplines">
+                                                @forelse($disciplines as $discipline)
+                                                    <option value="{{ $discipline->id }}">
+                                                        {{ $discipline->name }}
+                                                    </option>
+                                                @empty
+                                                    <option disabled>Nessuna disciplina trovata</option>
+                                                @endforelse
+                                            </select>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <h5 class="mb-3">Sede legale</h5>
+                            <div class="row">
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Nazione</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_nazione">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Provincia</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_provincia">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Comune</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_comune">
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-md-8 mb-3">
+                                    <label class="form-label">Indirizzo</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_indirizzo">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">CAP</label>
+                                    <input type="text" class="form-control" wire:model="sede_legale_cap">
+                                </div>
+                            </div>
+
+                            <div class="form-check mb-3">
+                                <input class="form-check-input" type="checkbox" wire:model="same_address" id="sameAddress">
+                                <label class="form-check-label" for="sameAddress">
+                                    Sede operativa uguale a sede legale
+                                </label>
+                            </div>
+
+                            @if(!$same_address)
+                                <h5 class="mb-3">Sede operativa</h5>
+                                <div class="row">
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">Nazione</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_nazione">
+                                    </div>
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">Provincia</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_provincia">
+                                    </div>
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">Comune</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_comune">
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="col-md-8 mb-3">
+                                        <label class="form-label">Indirizzo</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_indirizzo">
+                                    </div>
+                                    <div class="col-md-4 mb-3">
+                                        <label class="form-label">CAP</label>
+                                        <input type="text" class="form-control" wire:model="sede_operativa_cap">
+                                    </div>
+                                </div>
+                            @endif
+
+                            <h5 class="mb-3">Contatti</h5>
+                            <div class="row">
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Email*</label>
+                                    <input type="email" class="form-control @error('email') is-invalid @enderror"
+                                            wire:model="email">
+                                    @error('email') <span class="text-danger">{{ $message }}</span> @enderror
+                                </div>
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Pec*</label>
+                                    <input type="email" class="form-control @error('pec') is-invalid @enderror"
+                                            wire:model="pec">
+                                    @error('pec') <span class="text-danger">{{ $message }}</span> @enderror
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Telefono</label>
+                                    <input type="text" class="form-control" wire:model="telefono">
+                                </div>
+                                <div class="col-md-6 mb-3">
+                                    <label class="form-label">Cellulare*</label>
+                                    <input type="text" class="form-control @error('cellulare') is-invalid @enderror"
+                                            wire:model="cellulare">
+                                    @error('cellulare') <span class="text-danger">{{ $message }}</span> @enderror
+                                </div>
+                            </div>
+
+                            <h5 class="mb-3">Dati fiscali</h5>
+                            <div class="row">
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Partita IVA</label>
+                                    <input type="text" class="form-control" wire:model="partita_iva">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Codice fiscale</label>
+                                    <input type="text" class="form-control" wire:model="codice_fiscale">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Codice SDI</label>
+                                    <input type="text" class="form-control" wire:model="codice_sdi">
+                                </div>
+                            </div>
+
+                            <h5 class="mb-3">Configurazione contabilità</h5>
+                            <div class="row">
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Chiusura anno fiscale</label>
+                                    <input type="date" class="form-control" wire:model="chiusura_anno_fiscale">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Scadenza abbonamenti</label>
+                                    <input type="date" class="form-control" wire:model="scadenza_abbonamenti">
+                                </div>
+                                <div class="col-md-4 mb-3">
+                                    <label class="form-label">Scadenza pagamenti in uscita</label>
+                                    <input type="date" class="form-control" wire:model="scadenza_pagamenti_uscita">
+                                </div>
+                            </div>
+
+                            <div class="mt-4">
+                                <button type="submit" class="btn btn-primary">Salva</button>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+@push('scripts')
+    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+    <script>
+        $(document).ready(function() {
+            $('.discipline-select').select2({
+                placeholder: 'Seleziona discipline',
+                allowClear: true
+            });
+
+            $('.discipline-select').on('change', function (e) {
+                var data = $(this).select2("val");
+                @this.set('selectedDisciplines', data);
+            });
+        });
+    </script>
+@endpush

+ 142 - 168
resources/views/livewire/course.blade.php

@@ -114,13 +114,11 @@
                     <form action="">
 
                         <div class="row mb-3 ">
-                            @if(env('MANAGE_COURSE', false))
-                                <div class="col-12 mt-2">
-                                    <input type="radio" name="chkType" value="standard" wire:model="type">&nbsp;Standard
-                                    &nbsp;&nbsp;&nbsp;
-                                    <input type="radio" name="chkType" value="custom" wire:model="type">&nbsp;Personalizzato
-                                </div>
-                            @endif
+                            <div class="col-12 mt-2">
+                                <input type="radio" name="chkType" value="standard" wire:model="type">&nbsp;Standard
+                                &nbsp;&nbsp;&nbsp;
+                                <input type="radio" name="chkType" value="custom" wire:model="type">&nbsp;Personalizzato
+                            </div>
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">Nome</label>
@@ -130,28 +128,30 @@
                                     @enderror
                                 </div>
                             </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Tipologia</label>
-                                    <select class="form-select form-select-lg me-1 @error('course_type_id') is-invalid @enderror" wire:model="course_type_id">
-                                        <option value="">
-                                        @foreach($course_types as $c)
-                                            <option value="{{$c["id"]}}">{{$c["name"]}}</option>
-                                        @endforeach
-                                    </select>
+                            @if(false)
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Tipologia</label>
+                                        <select class="form-select form-select-lg me-1 @error('course_type_id') is-invalid @enderror" wire:model="course_type_id">
+                                            <option value="">
+                                            @foreach($course_types as $c)
+                                                <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                            @endforeach
+                                        </select>
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Durata</label>
-                                    <select class="form-select form-select-lg me-1 @error('course_duration_id') is-invalid @enderror" wire:model="course_duration_id">
-                                        <option value="">
-                                        @foreach($course_durations as $c)
-                                            <option value="{{$c["id"]}}">{{$c["name"]}}</option>
-                                        @endforeach
-                                    </select>
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Durata</label>
+                                        <select class="form-select form-select-lg me-1 @error('course_duration_id') is-invalid @enderror" wire:model="course_duration_id">
+                                            <option value="">
+                                            @foreach($course_durations as $c)
+                                                <option value="{{$c["id"]}}">{{$c["name"]}}</option>
+                                            @endforeach
+                                        </select>
+                                    </div>
                                 </div>
-                            </div>
+                            @endif
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">Frequenza</label>
@@ -186,42 +186,45 @@
                                     <input class="form-control" type="date" placeholder="Data fine" wire:model="date_to">
                                 </div>
                             </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Prezzo mensile</label>
-                                    <input class="form-control js-keyupTitle @error('price') is-invalid @enderror" type="text" id="price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="price">
-                                    @error('price')
-                                        <div class="invalid-feedback">{{ $message }}</div>
-                                    @enderror
+                            @if(false)
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Prezzo mensile</label>
+                                        <input class="form-control js-keyupTitle @error('price') is-invalid @enderror" type="text" id="price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="price">
+                                        @error('price')
+                                            <div class="invalid-feedback">{{ $message }}</div>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Prezzo iscrizione</label>
-                                    <input class="form-control js-keyupTitle @error('subscription_price') is-invalid @enderror" type="text" id="subscription_price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="subscription_price">
-                                    @error('subscription_price')
-                                        <div class="invalid-feedback">{{ $message }}</div>
-                                    @enderror
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Prezzo iscrizione</label>
+                                        <input class="form-control js-keyupTitle @error('subscription_price') is-invalid @enderror" type="text" id="subscription_price" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="subscription_price">
+                                        @error('subscription_price')
+                                            <div class="invalid-feedback">{{ $message }}</div>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Causale</label>
-                                    <livewire:causals :type="$typeIN" :idx="0" :causal_id="$causal_id" :wire:key="0" />
-                                    @error('causal_id')
-                                        <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
-                                    @enderror
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Causale</label>
+                                        <livewire:causals :type="$typeIN" :idx="0" :causal_id="$causal_id" :wire:key="0" />
+                                        @error('causal_id')
+                                            <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Causale iscrizione</label>
-                                    <livewire:causals :type="$typeIN" :idx="0" :causal_id="$sub_causal_id" :wire:key="0" :emit="$setSubscriptionCausal" />
-                                    @error('sub_causal_id')
-                                        <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
-                                    @enderror
+                                <div class="col-6 mt-2">
+                                    <div class="form--item">
+                                        <label for="inputName" class="form-label">Causale iscrizione</label>
+                                        <livewire:causals :type="$typeIN" :idx="0" :causal_id="$sub_causal_id" :wire:key="0" :emit="$setSubscriptionCausal" />
+                                        @error('sub_causal_id')
+                                            <span style="argin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color);">{{ $message }}</span>
+                                        @enderror
+                                    </div>
                                 </div>
-                            </div>
+                            @endif
+
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">N° partecipanti</label>
@@ -243,6 +246,19 @@
                                     </select>
                                 </div>
                             </div>
+
+                            <div class="col-6 mt-2">
+                                <div class="form--item">
+                                    <label for="inputName" class="form-label">Gruppo di appartenenza</label>
+                                    <select class="form-select form-select-lg me-1 @error('category_id') is-invalid @enderror" wire:model="category_id">
+                                        <option value="">
+                                        @foreach($categories as $category)
+                                            <option value="{{$category["id"]}}">{{str_repeat('  ', $category["indentation"])}}{{$category["name"]}}
+                                        @endforeach
+                                    </select>
+                                </div>
+                            </div>
+
                             <div class="col-6 mt-2">
                                 <div class="form--item">
                                     <label for="inputName" class="form-label">Anno</label>
@@ -257,17 +273,7 @@
                                 </div>
                             </div>
 
-                            <div class="col-6 mt-2">
-                                <div class="form--item">
-                                    <label for="inputName" class="form-label">Gruppo di appartenenza</label>
-                                    <select class="form-select form-select-lg me-1 @error('category_id') is-invalid @enderror" wire:model="category_id">
-                                        <option value="">
-                                        @foreach($categories as $category)
-                                            <option value="{{$category["id"]}}">{{str_repeat('  ', $category["indentation"])}}{{$category["name"]}}
-                                        @endforeach
-                                    </select>
-                                </div>
-                            </div>
+                            
 
                             <div class="col">
                                 <div class="form--item">
@@ -276,117 +282,85 @@
                                 </div>
                             </div>
 
-                            @if(env('MANAGE_COURSE', false))
-
-                                <div class="row">
-                                    <div class="col-1">
-                                        <br><button class="btn--ui primary add--daye" wire:click.prevent="addPrice()"><i class="fa-solid fa-plus"></i></button>
-                                    </div>
-                                    <div class="col-6">
-                                        @foreach($prices as $idP => $p)
-                                            <div class="row mt-2">
-                                                <div class="col-6">
-                                                    <label for="abb" class="form-label">Abbonamento</label>
-                                                    <select class="form-control" wire:model="prices.{{$idP}}.course_subscription_id">
-                                                        @foreach($course_subscriptions as $s)
-                                                            <option value="{{$s["id"]}}">{{$s["name"]}}
-                                                        @endforeach
-                                                    </select>
-                                                </div>
-                                                <div class="col-5">
-                                                    <label for="price" class="form-label">Prezzo</label>
-                                                    <input class="form-control " type="text" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="prices.{{$idP}}.price">
-                                                </div>
-                                                <div class="col-1">
-                                                    @if($idP > 0)
-                                                        <br><button class="btn--ui primary add--daye" wire:click.prevent="delPrice({{$idP}})"><i class="fa-solid fa-minus"></i></button>
-                                                    @endif
-                                                </div>
-                                            </div>
-                                        @endforeach
-                                    </div>
+                            <div class="row">
+                                <div class="col-1">
+                                    <br><button class="btn--ui primary add--daye" wire:click.prevent="addPrice()"><i class="fa-solid fa-plus"></i></button>
                                 </div>
-
-                                @if($type == 'standard')
-
-                                    @foreach($when as $idW => $w)
-
-                                        <div class="col-12">
-                                            <div class="day durata--corso d-flex">
-                                                <label for="" class="form-label">Giorno</label>
-                                                <div class="durata--corso_days mb-3">
-                                                    <a class="{{in_array('lun', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'lun')">Lun</a>
-                                                    <a class="{{in_array('mar', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'mar')">Mar</a>
-                                                    <a class="{{in_array('mer', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'mer')">Mer</a>
-                                                    <a class="{{in_array('gio', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'gio')">Gio</a>
-                                                    <a class="{{in_array('ven', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'ven')">Ven</a>
-                                                    <a class="{{in_array('sab', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'sab')">Sab</a>
-                                                    <a class="{{in_array('dom', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'dom')">Dom</a>
-                                                </div>
-
-                                                <div class="durata--corso_select">
-                                                    <div class="from--h me-3">
-                                                        <label for="" class="form-label">Dalle</label>
-                                                        <select class="form-select" wire:model="when.{{$idW}}.from">
-                                                            <option value="">--Seleziona--
-                                                            @for($c=6;$c<=23;$c++)
-                                                                <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00
-                                                                <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30
-                                                            @endfor
-                                                        </select>
-                                                    </div>
-                                                    <div class="to--h">
-                                                        <label for="" class="form-label">Alle</label>
-                                                        <select class="form-select" wire:model="when.{{$idW}}.to">
-                                                            <option value="">--Seleziona--
-                                                            @for($c=6;$c<=23;$c++)
-                                                                <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00
-                                                                <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30
-                                                            @endfor
-                                                        </select>
-                                                    </div>
-                                                </div>
-
-                                                <button class="btn--ui primary remove--day position-absolute" wire:click.prevent="delRow({{$idW}})"><i class="fa-solid fa-minus"></i></button>
-                                                <button class="btn--ui primary add--day position-absolute" wire:click.prevent="addRow()"><i class="fa-solid fa-plus"></i></button>
+                                <div class="col-6">
+                                    @foreach($prices as $idP => $p)
+                                        <div class="row mt-2">
+                                            <div class="col-6">
+                                                <label for="abb" class="form-label">Abbonamento</label>
+                                                <select class="form-control" wire:model="prices.{{$idP}}.course_subscription_id">
+                                                    @foreach($course_subscriptions as $s)
+                                                        <option value="{{$s["id"]}}">{{$s["name"]}}
+                                                    @endforeach
+                                                </select>
+                                            </div>
+                                            <div class="col-5">
+                                                <label for="price" class="form-label">Prezzo</label>
+                                                <input class="form-control " type="text" onkeyup="onlyNumberAmount(this)" placeholder="€ 0,00" wire:model="prices.{{$idP}}.price">
+                                            </div>
+                                            <div class="col-1">
+                                                @if($idP > 0)
+                                                    <br><button class="btn--ui primary add--daye" wire:click.prevent="delPrice({{$idP}})"><i class="fa-solid fa-minus"></i></button>
+                                                @endif
                                             </div>
                                         </div>
-
                                     @endforeach
-                                
-                                @endif
+                                </div>
+                            </div>
 
-                                <!--<div class="col-6">
-                                    <div class="form--item">
-                                        <label for="inputName" class="form-label">Durata</label>
-                                        <input class="form-control js-keyupTitle " type="text" id="duration" placeholder="Durata" wire:model="duration">
+                            @if($type == 'standard')
+
+                                @foreach($when as $idW => $w)
 
-                                    </div>
-                                </div>-->
-                                @if(false)
                                     <div class="col-12">
-                                        <div class="form--item">
-                                            <label for="inputName" class="form-label">Seleziona mesi</label>
-                                            @foreach($monthList as $idx => $y)
-                                                <div class="row">
-                                                    <div class="col-12">
-                                                        <b>{{$idx}}</b>
-                                                    </div>
-                                                    @foreach($y as $m)
-                                                        <div class="col-3">{{$m}}</div>
-                                                    @endforeach
+                                        <div class="day durata--corso d-flex">
+                                            <label for="" class="form-label">Giorno</label>
+                                            <div class="durata--corso_days mb-3">
+                                                <a class="{{in_array('lun', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'lun')">Lun</a>
+                                                <a class="{{in_array('mar', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'mar')">Mar</a>
+                                                <a class="{{in_array('mer', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'mer')">Mer</a>
+                                                <a class="{{in_array('gio', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'gio')">Gio</a>
+                                                <a class="{{in_array('ven', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'ven')">Ven</a>
+                                                <a class="{{in_array('sab', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'sab')">Sab</a>
+                                                <a class="{{in_array('dom', $when[$idW]["day"]) ? 'selected' : 'notSelected'}}" wire:click="setDay({{$idW}}, 'dom')">Dom</a>
+                                            </div>
+
+                                            <div class="durata--corso_select">
+                                                <div class="from--h me-3">
+                                                    <label for="" class="form-label">Dalle</label>
+                                                    <select class="form-select" wire:model="when.{{$idW}}.from">
+                                                        <option value="">--Seleziona--
+                                                        @for($c=6;$c<=23;$c++)
+                                                            <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00
+                                                            <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30
+                                                        @endfor
+                                                    </select>
                                                 </div>
-                                                <div class="row"><br></div>
-                                            @endforeach
+                                                <div class="to--h">
+                                                    <label for="" class="form-label">Alle</label>
+                                                    <select class="form-select" wire:model="when.{{$idW}}.to">
+                                                        <option value="">--Seleziona--
+                                                        @for($c=6;$c<=23;$c++)
+                                                            <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:00
+                                                            <option value="{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30">{{str_pad($c, 2, "0", STR_PAD_LEFT)}}:30
+                                                        @endfor
+                                                    </select>
+                                                </div>
+                                            </div>
+
+                                            <button class="btn--ui primary remove--day position-absolute" wire:click.prevent="delRow({{$idW}})"><i class="fa-solid fa-minus"></i></button>
+                                            <button class="btn--ui primary add--day position-absolute" wire:click.prevent="addRow()"><i class="fa-solid fa-plus"></i></button>
                                         </div>
                                     </div>
-                                @endif
+
+                                @endforeach
                             
                             @endif
-                            
-                        </div>
-
 
+                        </div>
 
                         <div class="form--item">
                             <button type="button" class="btn--ui lightGrey" onclick="annulla()">Annulla</button>

+ 2 - 0
resources/views/livewire/course_member.blade.php

@@ -275,6 +275,7 @@
                     <th scope="col">Nome</th>
                     <th scope="col">Età</th>
                     <th scope="col">Anno di nascita</th>
+                    <th scope="col">Sesso</th>
                     <th scope="col">Telefono</th>
                     <th scope="col">Certificato</th>
                 </tr>
@@ -664,6 +665,7 @@
                     },
                     { data: 'column_3' },
                     { data: 'column_4' },
+                    { data: 'column_9'  },
                     { data: 'column_5' },
                     {
                         data: 'column_6',

+ 123 - 81
resources/views/livewire/receipt.blade.php

@@ -1,6 +1,7 @@
 <div class="col card--ui" id="card--dashboard">
 
-    <header id="title--section" style="display:none !important"  class="d-flex align-items-center justify-content-between">
+    <header id="title--section" style="display:none !important"
+        class="d-flex align-items-center justify-content-between">
         <div class="title--section_name d-flex align-items-center justify-content-between">
             <i class="ico--ui title_section utenti me-2"></i>
             <h2 class="primary">Ricevute</h2>
@@ -16,7 +17,7 @@
 
     </header>
 
-    <div class="showFilter" style="display:none"  wire:ignore.self>
+    <div class="showFilter" style="display:none" wire:ignore.self>
         <hr size="1">
         <div class="row g-3">
             <div class="col-md-2">
@@ -31,10 +32,12 @@
                         <input id="dateTo" type="date" class="form-control filterTo" name="txtTo">
                     </div>
                     <div class="col-6 mt-2">
-                        <button class="btn--ui lightGrey todayButton" style="width:100%" onclick="setToday('{{date("Y-m-d")}}')">OGGI</button>
+                        <button class="btn--ui lightGrey todayButton" style="width:100%"
+                            onclick="setToday('{{date("Y-m-d")}}')">OGGI</button>
                     </div>
                     <div class="col-6 mt-2">
-                        <button class="btn--ui lightGrey yesterdayButton" style="width:100%" onclick="setYesterday('{{date("Y-m-d",strtotime("-1 days"))}}')">IERI</button>
+                        <button class="btn--ui lightGrey yesterdayButton" style="width:100%"
+                            onclick="setYesterday('{{date("Y-m-d", strtotime("-1 days"))}}')">IERI</button>
                     </div>
                 </div>
             </div>
@@ -52,6 +55,22 @@
                     </div>
                 </div>
             </div>
+            <div class="col-md-3">
+                <div class="row">
+                    <div class="col-md-12" style="margin-bottom:10px;">
+                        <b>Cerca Utente</b>
+                    </div>
+                    <div class="col-12">
+                        <select class="form-select filterMember" wire:model="filteredMemberId">
+                            <option value="">--Seleziona Utente--</option>
+                            @foreach($members as $member)
+                                <option value="{{ $member->id }}">{{ $member->last_name }} {{ $member->first_name }}
+                                </option>
+                            @endforeach
+                        </select>
+                    </div>
+                </div>
+            </div>
         </div>
         <div class="row g-3">
             <div class="col-md-12" style="text-align:right">
@@ -92,15 +111,23 @@
                             <td>{{$record->type == 'IN' ? ($record->member->first_name) : ''}}</td>
                             <td>
                                 <span class="tablesaw-cell-content">
-                                    <span class="badge tessera-badge {{$record->status == 1 ? 'active' : 'suspended'}}">{{$record->status == 1 ? 'Attivo' : 'Annullata'}}</span>
+                                    <span
+                                        class="badge tessera-badge {{$record->status == 1 ? 'active' : 'suspended'}}">{{$record->status == 1 ? 'Attivo' : 'Annullata'}}</span>
                                 </span>
                             </td>
                             <td>{{date("d/m/Y", strtotime($record->date))}}</td>
                             <td>{{formatPrice($record->rows->sum('amount'))}}</td>
                             <td>
-                                <button type="button" class="btn u" onclick="document.location.href='/in?id={{$record->record_id}}'" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Visualizza ricevuta"><i class="fa-regular fa-eye"></i></button>
-                                <a target="_blank" class="btn  u" href="/receipt/{{$record->id}}" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Stampa ricevuta"><i class="fa-regular fa-file-lines"></i></a>
-                                <a target="_blank" class="btn u" onclick="sendMail({{$record->id}})"><i class="fa-regular fa-envelope" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Invia email"></i></a>
+                                <button type="button" class="btn u"
+                                    onclick="document.location.href='/in?id={{$record->record_id}}'" data-bs-toggle="popover"
+                                    data-bs-trigger="hover focus" data-bs-placement="bottom"
+                                    data-bs-content="Visualizza ricevuta"><i class="fa-regular fa-eye"></i></button>
+                                <a target="_blank" class="btn  u" href="/receipt/{{$record->id}}" data-bs-toggle="popover"
+                                    data-bs-trigger="hover focus" data-bs-placement="bottom"
+                                    data-bs-content="Stampa ricevuta"><i class="fa-regular fa-file-lines"></i></a>
+                                <a target="_blank" class="btn u" onclick="sendMail({{$record->id}})"><i
+                                        class="fa-regular fa-envelope" data-bs-toggle="popover" data-bs-trigger="hover focus"
+                                        data-bs-placement="bottom" data-bs-content="Invia email"></i></a>
                             </td>
                         </tr>
                     @endforeach
@@ -137,54 +164,60 @@
 @push('scripts')
     <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
     <style>
-        .select2-container--default .select2-selection--single{
+        .select2-container--default .select2-selection--single {
             background-color: #E9F0F5;
             border: 0.0625rem solid #DFE5EB;
             font-size: 0.75rem;
         }
-        .select2-selection
-        {
+
+        .select2-selection {
             height: 38px !important;
         }
-        .select2-selection__rendered
-        {
-            padding-top:3px;
+
+        .select2-selection__rendered {
+            padding-top: 3px;
         }
+
         .select2 {
-            width:100% !important;
+            width: 100% !important;
         }
     </style>
-    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
+    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
     <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
 @endpush
 
 @push('scripts')
     <link href="/css/datatables.css" rel="stylesheet" />
-    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
+    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
 
 
     <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
     <style>
-        .select2-container--default .select2-selection--single{
+        .select2-container--default .select2-selection--single {
             background-color: #E9F0F5;
             border: 0.0625rem solid #DFE5EB;
             font-size: 0.75rem;
         }
-        .select2-selection
-        {
+
+        .select2-selection {
             height: 38px !important;
         }
-        .select2-selection__rendered
-        {
-            padding-top:3px;
+
+        .select2-selection__rendered {
+            padding-top: 3px;
         }
+
         .select2 {
-            width:100% !important;
+            width: 100% !important;
         }
-        .select2-selection--multiple{
+
+        .select2-selection--multiple {
             overflow: hidden !important;
             height: auto !important;
         }
+
         .select2-container {
             box-sizing: border-box;
             display: inline-block;
@@ -192,6 +225,7 @@
             position: relative;
             vertical-align: middle;
         }
+
         .select2-container .select2-selection--single {
             box-sizing: border-box;
             cursor: pointer;
@@ -200,6 +234,7 @@
             user-select: none;
             -webkit-user-select: none;
         }
+
         .select2-container .select2-selection--single .select2-selection__rendered {
             display: block;
             padding-left: 8px;
@@ -208,11 +243,13 @@
             text-overflow: ellipsis;
             white-space: nowrap;
         }
-        .select2-selection__choice__display{
-            color:#000000 !important;
+
+        .select2-selection__choice__display {
+            color: #000000 !important;
         }
     </style>
-    <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
+    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
+        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
     <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
     <script src="/assets/js/datatables.js"></script>
     <script src="https://cdn.datatables.net/buttons/3.0.2/js/buttons.dataTables.js"></script>
@@ -224,85 +261,88 @@
 @push('scripts')
     <script>
 
-        $(document).ready(function() {
+        $(document).ready(function () {
             loadDataTable();
-        } );
+        });
+
+        $('.filterStatus').select2({ "language": { "noResults": function () { return "Nessun risultato"; } } });
+        $('.filterMember').select2({
+            "language": { "noResults": function () { return "Nessun risultato"; } }
+        });
 
-        $('.filterStatus').select2({"language": {"noResults": function(){return "Nessun risultato";}}});
 
         Livewire.on('load-data-table', () => {
-            $('.filterStatus').select2({"language": {"noResults": function(){return "Nessun risultato";}}});
+            $('.filterStatus').select2({ "language": { "noResults": function () { return "Nessun risultato"; } } });
+            $('.filterMember').select2({ "language": { "noResults": function () { return "Nessun risultato"; } } });
+
             loadDataTable();
         });
 
         Livewire.on('load-data-table', () => {
-            setTimeout(function() {loadDataTable()}, 100);
+            setTimeout(function () { loadDataTable() }, 100);
         });
 
         Livewire.on('destroy-data-table', () => {
             $('#tablesaw-350').DataTable().destroy();
         });
 
-        function destroyDataTable()
-        {
+        function destroyDataTable() {
             $('#tablesaw-350').DataTable().destroy();
         }
 
         var isFilter = false;
-        $(document).ready(function() {
-            $(document).on("click",".showHideFilter",function() {
-                if (isFilter)
-                {
+        $(document).ready(function () {
+            $(document).on("click", ".showHideFilter", function () {
+                if (isFilter) {
                     isFilter = false;
                     $(".showFilter").hide();
                 }
-                else
-                {
+                else {
                     isFilter = true;
                     $(".showFilter").show();
                 }
             });
-        } );
+        });
 
-        function sendMail(id)
-        {
-            $.get("/receipt/mail/" + id, function(data, status){
+        function sendMail(id) {
+            $.get("/receipt/mail/" + id, function (data, status) {
                 alert('Mail inviata');
             });
         }
 
         @if(isset($_GET["showFilters"]))
             var filterStatus = localStorage.getItem("filterStatusReceipt");
-            if (filterStatus)
-            {
+            if (filterStatus) {
                 $('.filterStatus').val(filterStatus).trigger('change');
             }
             var filterFrom = localStorage.getItem("filterFromReceipt");
-            if (filterFrom)
-            {
+            if (filterFrom) {
                 $('input[name="txtFrom"]').val(filterFrom);
             }
             var filterTo = localStorage.getItem("filterToReceipt");
-            if (filterTo)
-            {
+            if (filterTo) {
                 $('input[name="txtTo"]').val(filterFrom);
             }
+            var filterMember = localStorage.getItem("filterMemberReceipt");
+            if (filterMember) {
+                $('.filterMember').val(filterMember).trigger('change');
+            }
         @endif
 
-        function reset()
-        {
-            $(".todayButton").addClass("lightGrey");
-            $(".yesterdayButton").addClass("lightGrey");
-            $('.filterStatus').val(null).trigger("change");
-            $('.filterFrom').val('');
-            $('.filterTo').val('');
-            destroyDataTable();
-            loadDataTable();
-        }
+            function reset() {
+                $(".todayButton").addClass("lightGrey");
+                $(".yesterdayButton").addClass("lightGrey");
+                $('.filterStatus').val(null).trigger("change");
+                $('.filterMember').val(null).trigger("change");
+                $('.filterFrom').val('');
+                $('.filterTo').val('');
+                destroyDataTable();
+                loadDataTable();
+            }
 
-        function loadDataTable(){
+        function loadDataTable() {
 
-            if ( $.fn.DataTable.isDataTable('#tablesaw-350') ) {
+            if ($.fn.DataTable.isDataTable('#tablesaw-350')) {
                 $('#tablesaw-350').DataTable().destroy();
             }
 
@@ -315,19 +355,22 @@
             var filterTo = $('.filterTo').val();
             localStorage.setItem("filterToReeipt", filterTo);
 
+            var filterMember = $('.filterMember').val();
+            localStorage.setItem("filterMemberReceipt", filterMember);
+
             $('#tablesaw-350').DataTable({
                 serverSide: true,
                 ajax: {
-                    url : '/get_receipts?filterStatus=' + filterStatus + '&filterFrom=' + filterFrom + '&filterTo=' + filterTo,
-                    dataSrc: function (json){
-                        if(json.totals){
+                    url: '/get_receipts?filterStatus=' + filterStatus + '&filterFrom=' + filterFrom + '&filterTo=' + filterTo + '&filterMember=' + filterMember,
+                    dataSrc: function (json) {
+                        if (json.totals) {
                             totals = json.totals;
                         }
                         return json.data;
                     }
                 },
                 thead: {
-                'th': {'background-color': 'blue'}
+                    'th': { 'background-color': 'blue' }
                 },
                 columns: [
                     {
@@ -344,7 +387,7 @@
                     },
                     {
                         data: "status",
-                        render: function (data){
+                        render: function (data) {
                             // Split class and text
                             var ret = '<span class="tablesaw-cell-content"><span class="badge tessera-badge ' + (data == 1 ? 'active' : 'suspended') + '">' + (data == 1 ? 'Attiva' : 'Annullata') + '</span></span>';
                             return ret;
@@ -358,7 +401,7 @@
                     },
                     {
                         data: "action",
-                        render: function (data){
+                        render: function (data) {
                             var ids = data.split("|");
                             // Split class and text
                             var ret = '<button type="button" class="btn u" onclick="document.location.href=' + "'" + '/in?id=' + ids[1] + "&from=receipts'" + '" data-bs-toggle="popover"  data-bs-trigger="hover focus" data-bs-placement="bottom" data-bs-content="Visualizza ricevuta"><i class="fa-regular fa-eye"></i></button>';
@@ -369,8 +412,8 @@
                     }
                 ],
                 layout: {
-                    topStart : null,
-                    topEnd : null,
+                    topStart: null,
+                    topEnd: null,
                     top1A: {
                         buttons: [
                             {
@@ -378,21 +421,21 @@
                                 text: 'ESPORTA',
                                 buttons: [
                                     {
-                                    extend: 'excelHtml5',"action":newexportaction,
+                                        extend: 'excelHtml5', "action": newexportaction,
                                         title: 'Ricevute',
                                         exportOptions: {
                                             columns: ":not(':last')"
                                         }
                                     },
                                     {
-                                        extend: 'pdfHtml5',"action":newexportaction,
+                                        extend: 'pdfHtml5', "action": newexportaction,
                                         title: 'Ricevute',
                                         exportOptions: {
                                             columns: ":not(':last')"
                                         }
                                     },
                                     {
-                                        extend: 'print',"action":newexportaction,
+                                        extend: 'print', "action": newexportaction,
                                         text: 'Stampa',
                                         title: 'Ricevute',
                                         exportOptions: {
@@ -404,12 +447,12 @@
                             }
                         ]
                     },
-                    top1B : {
+                    top1B: {
                         pageLength: {
                             menu: [[10, 25, 50, 100, 100000], [10, 25, 50, 100, "Tutti"]]
                         }
                     },
-                    top1C :'search',
+                    top1C: 'search',
                 },
                 order: [[0, 'desc'], [1, 'asc']],
                 pagingType: 'numbers',
@@ -423,18 +466,17 @@
             });
             $('#tablesaw-350 thead tr th').addClass('col');
             $('#tablesaw-350 thead tr th').css("background-color", "#f6f8fa");
-            $('#tablesaw-350').on('draw.dt', function() {
+            $('#tablesaw-350').on('draw.dt', function () {
                 $('[data-bs-toggle="popover"]').popover()
             });
 
-            $(document).ready(function() {
-                $(document).on("click",".addData",function() {
+            $(document).ready(function () {
+                $(document).on("click", ".addData", function () {
                     $(".title--section_addButton").trigger("click")
                 });
-            } );
+            });
 
         }
 
     </script>
 @endpush
-

+ 3 - 1
resources/views/livewire/records_in.blade.php

@@ -1249,7 +1249,7 @@
                             return ret;
                         }
                     },
-                    { data: 'total', className: "numericCol" },
+                    { data: 'total'},
                     { data: 'last_name' },
                     { data: 'first_name' },
                     { data: 'commercial' },
@@ -1393,6 +1393,8 @@
         function setCommercialType(t)
         {
             @this.set('commercial', t);
+            Livewire.emit('refreshMembers');
+
         }
 
         Livewire.on('goToAnchor', () =>

+ 579 - 0
resources/views/livewire/reports.blade.php

@@ -0,0 +1,579 @@
+<!-- filepath: /Users/fabiofratini/Desktop/Projects/iao_team/resources/views/livewire/reports.blade.php -->
+<div class="col card--ui" id="card--dashboard">
+
+    <header id="title--section" style="display:none !important"
+        class="d-flex align-items-center justify-content-between">
+        <div class="title--section_name d-flex align-items-center justify-content-between">
+            <i class="ico--ui title_section utenti me-2"></i>
+            <h2 class="primary">Reports</h2>
+        </div>
+
+    </header>
+
+
+    <section id="subheader" class="d-flex align-items-center">
+    </section>
+
+    <section id="reports-section">
+
+        <div class="row">
+            <div class="col-md-12">
+                <canvas id="monthly-in-out-chart"></canvas>
+            </div>
+
+        </div>
+        <div class="col-md-12 chart-container">
+            <canvas id="causals-chart" style="height: 300px; max-height: 300px;"></canvas>
+        </div>
+        <div class="row mt-5">
+            <div class="col-md-12">
+                <canvas id="tesserati-chart"></canvas>
+            </div>
+        </div>
+        <div class="row mt-5">
+            <div class="col-md-6">
+                <select wire:model="selectedCourse" wire:change="updateCourseChart">
+                    <option value="">Seleziona un Corso</option>
+                    @foreach($courses as $course)
+                        <option value="{{ $course['id'] }}">{{ $course['full_name'] }}</option>
+                    @endforeach
+                </select>
+            </div>
+            <div class="col-md-12 mt-3">
+                <canvas id="courses-chart" style="height: 250px; max-height: 250px;"></canvas>
+            </div>
+        </div>
+    </section>
+
+</div>
+
+@push('scripts')
+    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+    <script>
+        document.addEventListener('DOMContentLoaded', function () {
+            initializeChartSizes();
+
+            window.livewire.on('dataUpdated', () => {
+                updateCharts();
+                updateCausalsChart();
+            });
+
+            window.livewire.on('courseDataUpdated', async (courseId) => {
+                console.log('Course data update event received for course ID:', courseId);
+                await updateCoursesChart(courseId);
+                updateCausalsChart();
+                Object.keys(window.chartSizes).forEach(chartId => {
+                    restoreChartSize(chartId);
+                });
+            });
+
+            updateCharts();
+            updateCausalsChart();
+            updateTesseratiChart();
+
+
+            async function updateCharts() {
+                try {
+                    const monthlyData = await @this.getMonthlyTotals();
+
+                    if (window.monthlyInOutChart) {
+                        window.monthlyInOutChart.destroy();
+                    }
+
+                    const monthlyInOutCtx = document.getElementById('monthly-in-out-chart').getContext('2d');
+
+                    window.monthlyInOutChart = new Chart(monthlyInOutCtx, {
+                        type: 'bar',
+                        data: monthlyData,
+                        options: {
+                            responsive: true,
+                            scales: {
+                                x: {
+                                    title: {
+                                        display: false,
+                                        text: 'Mese'
+                                    },
+                                    grid: {
+                                        display: false
+                                    }
+                                },
+                                y: {
+                                    display: false,
+                                    title: {
+                                        display: false,
+                                        text: 'Importo (€)'
+                                    },
+                                    beginAtZero: true,
+                                    ticks: {
+                                        display: false
+                                    },
+                                    grid: {
+                                        display: false
+                                    }
+                                }
+                            },
+                            plugins: {
+                                legend: {
+                                    display: true
+                                },
+                                title: {
+                                    display: true,
+                                    text: 'Entrate/Uscite Mensili',
+                                    font: {
+                                        size: 16
+                                    }
+                                },
+                                tooltip: {
+                                    callbacks: {
+                                        label: function (context) {
+                                            let label = context.dataset.label || '';
+                                            if (label) {
+                                                label += ': ';
+                                            }
+                                            if (context.parsed.y !== null) {
+                                                label += new Intl.NumberFormat('it-IT', {
+                                                    style: 'currency',
+                                                    currency: 'EUR'
+                                                }).format(context.parsed.y);
+                                            }
+                                            return label;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    });
+
+                    const summaryData = await @this.getYearlySummary();
+
+                } catch (error) {
+                    console.error('Error updating charts:', error);
+                    document.getElementById('monthly-in-out-chart').insertAdjacentHTML(
+                        'afterend',
+                        '<div class="alert alert-danger">Errore nel caricamento dei dati finanziari</div>'
+                    );
+                }
+            }
+
+            async function updateCausalsChart() {
+                try {
+                    const causalsData = await @this.getTopCausalsByAmount(10, 'IN');
+
+                    if (window.causalsChart) {
+                        window.causalsChart.destroy();
+                    }
+
+                    const causalsCtx = document.getElementById('causals-chart').getContext('2d');
+
+                    const existingTabs = document.querySelector('.causals-tabs');
+                    if (existingTabs) {
+                        existingTabs.remove();
+                    }
+
+                    const existingTitle = document.querySelector('.causals-title');
+                    if (existingTitle) {
+                        existingTitle.remove();
+                    }
+
+                    const chartTitle = document.createElement('h4');
+                    chartTitle.className = 'text-center mt-2 mb-3 causals-title';
+
+                    const chartCanvas = document.getElementById('causals-chart');
+                    chartCanvas.parentNode.insertBefore(chartTitle, chartCanvas);
+
+                    const inData = causalsData.inData;
+
+                    const colors = [
+                        'rgba(54, 162, 235, 0.8)',   // Blue
+                        'rgba(75, 192, 192, 0.8)',   // Teal
+                        'rgba(153, 102, 255, 0.8)',  // Purple
+                        'rgba(255, 159, 64, 0.8)',   // Orange
+                        'rgba(39, 174, 96, 0.8)',    // Green
+                        'rgba(41, 128, 185, 0.8)',   // Dark blue
+                        'rgba(142, 68, 173, 0.8)',   // Dark purple
+                        'rgba(230, 126, 34, 0.8)',   // Dark orange
+                        'rgba(46, 204, 113, 0.8)',   // Light green
+                        'rgba(52, 152, 219, 0.8)'    // Light blue
+                    ];
+
+                    const commonOptions = {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        plugins: {
+                            title: {
+                                display: true,
+                                text: 'Causali performanti',
+                                font: {
+                                    size: 16
+                                }
+                            },
+                            tooltip: {
+                                callbacks: {
+                                    label: function (context) {
+                                        const fullName = inData[context.dataIndex]?.fullName || context.label;
+                                        const value = context.raw;
+
+                                        return fullName + ': ' + new Intl.NumberFormat('it-IT', {
+                                            style: 'currency',
+                                            currency: 'EUR'
+                                        }).format(value);
+                                    }
+                                }
+                            },
+                            legend: {
+                                display: true,
+                                position: 'right',
+                                labels: {
+                                    boxWidth: 15,
+                                    padding: 10,
+                                    generateLabels: function (chart) {
+                                        const data = chart.data;
+                                        if (data.labels.length && data.datasets.length) {
+                                            return data.labels.map(function (label, i) {
+                                                const meta = chart.getDatasetMeta(0);
+                                                const style = meta.controller.getStyle(i);
+
+                                                let shortenedLabel = label;
+                                                if (label.length > 20) {
+                                                    shortenedLabel = label.substring(0, 17) + '...';
+                                                }
+
+                                                return {
+                                                    text: shortenedLabel,
+                                                    fillStyle: style.backgroundColor,
+                                                    hidden: !chart.getDataVisibility(i),
+                                                    index: i,
+                                                    datasetIndex: 0
+                                                };
+                                            });
+                                        }
+                                        return [];
+                                    }
+                                }
+                            }
+                        }
+                    };
+
+                    let chartData = {
+                        labels: inData.map(item => item.label),
+                        datasets: [{
+                            label: 'Importo',
+                            data: inData.map(item => item.value),
+                            backgroundColor: inData.map((item, index) => colors[index % colors.length]),
+                            borderWidth: 1,
+                            borderColor: '#fff'
+                        }]
+                    };
+
+                    window.causalsChart = new Chart(causalsCtx, {
+                        type: 'doughnut',
+                        data: chartData,
+                        options: commonOptions
+                    });
+
+                } catch (error) {
+                    console.error('Error updating causals chart:', error);
+                    document.getElementById('causals-chart').insertAdjacentHTML(
+                        'afterend',
+                        '<div class="alert alert-danger">Errore nel caricamento dei dati delle causali</div>'
+                    );
+                }
+            }
+        });
+
+
+        async function updateCoursesChart() {
+            try {
+                const courseData = await @this.getCourseMonthlyEarnings();
+                console.log('Course data received:', courseData);
+
+                if (window.coursesChart) {
+                    window.coursesChart.destroy();
+                }
+
+                const coursesCtx = document.getElementById('courses-chart').getContext('2d');
+
+                const dashedLinesPlugin = {
+                    // Plugin definition unchanged
+                    id: 'dashedLines',
+                    beforeDatasetsDraw: (chart) => {
+                        const ctx = chart.ctx;
+                        const lineDataset = chart.data.datasets.find(d => d.type === 'line' && d.label === 'Pagamenti Attesi');
+                        const barDataset = chart.data.datasets.find(d => d.type === 'bar' && d.label === 'Pagamenti Effettuati');
+
+                        if (!lineDataset || !barDataset) return;
+
+                        const lineMeta = chart.getDatasetMeta(chart.data.datasets.indexOf(lineDataset));
+                        const barMeta = chart.getDatasetMeta(chart.data.datasets.indexOf(barDataset));
+
+                        if (!lineMeta.data.length || !barMeta.data.length) return;
+
+                        const missingData = lineDataset.missing || [];
+
+                        ctx.save();
+                        ctx.lineWidth = 2;
+                        ctx.setLineDash([8, 4]);
+                        ctx.strokeStyle = 'rgba(48, 51, 107, 0.3)';
+
+                        for (let i = 0; i < lineMeta.data.length; i++) {
+                            const linePoint = lineMeta.data[i];
+                            const barPoint = barMeta.data[i];
+
+                            if (!linePoint || !barPoint) continue;
+
+                            ctx.beginPath();
+                            ctx.moveTo(linePoint.x, linePoint.y);
+                            ctx.lineTo(linePoint.x, barPoint.y);
+                            ctx.stroke();
+
+                            if (missingData[i] && missingData[i] > 0) {
+                                const midY = (linePoint.y + barPoint.y) / 2;
+                                ctx.textAlign = 'center';
+                                ctx.font = '10px Arial';
+                                ctx.fillStyle = 'rgba(48, 51, 107, 0.8)';
+                            }
+                        }
+
+                        ctx.restore();
+                    }
+                };
+
+                window.coursesChart = new Chart(coursesCtx, {
+                    type: 'bar',
+                    plugins: [dashedLinesPlugin],
+                    data: courseData,
+                    options: {
+                        responsive: true,
+                        maintainAspectRatio: false,
+                        layout: {
+                            padding: {
+                                top: 20
+                            }
+                        },
+                        // Rest of options unchanged
+                        scales: {
+                            x: {
+                                grid: {
+                                    display: false
+                                }
+                            },
+                            y: {
+                                display: true,
+                                beginAtZero: true,
+                                grid: {
+                                    color: 'rgba(0, 0, 0, 0.1)',
+                                    borderDash: [5, 5]
+                                },
+                                ticks: {
+                                    callback: function (value) {
+                                        return new Intl.NumberFormat('it-IT', {
+                                            style: 'currency',
+                                            currency: 'EUR',
+                                            maximumFractionDigits: 0
+                                        }).format(value);
+                                    }
+                                }
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: true,
+                                position: 'top',
+                                align: 'center',
+                                labels: {
+                                    usePointStyle: true,
+                                    padding: 20
+                                }
+                            },
+                            title: {
+                                display: true,
+                                text: 'Pagamenti per corso',
+                                font: {
+                                    size: 16
+                                }
+                            },
+                            tooltip: {
+                                callbacks: {
+                                    label: function (context) {
+                                        if (context.dataset.label === 'Pagamenti Attesi') {
+                                            let parts = [];
+
+                                            const participants = context.dataset.participants ? context.dataset.participants[context.dataIndex] : 0;
+                                            parts.push('N° iscritti: ' + participants);
+
+                                            const expectedAmount = context.parsed.y;
+                                            parts.push('Pagamenti attesi: ' + new Intl.NumberFormat('it-IT', {
+                                                style: 'currency',
+                                                currency: 'EUR'
+                                            }).format(expectedAmount));
+
+                                            const missingAmount = context.dataset.missing ? context.dataset.missing[context.dataIndex] : 0;
+                                            parts.push('Ancora da pagare: ' + new Intl.NumberFormat('it-IT', {
+                                                style: 'currency',
+                                                currency: 'EUR'
+                                            }).format(missingAmount));
+
+                                            return parts.join(' | ');
+                                        }
+
+                                        let label = context.dataset.label || '';
+                                        if (label) {
+                                            label += ': ';
+                                        }
+
+                                        if (context.parsed.y !== null) {
+                                            label += new Intl.NumberFormat('it-IT', {
+                                                style: 'currency',
+                                                currency: 'EUR'
+                                            }).format(context.parsed.y);
+                                        }
+
+                                        return label;
+                                    }
+                                }
+                            }
+                        },
+                        elements: {
+                            point: {
+                                radius: 4,
+                                hoverRadius: 6
+                            },
+                            line: {
+                                tension: 0.4
+                            }
+                        }
+                    }
+                });
+
+                // Maintain chart dimensions
+                coursesCtx.canvas.style.height = '250px';
+                coursesCtx.canvas.style.maxHeight = '250px';
+
+            } catch (error) {
+                console.error('Error updating courses chart:', error);
+                document.getElementById('courses-chart').insertAdjacentHTML(
+                    'afterend',
+                    '<div class="alert alert-danger">Errore nel caricamento dei dati del corso</div>'
+                );
+            }
+        }
+
+        async function updateTesseratiChart() {
+            try {
+                const tesseratiData = await @this.getTesseratiData();
+
+                if (window.tesseratiChart) {
+                    window.tesseratiChart.destroy();
+                }
+
+                const tesseratiCtx = document.getElementById('tesserati-chart').getContext('2d');
+
+                window.tesseratiChart = new Chart(tesseratiCtx, {
+                    type: 'line',
+                    data: tesseratiData,
+                    options: {
+                        responsive: true,
+                        scales: {
+                            x: {
+                                title: {
+                                    display: true,
+                                    text: 'Anno Tesseramento'
+                                },
+                                grid: {
+                                    display: false
+                                }
+                            },
+                            y: {
+                                title: {
+                                    display: true,
+                                    text: 'Numero di Tesserati'
+                                },
+                                beginAtZero: true,
+                                ticks: {
+                                    precision: 0
+                                }
+                            }
+                        },
+                        plugins: {
+                            legend: {
+                                display: true,
+                                position: 'top'
+                            },
+                            title: {
+                                display: true,
+                                text: 'Andamento Tesserati per Anno',
+                                font: {
+                                    size: 16
+                                }
+                            },
+                            tooltip: {
+                                callbacks: {
+                                    label: function (context) {
+                                        let label = context.dataset.label || '';
+                                        if (label) {
+                                            label += ': ';
+                                        }
+                                        if (context.parsed.y !== null) {
+                                            label += context.parsed.y + ' tesserati';
+                                        }
+                                        return label;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                });
+            } catch (error) {
+                console.error('Error updating tesserati chart:', error);
+                document.getElementById('tesserati-chart').insertAdjacentHTML(
+                    'afterend',
+                    '<div class="alert alert-danger">Errore nel caricamento dei dati tesserati</div>'
+                );
+            }
+        }
+
+        document.addEventListener('DOMContentLoaded', function () {
+            const selectElement = document.querySelector('select[name="selectedCourse"]');
+            if (selectElement) {
+                selectElement.addEventListener('change', function () {
+                    const selectedValue = this.value;
+                    console.log('Selected course ID:', selectedValue);
+                    @this.set('selectedCourse', selectedValue);
+                });
+            }
+        });
+
+        function initializeChartSizes() {
+            window.chartSizes = {
+                'monthly-in-out-chart': {
+                    height: document.getElementById('monthly-in-out-chart').style.height || 'auto',
+                    maxHeight: document.getElementById('monthly-in-out-chart').style.maxHeight || 'none'
+                },
+                'causals-chart': {
+                    height: '300px',
+                    maxHeight: '300px'
+                },
+                'tesserati-chart': {
+                    height: document.getElementById('tesserati-chart').style.height || 'auto',
+                    maxHeight: document.getElementById('tesserati-chart').style.maxHeight || 'none'
+                },
+                'courses-chart': {
+                    height: '250px',
+                    maxHeight: '250px'
+                }
+            };
+        }
+
+        function restoreChartSize(chartId) {
+            if (!window.chartSizes || !window.chartSizes[chartId]) return;
+
+            const canvas = document.getElementById(chartId);
+            if (canvas) {
+                canvas.style.height = window.chartSizes[chartId].height;
+                canvas.style.maxHeight = window.chartSizes[chartId].maxHeight;
+            }
+        }
+
+
+    </script>
+@endpush

+ 43 - 24
routes/web.php

@@ -31,8 +31,7 @@ Route::get('/login', function () {
 
 Route::post('/login', function () {
 
-    if(Auth::attempt(array('email' => $_POST["email"], 'password' => $_POST["password"])))
-    {
+    if (Auth::attempt(array('email' => $_POST["email"], 'password' => $_POST["password"]))) {
         return Redirect::to('/dashboard');
     } else {
         return Redirect::to('/?error=1');
@@ -81,6 +80,8 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/records_in_out', \App\Http\Livewire\RecordINOUT::class);
     Route::get('/users', \App\Http\Livewire\User::class);
     Route::get('/profile', \App\Http\Livewire\Profile::class);
+    Route::get('/reports', \App\Http\Livewire\Reports::class);
+    Route::get('/azienda', \App\Http\Livewire\Azienda::class);
 });
 
 Route::get('/receipt/{id}', function ($id) {
@@ -453,13 +454,13 @@ Route::get('/get_record_in', function () {
         ->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
         ->where('records.type', 'IN');
 
-        $x = \App\Models\Record::select('records.*', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name'), DB::raw('payment_methods.name as payment'), DB::raw('receipts.created_at as receipt_date')) // , \DB::raw('SUM(records.id) As total'))
-                ->leftJoin('members', 'records.member_id', '=', 'members.id')
-                ->leftJoin('payment_methods', 'records.payment_method_id', '=', 'payment_methods.id')
-                ->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
-                ->where('records.type', 'IN');
+    $x = \App\Models\Record::select('records.*', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name'), DB::raw('payment_methods.name as payment'), DB::raw('receipts.created_at as receipt_date')) // , \DB::raw('SUM(records.id) As total'))
+        ->leftJoin('members', 'records.member_id', '=', 'members.id')
+        ->leftJoin('payment_methods', 'records.payment_method_id', '=', 'payment_methods.id')
+        ->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
+        ->where('records.type', 'IN');
 
-        $y = \App\Models\Record::select('records_rows.amount', 'records.member_id', 'records.corrispettivo_fiscale', 'records.deleted', 'records.financial_movement', 'records_rows.causal_id', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name')) // , \DB::raw('SUM(records.id) As total'))
+    $y = \App\Models\Record::select('records_rows.amount', 'records.member_id', 'records.corrispettivo_fiscale', 'records.deleted', 'records.financial_movement', 'records_rows.causal_id', DB::raw('members.first_name as first_name'), DB::raw('members.last_name as last_name')) // , \DB::raw('SUM(records.id) As total'))
         ->leftJoin('members', 'records.member_id', '=', 'members.id')
         ->leftJoin('records_rows', 'records.id', '=', 'records_rows.record_id')
         //->leftJoin('receipts', 'records.id', '=', 'receipts.record_id')
@@ -568,8 +569,8 @@ Route::get('/get_record_in', function () {
         if ($borsellino)
             $excludeCausals[] = $borsellino->id;*/
 
-        // Aggiungo
-        /*
+    // Aggiungo
+    /*
         $excludes = \App\Models\Causal::where('no_records', true)->get();
         foreach($excludes as $e)
         {
@@ -1146,7 +1147,7 @@ Route::get('/get_course_members', function () {
 
     //$datas = \App\Models\MemberCourse::with('member');
 
-    $datas = \App\Models\MemberCourse::select('member_courses.*', 'courses.name as course_name', 'members.first_name', 'members.last_name', 'members.email', 'members.phone', 'members.birth_date')
+    $datas = \App\Models\MemberCourse::select('member_courses.*', 'courses.name as course_name', 'members.first_name', 'members.last_name', 'members.email', 'members.phone', 'members.birth_date','members.gender')
         ->leftJoin('courses', 'member_courses.course_id', '=', 'courses.id')
         ->leftJoin('members', 'member_courses.member_id', '=', 'members.id');
 
@@ -1165,8 +1166,7 @@ Route::get('/get_course_members', function () {
         $datas = $datas->whereIn('member_id', $member_ids);
     }
 
-    if (isset($_GET["filterFromPrevious"]) && $_GET["filterFromPrevious"] != "")
-    {
+    if (isset($_GET["filterFromPrevious"]) && $_GET["filterFromPrevious"] != "") {
         $datas = $datas->whereIn('course_id', [$_GET["filterFromPrevious"]]);
     }
 
@@ -1331,6 +1331,8 @@ Route::get('/get_course_members', function () {
         //$this->filter .= "Anno : " . $this->filterYear . " ";
     }
 
+
+
     $aRet = [];
 
     if (isset($_GET["order"])) {
@@ -1351,6 +1353,8 @@ Route::get('/get_course_members', function () {
             $column = 'phone';
         if ($_GET["order"][0]["column"] == 8)
             $column = 'certificate_expire_date';
+        if ($_GET["order"][0]["column"] == 9)
+            $column = 'gender';
         if ($column != '')
             $datas = $datas->orderBy($column, $_GET["order"][0]["dir"]);
         else
@@ -1399,6 +1403,17 @@ Route::get('/get_course_members', function () {
             $certificateInfo = $status . "|" . date("d/m/Y", strtotime($certificate->expire_date)) . "|" . $certificate->type;
         }
 
+        $genderDisplay = "";
+        if ($r->gender == 'M' || $r->gender == 'U') {
+            $genderDisplay = "Uomo";
+        } elseif ($r->gender == 'F' || $r->gender == 'D') {
+            $genderDisplay = "Donna";
+        } elseif ($r->gender == 'O') {
+            $genderDisplay = "Altro";
+        }elseif ($r->gender == null || $r->gender == "") {
+            $genderDisplay = "N/A";
+        }
+
         $ret[] = array(
             "column_0" => $idx + 1,
             "column_8" => $r->course_name,
@@ -1406,9 +1421,10 @@ Route::get('/get_course_members', function () {
             "column_2" => $r->first_name,
             "column_3" => strval($interval->y),
             "column_4" => date("Y", strtotime($r->birth_date)),
+            "column_9" => $genderDisplay,
             "column_5" => $r->phone,
             "column_6" => $certificateInfo,
-            "column_7" => $r->member_id
+            "column_7" => $r->member_id,
         );
     }
 
@@ -1419,28 +1435,30 @@ Route::get('/get_course_members', function () {
 });
 
 Route::get('/get_receipts', function () {
+    $x = \App\Models\Receipt::select('receipts.*', 'members.first_name', 'members.last_name')
+        ->leftJoin('members', 'receipts.member_id', '=', 'members.id');
 
-
-    $x = \App\Models\Receipt::select('receipts.*', 'members.first_name', 'members.last_name')->leftJoin('members', 'receipts.member_id', '=', 'members.id');
     if (isset($_GET["search"]["value"])) {
         $v = str_replace("'", "\'", stripcslashes($_GET["search"]["value"]));
         $member_ids = \App\Models\Member::where(function ($query) use ($v) {
             $query->whereRaw("CONCAT(first_name, ' ', last_name) like '%" . $v . "%'")
                 ->orWhereRaw("CONCAT(last_name, ' ', first_name) like '%" . $v . "%'");
         })->pluck('id');
-        /*$member_ids = \App\Models\Member::where(function ($query) use ($v) {
-                $query->where('first_name', 'like', '%' . $v . '%')
-                        ->orWhere('last_name', 'like', '%' . $v . '%');
-            })->pluck('id');*/
         $x = $x->whereIn('member_id', $member_ids);
     }
-    if ($_GET["filterStatus"] != '')
+
+    if (isset($_GET["filterStatus"]) && $_GET["filterStatus"] != '')
         $x = $x->where('receipts.status', $_GET["filterStatus"]);
-    if ($_GET["filterFrom"] != "")
+
+    if (isset($_GET["filterFrom"]) && $_GET["filterFrom"] != "")
         $x = $x->where('date', '>=', $_GET["filterFrom"]);
-    if ($_GET["filterTo"] != "")
+
+    if (isset($_GET["filterTo"]) && $_GET["filterTo"] != "")
         $x = $x->where('date', '<=', $_GET["filterTo"]);
 
+    if (isset($_GET["filterMember"]) && $_GET["filterMember"] != "")
+        $x = $x->where('receipts.member_id', $_GET["filterMember"]);
+
     $count = $x->count();
 
     if (isset($_GET["order"])) {
@@ -1468,6 +1486,7 @@ Route::get('/get_receipts', function () {
         $x = $x->offset($_GET["start"])->limit($_GET["length"])->get();
     else
         $x = $x->get();
+
     $datas = [];
     foreach ($x as $idx => $r) {
         $ids = $r->id . "|" . $r->record_id;
@@ -1506,7 +1525,7 @@ function getColor($months, $m)
     return $class;
 }
 
-Route::get('/migrate', function(){
+Route::get('/migrate', function () {
     Artisan::call('migrate');
     dd('migrated!');
 });