ferrari 3 месяцев назад
Родитель
Сommit
f658acbe0a
3 измененных файлов с 95 добавлено и 40 удалено
  1. 51 15
      app/Http/Livewire/Reports.php
  2. 18 2
      public/css/chart-reports.css
  3. 26 23
      resources/views/livewire/reports.blade.php

+ 51 - 15
app/Http/Livewire/Reports.php

@@ -169,6 +169,13 @@ class Reports extends Component
 
         $incomeRecords = DB::table('records')
             ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
+            ->join('causals', function ($join) {
+                $join->on('causals.id', '=', 'records_rows.causal_id')
+                    ->where(function ($query) {
+                        $query->where('causals.no_reports', 0)
+                            ->orWhereNull('causals.no_reports');
+                    });
+            })
             ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
             ->where('records.type', 'IN')
             ->select(DB::raw('MONTH(records.date) as month_num'), DB::raw('SUM(records_rows.amount) as total'))
@@ -177,6 +184,13 @@ class Reports extends Component
 
         $expenseRecords = DB::table('records')
             ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
+            ->join('causals', function ($join) {
+                $join->on('causals.id', '=', 'records_rows.causal_id')
+                    ->where(function ($query) {
+                        $query->where('causals.no_reports', 0)
+                            ->orWhereNull('causals.no_reports');
+                    });
+            })
             ->whereBetween('records.date', [$dateRange['start'], $dateRange['end']])
             ->where('records.type', 'OUT')
             ->select(DB::raw('MONTH(records.date) as month_num'), DB::raw('SUM(records_rows.amount) as total'))
@@ -216,13 +230,20 @@ class Reports extends Component
             ]
         ];
     }
-    
+
     public function getYearlyTotals()
     {
         Log::info('=== getyearlyTotals called ===');
 
         $incomeRecords = DB::table('records')
             ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
+            ->join('causals', function ($join) {
+                $join->on('causals.id', '=', 'records_rows.causal_id')
+                    ->where(function ($query) {
+                        $query->where('causals.no_reports', 0)
+                            ->orWhereNull('causals.no_reports');
+                    });
+            })
             ->where('records.type', 'IN')
             ->selectRaw("
                 CASE
@@ -237,6 +258,13 @@ class Reports extends Component
 
         $expenseRecords = DB::table('records')
             ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
+            ->join('causals', function ($join) {
+                $join->on('causals.id', '=', 'records_rows.causal_id')
+                    ->where(function ($query) {
+                        $query->where('causals.no_reports', 0)
+                            ->orWhereNull('causals.no_reports');
+                    });
+            })
             ->where('records.type', 'OUT')
             ->selectRaw("
                 CASE
@@ -248,21 +276,21 @@ class Reports extends Component
             ")
             ->groupBy('year_num')
             ->get();
-        
+
         // Mappa anno/totale
         $incomeByYear = $incomeRecords->pluck('total', 'year_num');
         $expenseByYear = $expenseRecords->pluck('total', 'year_num');
 
         // Unione di tutti gli anni presenti ordinati
         $allYears = $incomeByYear->keys()
-                                ->merge($expenseByYear->keys())
-                                ->unique()
-                                ->sort()
-                                ->values();
+            ->merge($expenseByYear->keys())
+            ->unique()
+            ->sort()
+            ->values();
 
         // Allineo i dati dei due array, se non presente l'anno: default 0
-        $incomeData  = $allYears->map(fn ($y) => (float) ($incomeByYear[$y]  ?? 0))->toArray();
-        $expenseData = $allYears->map(fn ($y) => (float) ($expenseByYear[$y] ?? 0))->toArray();
+        $incomeData  = $allYears->map(fn($y) => (float) ($incomeByYear[$y]  ?? 0))->toArray();
+        $expenseData = $allYears->map(fn($y) => (float) ($expenseByYear[$y] ?? 0))->toArray();
 
 
         return [
@@ -415,7 +443,7 @@ class Reports extends Component
                 Log::info('Processing course: ' . $course->name . ' (ID: ' . $course->id . ')' . $course);
 
                 $levelName = is_object($course->level) ? $course->level->name : 'No Level';
-                $typeName = '';//$course->getFormattedTypeField();
+                $typeName = ''; //$course->getFormattedTypeField();
                 $frequencyName = is_object($course->frequency) ? $course->frequency->name : 'No Frequency';
                 $year = $course->year ?? '';
 
@@ -563,8 +591,7 @@ class Reports extends Component
             if ($member_course->months) {
                 $monthsData = json_decode($member_course->months, true);
 
-                if (is_array($monthsData) && count($monthsData) > 0) 
-                {
+                if (is_array($monthsData) && count($monthsData) > 0) {
 
 
 
@@ -577,7 +604,7 @@ class Reports extends Component
                             $monthlyData[$monthNumber]['total'] += $pricePerMonth;
                             $monthlyData[$monthNumber]['participants']++;
                             $hasData = true;
-                            
+
                             //if (!is_null($rate->record_id) && $rate->record_id !== '') {
                             if ($month["status"] == 1) {
                                 $monthlyData[$monthNumber]['participants']--;
@@ -637,7 +664,7 @@ class Reports extends Component
             ];
         }
 
-        $daIncassareData = array_map(function($tot, $inc) {
+        $daIncassareData = array_map(function ($tot, $inc) {
             return $tot - $inc;
         }, $totalData, $earnedData);
 
@@ -683,7 +710,14 @@ class Reports extends Component
             ->whereRaw('YEAR(expire_date) <= ?', [$endYear])
             ->get();
 
-        $cardTypes = $memberCards->pluck('card.name')->unique()->filter()->sort()->values();
+        $cardTypes = $memberCards->pluck('card.name')->unique()->filter()->sort()->values()->toArray();
+        usort($cardTypes, function($a, $b) {
+            if ($a == $b) return 0;
+            if ($a == "UISP") return 1;
+            if ($b == "UISP") return 1;
+
+            return strcmp($a, $b);
+        });
 
         $seasonCounts = [];
         $seasonCardCounts = [];
@@ -756,6 +790,7 @@ class Reports extends Component
                 'tension' => 0.3,
                 'fill' => true
             ];
+            if ($cardType != "UISP") $cardTypeDatasets[$cardType]["hidden"] = true;
         }
 
         foreach ($seasonCounts as $seasonPeriod => $members) {
@@ -781,7 +816,8 @@ class Reports extends Component
                 'pointRadius' => 6,
                 'tension' => 0.3,
                 'fill' => true,
-                'type' => 'line'
+                'type' => 'line',
+                'hidden' => true
             ]
         ];
         foreach ($cardTypeDatasets as $dataset) {

+ 18 - 2
public/css/chart-reports.css

@@ -433,6 +433,7 @@
 
 .table-cell.net {
     font-weight: 600;
+    white-space: nowrap;
 }
 
 .table-row.positive .table-cell.net {
@@ -614,6 +615,11 @@
     font-weight: 600;
 }
 
+.course-table .table-cell.earned {
+    justify-content: flex-end;
+    font-weight: 600;
+}
+
 .course-table .table-cell.delta.positive {
     color: #10b981;
 }
@@ -632,6 +638,14 @@
     font-size: 0.8rem;
     color: #10b981;
 }
+
+.course-table .table-cell.earned.positive {
+    color: #10b981;
+}
+
+.course-table .table-cell.earned.neutral {
+    color: #6b7280;
+}
 /*
 .course-table .table-cell.percentage.good {
     color: #10b981;
@@ -747,7 +761,8 @@
 
 .causals-table.compact .table-header {
     display: grid;
-    grid-template-columns: 2fr 60px 45px;
+    /* grid-template-columns: 2fr 80px 45px; */
+    grid-template-columns: 2fr auto;
     gap: 8px;
     padding: 8px 12px;
     font-size: 0.7rem;
@@ -755,7 +770,8 @@
 
 .causals-table.compact .table-row {
     display: grid;
-    grid-template-columns: 2fr 60px 45px;
+    /* grid-template-columns: 2fr 80px 45px; */
+    grid-template-columns: 2fr auto;
     gap: 8px;
     padding: 6px 12px;
     font-size: 0.75rem;

+ 26 - 23
resources/views/livewire/reports.blade.php

@@ -14,14 +14,14 @@
                     <div class="yearly-table-container" id="yearly-table"></div>
                 </div>
             </div>
-            <div class="chart-card">
+            {{-- <div class="chart-card">
                 <div class="chart-header">
                     <h3 class="chart-title">Tesserati per Stagione</h3>
                 </div>
                 <div class="chart-body">
                     <div class="members-table-container" id="members-table"></div>
                 </div>
-            </div>
+            </div> --}}
         </div>
 
         <div class="controls-section">
@@ -99,6 +99,9 @@
                             <div class="chart-container">
                                 <canvas id="members-chart"></canvas>
                             </div>
+                            <div class="chart-body">
+                                <div class="members-table-container" id="members-table"></div>
+                            </div>
                         </div>
                     </div>
                 </div>
@@ -109,7 +112,7 @@
         <div class="chart-row">
             <div class="chart-card modern-course-card">
                 <div class="chart-header">
-                    <h3 class="chart-title">Analisi Corsi</h3>
+                    <h3 class="chart-title">Analisi  incassi corsi</h3>
                 </div>
                 <div class="chart-body">
                     <div class="course-controls">
@@ -300,7 +303,7 @@
                                 callbacks: {
                                     label: function (context) {
                                         return context.dataset.label + ': €' +
-                                            new Intl.NumberFormat('it-IT').format(context.parsed.y);
+                                            new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(context.parsed.y);
                                     }
                                 }
                             },
@@ -315,7 +318,7 @@
                                 grid: { color: 'rgba(0, 0, 0, 0.05)' },
                                 ticks: {
                                     callback: function (value) {
-                                        return '€' + new Intl.NumberFormat('it-IT').format(value);
+                                        return '€' + new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(value);
                                     }
                                 }
                             }
@@ -588,7 +591,6 @@
                             barThickness: "flex",
                             barPercentage: 0.65,
                             categoryPercentage: 0.4,
-
                         };
                     } else {
                         return {
@@ -602,7 +604,6 @@
                             barThickness: "flex",
                             barPercentage: 0.65,
                             categoryPercentage: 0.4,
-
                         };
                     }
                 });
@@ -697,13 +698,14 @@
                     const expense = parseFloat(expenseData[index] || 0);
                     const net = income - expense;
                     const rowClass = net < 0 ? 'negative' : (net > 0 ? 'positive' : 'neutral');
+                    const plusSymbol = net > 0 ? "+" : "";
 
                     tableHtml += `
                         <div class="table-row ${rowClass}">
                             <div class="table-cell month-name">${month}</div>
-                            <div class="table-cell income">€${new Intl.NumberFormat('it-IT').format(income)}</div>
-                            <div class="table-cell expense">€${new Intl.NumberFormat('it-IT').format(expense)}</div>
-                            <div class="table-cell net">€${new Intl.NumberFormat('it-IT').format(net)}</div>
+                            <div class="table-cell income">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(income)}</div>
+                            <div class="table-cell expense">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(expense)}</div>
+                            <div class="table-cell net">€${plusSymbol}${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(net)}</div>
                         </div>
                     `;
                 });
@@ -738,9 +740,9 @@
                     tableHtml += `
                         <div class="table-row ${rowClass}">
                             <div class="table-cell month-name">${year}</div>
-                            <div class="table-cell income">€${new Intl.NumberFormat('it-IT').format(income)}</div>
-                            <div class="table-cell expense">€${new Intl.NumberFormat('it-IT').format(expense)}</div>
-                            <div class="table-cell net">€${new Intl.NumberFormat('it-IT').format(net)}</div>
+                            <div class="table-cell income">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(income)}</div>
+                            <div class="table-cell expense">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(expense)}</div>
+                            <div class="table-cell net">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(net)}</div>
                         </div>
                     `;
                 });
@@ -881,7 +883,7 @@
                                 },
                                 ticks: {
                                     callback: function (value) {
-                                        return '€' + new Intl.NumberFormat('it-IT').format(value);
+                                        return '€' + new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(value);
                                     }
                                 }
                             }
@@ -906,7 +908,7 @@
                                 callbacks: {
                                     label: function (context) {
                                         return context.dataset.label + ': €' +
-                                            new Intl.NumberFormat('it-IT').format(context.parsed.y);
+                                            new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(context.parsed.y);
                                     }
                                 }
                             }
@@ -1163,14 +1165,14 @@
                                             let index = item.dataIndex;
                                             let monthNameExtended = item.dataset["monthNamesExtended"] ? item.dataset["monthNamesExtended"][index] : 0;
 
-                                            // return item.label + '\n' + 'TOTALE ATTESO: €' + new Intl.NumberFormat('it-IT').format(sum);
-                                            return monthNameExtended + '\n' + 'TOTALE ATTESO: €' + new Intl.NumberFormat('it-IT').format(sum);
+                                            // return item.label + '\n' + 'TOTALE ATTESO: €' + new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(sum);
+                                            return monthNameExtended + '\n' + 'TOTALE ATTESO: €' + new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(sum);
                                         },
                                         // labelTextColor: function(tooltipItems) {
                                         //     return tooltipItems.dataset.backgroundColor;
                                         // },
                                         label: function (tooltipItems) {
-                                            let label = tooltipItems.dataset.label + ': €' + new Intl.NumberFormat('it-IT').format(tooltipItems.parsed.y);
+                                            let label = tooltipItems.dataset.label + ': €' + new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(tooltipItems.parsed.y);
 
                                             return label;
                                         },
@@ -1219,7 +1221,7 @@
                 let tableHtml = `<div class="course-table">
                                     <div class="table-header">
                                         <div class="table-cell month">MESE</div>
-                                        <div class="table-cell percentage">%<br/>INCASSATO</div>
+                                        <div class="table-cell percentage">TOT. INCASSATO</div>
                                         <div class="table-cell delta">TOT. DA INCASSARE</div>
                                         <div class="table-cell suspended">SOSPESI</div>
                                     </div>`;
@@ -1248,14 +1250,15 @@
                     }
 
                     // Delta styling: positive when delta is 0 (fully paid), negative when there's missing amount
-                    const deltaClass = (total > 0 && delta === 0) ? 'positive' :
-                        (delta > 0) ? 'negative' : 'neutral';
+                    const deltaClass = (total > 0 && delta === 0) ? 'positive' : (delta > 0) ? 'negative' : 'neutral';
+                    const earnedClass = (earned > 0) ? 'positive' : 'neutral';
 
                     tableHtml += `
             <div class="table-row">
                 <div class="table-cell month">${row.month}</div>
-                <div class="table-cell percentage ${percentageClass}">${percentageDisplay}</div>
-                <div class="table-cell delta ${deltaClass}">€${new Intl.NumberFormat('it-IT').format(delta)}</div>
+                <!-- <div class="table-cell percentage ${percentageClass}">${percentageDisplay}</div> -->
+                <div class="table-cell earned ${earnedClass}">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(earned)}</div>
+                <div class="table-cell delta ${deltaClass}">€${new Intl.NumberFormat('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(delta)}</div>
                 <div class="table-cell suspended">${row.suspended}</div>
             </div>
         `;