Initial commit
This commit is contained in:
commit
24992624f0
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
||||
146
AmministratoreController.php
Normal file
146
AmministratoreController.php
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\SuperAdmin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Amministratore;
|
||||
use App\Models\User;
|
||||
use Spatie\Permission\Models\Role; // Aggiunto per Role
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Auth; // Aggiunto per Auth
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Gate; // Aggiunto per Gate
|
||||
|
||||
class AmministratoreController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Proteggi le rotte con i permessi di Spatie
|
||||
$this->middleware('permission:view-amministratori', ['only' => ['index']]); // Permesso per visualizzare la lista
|
||||
$this->middleware('permission:manage-amministratori', ['except' => ['index', 'show']]); // Permesso per tutte le altre azioni CRUD
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Gate::authorize('view-amministratori'); // Il middleware nel costruttore è sufficiente
|
||||
$amministratori = Amministratore::with('user')->paginate(10);
|
||||
return view('superadmin.amministratori.index', compact('amministratori'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
// Gate::authorize('manage-amministratori'); // Il middleware nel costruttore è sufficiente
|
||||
$usersWithoutAdminRole = User::doesntHave('amministratore')->get(); // Utenti non ancora associati a un amministratore
|
||||
return view('superadmin.amministratori.create', compact('usersWithoutAdminRole'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// Gate::authorize('manage-amministratori'); // Il middleware nel costruttore è sufficiente
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users,email',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
'nome' => 'required|string|max:255',
|
||||
'cognome' => 'required|string|max:255',
|
||||
'denominazione_studio' => 'nullable|string|max:255',
|
||||
'partita_iva' => 'nullable|string|max:20|unique:amministratori,partita_iva',
|
||||
'codice_fiscale_studio' => 'nullable|string|max:20',
|
||||
'indirizzo_studio' => 'nullable|string|max:255',
|
||||
'cap_studio' => 'nullable|string|max:10',
|
||||
'citta_studio' => 'nullable|string|max:60',
|
||||
'provincia_studio' => 'nullable|string|max:2',
|
||||
'telefono_studio' => 'nullable|string|max:20',
|
||||
'email_studio' => 'nullable|string|email|max:255',
|
||||
'pec_studio' => 'nullable|string|email|max:255',
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
$user->assignRole('admin'); // Assegna il ruolo 'admin' al nuovo utente per coerenza con le rotte
|
||||
|
||||
Amministratore::create([
|
||||
'user_id' => $user->id,
|
||||
'nome' => $request->nome,
|
||||
'cognome' => $request->cognome,
|
||||
'denominazione_studio' => $request->denominazione_studio,
|
||||
'partita_iva' => $request->partita_iva,
|
||||
'codice_fiscale_studio' => $request->codice_fiscale_studio,
|
||||
'indirizzo_studio' => $request->indirizzo_studio,
|
||||
'cap_studio' => $request->cap_studio,
|
||||
'citta_studio' => $request->citta_studio,
|
||||
'provincia_studio' => $request->provincia_studio,
|
||||
'telefono_studio' => $request->telefono_studio,
|
||||
'email_studio' => $request->email_studio,
|
||||
'pec_studio' => $request->pec_studio,
|
||||
]);
|
||||
|
||||
return redirect()->route('superadmin.amministratori.index')->with('success', 'Amministratore creato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Amministratore $amministratore) // Aggiunto metodo edit
|
||||
{
|
||||
// Gate::authorize('manage-amministratori'); // Il middleware nel costruttore è sufficiente
|
||||
// Recupera gli utenti che non sono ancora collegati a un record Amministratore
|
||||
$usersWithoutAdminRole = User::doesntHave('amministratore')->get();
|
||||
// Includi l'utente attualmente collegato a questo amministratore nella lista
|
||||
$usersWithoutAdminRole = $usersWithoutAdminRole->merge([$amministratore->user]);
|
||||
return view('superadmin.amministratori.edit', compact('amministratore', 'usersWithoutAdminRole'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Amministratore $amministratore)
|
||||
{
|
||||
// Gate::authorize('manage-amministratori'); // Il middleware nel costruttore è sufficiente
|
||||
$request->validate([
|
||||
'user_id' => 'required|exists:users,id|unique:amministratori,user_id,' . $amministratore->id_amministratore . ',id_amministratore',
|
||||
'nome' => 'required|string|max:255',
|
||||
'cognome' => 'required|string|max:255',
|
||||
'denominazione_studio' => 'nullable|string|max:255',
|
||||
'partita_iva' => ['nullable', 'string', 'max:20', Rule::unique('amministratori')->ignore($amministratore->id_amministratore, 'id_amministratore')], // Corretto id a id_amministratore
|
||||
'codice_fiscale_studio' => 'nullable|string|max:20',
|
||||
'indirizzo_studio' => 'nullable|string|max:255',
|
||||
'cap_studio' => 'nullable|string|max:10',
|
||||
'citta_studio' => 'nullable|string|max:255',
|
||||
'provincia_studio' => 'nullable|string|max:2',
|
||||
'telefono_studio' => 'nullable|string|max:20',
|
||||
'email_studio' => 'nullable|email|max:255',
|
||||
'pec_studio' => 'nullable|email|max:255',
|
||||
]);
|
||||
|
||||
// Aggiorna i dati dell'amministratore
|
||||
$amministratore->update($request->all());
|
||||
|
||||
return redirect()->route('superadmin.amministratori.index')->with('success', 'Amministratore aggiornato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Amministratore $amministratore)
|
||||
{
|
||||
// Gate::authorize('manage-amministratori'); // Il middleware nel costruttore è sufficiente
|
||||
$amministratore->user->delete(); // Elimina anche l'utente associato
|
||||
$amministratore->delete();
|
||||
return redirect()->route('superadmin.amministratori.index')->with('success', 'Amministratore eliminato con successo.');
|
||||
}
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# sb1-netgescon
|
||||
|
||||
[Edit in StackBlitz next generation editor ⚡️](https://stackblitz.com/~/github.com/Pikappa2/sb1-netgescon)
|
||||
528
app/Http/Controllers/Admin/AssembleaController.php
Normal file
528
app/Http/Controllers/Admin/AssembleaController.php
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Assemblea;
|
||||
use App\Models\OrdineGiorno;
|
||||
use App\Models\Convocazione;
|
||||
use App\Models\PresenzaAssemblea;
|
||||
use App\Models\Votazione;
|
||||
use App\Models\Verbale;
|
||||
use App\Models\RegistroProtocollo;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\Preventivo;
|
||||
use App\Models\TabellaMillesimale;
|
||||
use App\Models\Soggetto;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class AssembleaController extends Controller
|
||||
{
|
||||
/**
|
||||
* Dashboard assemblee
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$assemblee = Assemblea::with(['stabile', 'creatoDa'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->orderBy('data_prima_convocazione', 'desc')
|
||||
->paginate(15);
|
||||
|
||||
// Statistiche
|
||||
$stats = [
|
||||
'assemblee_programmate' => Assemblea::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->whereIn('stato', ['bozza', 'convocata'])->count(),
|
||||
|
||||
'assemblee_svolte' => Assemblea::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('stato', 'svolta')->count(),
|
||||
|
||||
'convocazioni_inviate' => Convocazione::whereHas('assemblea.stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('data_invio', '>=', now()->subDays(30))->count(),
|
||||
|
||||
'delibere_approvate' => OrdineGiorno::whereHas('assemblea.stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('esito_votazione', 'approvato')->count(),
|
||||
];
|
||||
|
||||
return view('admin.assemblee.index', compact('assemblee', 'stats'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form creazione assemblea
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
$stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get();
|
||||
|
||||
return view('admin.assemblee.create', compact('stabili'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store assemblea
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'tipo' => 'required|in:ordinaria,straordinaria',
|
||||
'data_prima_convocazione' => 'required|date|after:now',
|
||||
'data_seconda_convocazione' => 'required|date|after:data_prima_convocazione',
|
||||
'luogo' => 'required|string|max:255',
|
||||
'note' => 'nullable|string',
|
||||
'ordine_giorno' => 'required|array|min:1',
|
||||
'ordine_giorno.*.titolo' => 'required|string|max:255',
|
||||
'ordine_giorno.*.descrizione' => 'required|string',
|
||||
'ordine_giorno.*.tipo_voce' => 'required|in:discussione,delibera,spesa,preventivo,altro',
|
||||
'ordine_giorno.*.importo_spesa' => 'nullable|numeric|min:0',
|
||||
'ordine_giorno.*.tabella_millesimale_id' => 'nullable|exists:tabelle_millesimali,id',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$assemblea = Assemblea::create([
|
||||
'stabile_id' => $request->stabile_id,
|
||||
'tipo' => $request->tipo,
|
||||
'data_prima_convocazione' => $request->data_prima_convocazione,
|
||||
'data_seconda_convocazione' => $request->data_seconda_convocazione,
|
||||
'luogo' => $request->luogo,
|
||||
'note' => $request->note,
|
||||
'stato' => 'bozza',
|
||||
'creato_da_user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
// Crea ordine del giorno
|
||||
foreach ($request->ordine_giorno as $index => $punto) {
|
||||
OrdineGiorno::create([
|
||||
'assemblea_id' => $assemblea->id,
|
||||
'numero_punto' => $index + 1,
|
||||
'titolo' => $punto['titolo'],
|
||||
'descrizione' => $punto['descrizione'],
|
||||
'tipo_voce' => $punto['tipo_voce'],
|
||||
'importo_spesa' => $punto['importo_spesa'] ?? null,
|
||||
'tabella_millesimale_id' => $punto['tabella_millesimale_id'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return redirect()->route('admin.assemblee.show', $assemblea)
|
||||
->with('success', 'Assemblea creata con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore durante la creazione: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visualizza assemblea
|
||||
*/
|
||||
public function show(Assemblea $assemblea)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$assemblea->load([
|
||||
'stabile',
|
||||
'ordineGiorno.preventivo',
|
||||
'ordineGiorno.tabellaMillesimale',
|
||||
'convocazioni.soggetto',
|
||||
'presenze.soggetto',
|
||||
'verbale',
|
||||
'documenti'
|
||||
]);
|
||||
|
||||
// Calcola statistiche convocazioni
|
||||
$statsConvocazioni = [
|
||||
'totale_inviate' => $assemblea->convocazioni->count(),
|
||||
'consegnate' => $assemblea->convocazioni->where('esito_invio', 'consegnato')->count(),
|
||||
'lette' => $assemblea->convocazioni->where('esito_invio', 'letto')->count(),
|
||||
'conferme_presenza' => $assemblea->convocazioni->where('presenza_confermata', true)->count(),
|
||||
'deleghe' => $assemblea->convocazioni->where('delega_presente', true)->count(),
|
||||
];
|
||||
|
||||
// Calcola quorum se assemblea svolta
|
||||
$quorum = null;
|
||||
if ($assemblea->stato === 'svolta') {
|
||||
$quorum = $assemblea->calcolaQuorum();
|
||||
}
|
||||
|
||||
return view('admin.assemblee.show', compact('assemblea', 'statsConvocazioni', 'quorum'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia convocazioni
|
||||
*/
|
||||
public function inviaConvocazioni(Request $request, Assemblea $assemblea)
|
||||
{
|
||||
$request->validate([
|
||||
'canali' => 'required|array',
|
||||
'canali.*' => 'in:email,pec,whatsapp,telegram,raccomandata,mano,portiere',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if ($assemblea->stato !== 'bozza') {
|
||||
return back()->withErrors(['error' => 'Le convocazioni possono essere inviate solo per assemblee in bozza.']);
|
||||
}
|
||||
|
||||
try {
|
||||
$convocazioniInviate = $assemblea->inviaConvocazioni($request->canali, Auth::id());
|
||||
|
||||
return back()->with('success', "Inviate {$convocazioniInviate} convocazioni con successo.");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withErrors(['error' => 'Errore nell\'invio convocazioni: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestione presenze
|
||||
*/
|
||||
public function presenze(Assemblea $assemblea)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$unitaImmobiliari = $assemblea->stabile->unitaImmobiliari()
|
||||
->with(['proprieta.soggetto'])
|
||||
->get();
|
||||
|
||||
$presenzeEsistenti = $assemblea->presenze()
|
||||
->with(['soggetto', 'unitaImmobiliare'])
|
||||
->get()
|
||||
->keyBy(function($presenza) {
|
||||
return $presenza->soggetto_id . '_' . $presenza->unita_immobiliare_id;
|
||||
});
|
||||
|
||||
return view('admin.assemblee.presenze', compact('assemblea', 'unitaImmobiliari', 'presenzeEsistenti'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra presenza
|
||||
*/
|
||||
public function registraPresenza(Request $request, Assemblea $assemblea)
|
||||
{
|
||||
$request->validate([
|
||||
'presenze' => 'required|array',
|
||||
'presenze.*.soggetto_id' => 'required|exists:soggetti,id_soggetto',
|
||||
'presenze.*.unita_immobiliare_id' => 'required|exists:unita_immobiliari,id_unita',
|
||||
'presenze.*.tipo_presenza' => 'required|in:presente,delegato,assente',
|
||||
'presenze.*.millesimi_rappresentati' => 'required|numeric|min:0',
|
||||
'presenze.*.delegante_soggetto_id' => 'nullable|exists:soggetti,id_soggetto',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Elimina presenze esistenti
|
||||
$assemblea->presenze()->delete();
|
||||
|
||||
// Registra nuove presenze
|
||||
foreach ($request->presenze as $presenzaData) {
|
||||
if ($presenzaData['tipo_presenza'] !== 'assente') {
|
||||
PresenzaAssemblea::create([
|
||||
'assemblea_id' => $assemblea->id,
|
||||
'soggetto_id' => $presenzaData['soggetto_id'],
|
||||
'unita_immobiliare_id' => $presenzaData['unita_immobiliare_id'],
|
||||
'tipo_presenza' => $presenzaData['tipo_presenza'],
|
||||
'millesimi_rappresentati' => $presenzaData['millesimi_rappresentati'],
|
||||
'delegante_soggetto_id' => $presenzaData['delegante_soggetto_id'] ?? null,
|
||||
'ora_arrivo' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Aggiorna stato assemblea
|
||||
$assemblea->update(['stato' => 'svolta', 'data_svolgimento' => now()]);
|
||||
|
||||
DB::commit();
|
||||
return back()->with('success', 'Presenze registrate con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore nella registrazione presenze: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestione votazioni
|
||||
*/
|
||||
public function votazioni(Assemblea $assemblea, OrdineGiorno $ordineGiorno)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if ($assemblea->stato !== 'svolta') {
|
||||
return back()->withErrors(['error' => 'Le votazioni possono essere gestite solo per assemblee svolte.']);
|
||||
}
|
||||
|
||||
$presenze = $assemblea->presenze()->with(['soggetto', 'unitaImmobiliare'])->get();
|
||||
$votazioniEsistenti = $ordineGiorno->votazioni()
|
||||
->get()
|
||||
->keyBy(function($voto) {
|
||||
return $voto->soggetto_id . '_' . $voto->unita_immobiliare_id;
|
||||
});
|
||||
|
||||
return view('admin.assemblee.votazioni', compact('assemblea', 'ordineGiorno', 'presenze', 'votazioniEsistenti'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra votazioni
|
||||
*/
|
||||
public function registraVotazioni(Request $request, Assemblea $assemblea, OrdineGiorno $ordineGiorno)
|
||||
{
|
||||
$request->validate([
|
||||
'voti' => 'required|array',
|
||||
'voti.*.soggetto_id' => 'required|exists:soggetti,id_soggetto',
|
||||
'voti.*.unita_immobiliare_id' => 'required|exists:unita_immobiliari,id_unita',
|
||||
'voti.*.voto' => 'required|in:favorevole,contrario,astenuto,non_votante',
|
||||
'voti.*.millesimi_voto' => 'required|numeric|min:0',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Elimina votazioni esistenti
|
||||
$ordineGiorno->votazioni()->delete();
|
||||
|
||||
// Registra nuovi voti
|
||||
foreach ($request->voti as $votoData) {
|
||||
if ($votoData['voto'] !== 'non_votante') {
|
||||
Votazione::create([
|
||||
'ordine_giorno_id' => $ordineGiorno->id,
|
||||
'soggetto_id' => $votoData['soggetto_id'],
|
||||
'unita_immobiliare_id' => $votoData['unita_immobiliare_id'],
|
||||
'voto' => $votoData['voto'],
|
||||
'millesimi_voto' => $votoData['millesimi_voto'],
|
||||
'data_voto' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Calcola risultato
|
||||
$risultato = $ordineGiorno->calcolaRisultato();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return back()->with('success', 'Votazioni registrate. Esito: ' . $risultato['esito']);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore nella registrazione voti: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestione verbale
|
||||
*/
|
||||
public function verbale(Assemblea $assemblea)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$assemblea->load(['ordineGiorno.delibera', 'presenze.soggetto']);
|
||||
$verbale = $assemblea->verbale;
|
||||
|
||||
return view('admin.assemblee.verbale', compact('assemblea', 'verbale'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store/Update verbale
|
||||
*/
|
||||
public function storeVerbale(Request $request, Assemblea $assemblea)
|
||||
{
|
||||
$request->validate([
|
||||
'testo_verbale' => 'required|string',
|
||||
'allegati.*' => 'nullable|file|max:10240',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
try {
|
||||
$numeroVerbale = $this->generaNumeroVerbale($assemblea);
|
||||
|
||||
// Gestione allegati
|
||||
$allegati = [];
|
||||
if ($request->hasFile('allegati')) {
|
||||
foreach ($request->file('allegati') as $file) {
|
||||
$path = $file->store('verbali/allegati', 'public');
|
||||
$allegati[] = [
|
||||
'nome' => $file->getClientOriginalName(),
|
||||
'path' => $path,
|
||||
'size' => $file->getSize(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$verbale = Verbale::updateOrCreate(
|
||||
['assemblea_id' => $assemblea->id],
|
||||
[
|
||||
'numero_verbale' => $numeroVerbale,
|
||||
'testo_verbale' => $request->testo_verbale,
|
||||
'allegati' => $allegati,
|
||||
'data_redazione' => now(),
|
||||
'redatto_da_user_id' => Auth::id(),
|
||||
'stato' => 'definitivo',
|
||||
]
|
||||
);
|
||||
|
||||
return back()->with('success', 'Verbale salvato con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withErrors(['error' => 'Errore nel salvataggio verbale: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia verbale ai condomini
|
||||
*/
|
||||
public function inviaVerbale(Request $request, Assemblea $assemblea)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($assemblea->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$verbale = $assemblea->verbale;
|
||||
if (!$verbale) {
|
||||
return back()->withErrors(['error' => 'Nessun verbale da inviare.']);
|
||||
}
|
||||
|
||||
try {
|
||||
// Invia verbale a tutti i condomini
|
||||
$inviiRiusciti = $this->inviaVerbaleCondomini($assemblea, $verbale);
|
||||
|
||||
$verbale->update([
|
||||
'inviato_condomini' => true,
|
||||
'data_invio_condomini' => now(),
|
||||
'stato' => 'inviato',
|
||||
]);
|
||||
|
||||
return back()->with('success', "Verbale inviato a {$inviiRiusciti} condomini.");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withErrors(['error' => 'Errore nell\'invio verbale: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registro protocollo
|
||||
*/
|
||||
public function registroProtocollo(Request $request)
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$query = RegistroProtocollo::with(['assemblea.stabile', 'soggettoDestinatario', 'creatoDa'])
|
||||
->whereHas('assemblea.stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
});
|
||||
|
||||
// Filtri
|
||||
if ($request->filled('tipo_comunicazione')) {
|
||||
$query->where('tipo_comunicazione', $request->tipo_comunicazione);
|
||||
}
|
||||
|
||||
if ($request->filled('data_da')) {
|
||||
$query->where('data_invio', '>=', $request->data_da);
|
||||
}
|
||||
|
||||
if ($request->filled('data_a')) {
|
||||
$query->where('data_invio', '<=', $request->data_a);
|
||||
}
|
||||
|
||||
$comunicazioni = $query->orderBy('data_invio', 'desc')->paginate(20);
|
||||
|
||||
return view('admin.assemblee.registro-protocollo', compact('comunicazioni'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero verbale
|
||||
*/
|
||||
private function generaNumeroVerbale(Assemblea $assemblea)
|
||||
{
|
||||
$anno = $assemblea->data_prima_convocazione->year;
|
||||
$ultimoVerbale = Verbale::whereHas('assemblea', function($q) use ($anno) {
|
||||
$q->whereYear('data_prima_convocazione', $anno);
|
||||
})->orderBy('numero_verbale', 'desc')->first();
|
||||
|
||||
if ($ultimoVerbale) {
|
||||
$numero = intval(substr($ultimoVerbale->numero_verbale, -3)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return 'VERB/' . $anno . '/' . str_pad($numero, 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia verbale ai condomini
|
||||
*/
|
||||
private function inviaVerbaleCondomini(Assemblea $assemblea, Verbale $verbale)
|
||||
{
|
||||
$unitaImmobiliari = $assemblea->stabile->unitaImmobiliari()->with('proprieta.soggetto')->get();
|
||||
$inviiRiusciti = 0;
|
||||
|
||||
foreach ($unitaImmobiliari as $unita) {
|
||||
foreach ($unita->proprieta as $proprieta) {
|
||||
$soggetto = $proprieta->soggetto;
|
||||
|
||||
if ($soggetto->email) {
|
||||
// Simula invio email
|
||||
$numeroProtocollo = RegistroProtocollo::generaNumeroProtocollo();
|
||||
|
||||
RegistroProtocollo::create([
|
||||
'numero_protocollo' => $numeroProtocollo,
|
||||
'tipo_comunicazione' => 'verbale',
|
||||
'assemblea_id' => $assemblea->id,
|
||||
'soggetto_destinatario_id' => $soggetto->id_soggetto,
|
||||
'oggetto' => "Verbale Assemblea {$assemblea->tipo} del {$assemblea->data_prima_convocazione->format('d/m/Y')}",
|
||||
'contenuto' => $verbale->testo_verbale,
|
||||
'canale' => 'email',
|
||||
'data_invio' => now(),
|
||||
'esito' => 'inviato',
|
||||
'creato_da_user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
$inviiRiusciti++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $inviiRiusciti;
|
||||
}
|
||||
}
|
||||
386
app/Http/Controllers/Admin/BilancioController.php
Normal file
386
app/Http/Controllers/Admin/BilancioController.php
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Bilancio;
|
||||
use App\Models\ScritturaBilancio;
|
||||
use App\Models\Conguaglio;
|
||||
use App\Models\Quadratura;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\Gestione;
|
||||
use App\Models\PianoConto;
|
||||
use App\Models\MovimentoContabile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class BilancioController extends Controller
|
||||
{
|
||||
/**
|
||||
* Dashboard bilanci
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$bilanci = Bilancio::with(['stabile', 'gestione', 'approvatoDa'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->orderBy('anno_esercizio', 'desc')
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(15);
|
||||
|
||||
// Statistiche
|
||||
$stats = [
|
||||
'bilanci_aperti' => Bilancio::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->whereIn('stato', ['bozza', 'provvisorio'])->count(),
|
||||
|
||||
'bilanci_approvati' => Bilancio::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('stato', 'approvato')->count(),
|
||||
|
||||
'conguagli_da_pagare' => Conguaglio::whereHas('bilancio.stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('stato', 'calcolato')->count(),
|
||||
|
||||
'totale_avanzi' => Bilancio::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('risultato_gestione', '>', 0)->sum('risultato_gestione'),
|
||||
];
|
||||
|
||||
return view('admin.bilanci.index', compact('bilanci', 'stats'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form creazione bilancio
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
$stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get();
|
||||
|
||||
return view('admin.bilanci.create', compact('stabili'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store bilancio
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'gestione_id' => 'required|exists:gestioni,id_gestione',
|
||||
'anno_esercizio' => 'required|integer|min:2020|max:2030',
|
||||
'data_inizio_esercizio' => 'required|date',
|
||||
'data_fine_esercizio' => 'required|date|after:data_inizio_esercizio',
|
||||
'tipo_gestione' => 'required|in:ordinaria,riscaldamento,straordinaria,acqua,altro',
|
||||
'descrizione' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$bilancio = Bilancio::create([
|
||||
'stabile_id' => $request->stabile_id,
|
||||
'gestione_id' => $request->gestione_id,
|
||||
'anno_esercizio' => $request->anno_esercizio,
|
||||
'data_inizio_esercizio' => $request->data_inizio_esercizio,
|
||||
'data_fine_esercizio' => $request->data_fine_esercizio,
|
||||
'tipo_gestione' => $request->tipo_gestione,
|
||||
'descrizione' => $request->descrizione,
|
||||
'stato' => 'bozza',
|
||||
'versione' => 1,
|
||||
]);
|
||||
|
||||
// Importa movimenti contabili del periodo
|
||||
$this->importaMovimentiContabili($bilancio);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return redirect()->route('admin.bilanci.show', $bilancio)
|
||||
->with('success', 'Bilancio creato con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore durante la creazione: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visualizza bilancio
|
||||
*/
|
||||
public function show(Bilancio $bilancio)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($bilancio->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$bilancio->load([
|
||||
'stabile',
|
||||
'gestione',
|
||||
'scritture.dettagli.conto',
|
||||
'conguagli.unitaImmobiliare',
|
||||
'quadrature',
|
||||
'rimborsiAssicurativi'
|
||||
]);
|
||||
|
||||
// Calcola totali aggiornati
|
||||
$bilancio->calcolaTotali();
|
||||
|
||||
return view('admin.bilanci.show', compact('bilancio'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Importa movimenti contabili nel bilancio
|
||||
*/
|
||||
private function importaMovimentiContabili(Bilancio $bilancio)
|
||||
{
|
||||
$movimenti = MovimentoContabile::where('stabile_id', $bilancio->stabile_id)
|
||||
->where('gestione_id', $bilancio->gestione_id)
|
||||
->whereBetween('data_registrazione', [
|
||||
$bilancio->data_inizio_esercizio,
|
||||
$bilancio->data_fine_esercizio
|
||||
])
|
||||
->with('dettagli')
|
||||
->get();
|
||||
|
||||
foreach ($movimenti as $movimento) {
|
||||
$this->creaScritturaDaMovimento($bilancio, $movimento);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea scrittura bilancio da movimento contabile
|
||||
*/
|
||||
private function creaScritturaDaMovimento(Bilancio $bilancio, MovimentoContabile $movimento)
|
||||
{
|
||||
$scrittura = ScritturaBilancio::create([
|
||||
'bilancio_id' => $bilancio->id,
|
||||
'numero_scrittura' => $this->generaNumeroScrittura($bilancio),
|
||||
'data_scrittura' => $movimento->data_registrazione,
|
||||
'descrizione' => $movimento->descrizione,
|
||||
'tipo_scrittura' => 'gestione',
|
||||
'importo_totale' => $movimento->importo_totale,
|
||||
'movimento_contabile_id' => $movimento->id,
|
||||
'creato_da_user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
// Crea dettagli in partita doppia
|
||||
foreach ($movimento->dettagli as $dettaglio) {
|
||||
$scrittura->dettagli()->create([
|
||||
'conto_id' => $dettaglio->conto_id ?? $this->getContoDefault($movimento->tipo_movimento),
|
||||
'importo_dare' => $dettaglio->importo_dare,
|
||||
'importo_avere' => $dettaglio->importo_avere,
|
||||
'descrizione_dettaglio' => $dettaglio->descrizione,
|
||||
]);
|
||||
}
|
||||
|
||||
return $scrittura;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola conguagli
|
||||
*/
|
||||
public function calcolaConguagli(Bilancio $bilancio)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($bilancio->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$bilancio->calcolaConguagli();
|
||||
|
||||
DB::commit();
|
||||
return back()->with('success', 'Conguagli calcolati con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore nel calcolo conguagli: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera rate conguaglio
|
||||
*/
|
||||
public function generaRateConguaglio(Request $request, Bilancio $bilancio)
|
||||
{
|
||||
$request->validate([
|
||||
'conguaglio_ids' => 'required|array',
|
||||
'numero_rate' => 'required|integer|min:1|max:12',
|
||||
'data_inizio' => 'required|date',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($bilancio->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$dataInizio = Carbon::parse($request->data_inizio);
|
||||
$rateGenerate = 0;
|
||||
|
||||
foreach ($request->conguaglio_ids as $conguaglioId) {
|
||||
$conguaglio = Conguaglio::findOrFail($conguaglioId);
|
||||
|
||||
if ($conguaglio->bilancio_id !== $bilancio->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rate = $conguaglio->generaRate($request->numero_rate, $dataInizio, Auth::id());
|
||||
$rateGenerate += $rate->count();
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return back()->with('success', "Generate {$rateGenerate} rate di conguaglio.");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore nella generazione rate: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quadratura bilancio
|
||||
*/
|
||||
public function quadratura(Request $request, Bilancio $bilancio)
|
||||
{
|
||||
$request->validate([
|
||||
'data_quadratura' => 'required|date',
|
||||
'saldo_banca_effettivo' => 'required|numeric',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($bilancio->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
// Calcola saldo contabile
|
||||
$saldoContabile = $this->calcolaSaldoContabile($bilancio, $request->data_quadratura);
|
||||
|
||||
// Calcola totali crediti/debiti
|
||||
$totaleCrediti = $bilancio->conguagli()->where('tipo_conguaglio', 'a_credito')->sum('conguaglio_dovuto');
|
||||
$totaleDebiti = $bilancio->conguagli()->where('tipo_conguaglio', 'a_debito')->sum('conguaglio_dovuto');
|
||||
|
||||
// Calcola rate
|
||||
$totaleRateEmesse = $this->calcolaTotaleRateEmesse($bilancio);
|
||||
$totaleRateIncassate = $this->calcolaTotaleRateIncassate($bilancio);
|
||||
|
||||
$differenza = $request->saldo_banca_effettivo - $saldoContabile;
|
||||
|
||||
$quadratura = Quadratura::create([
|
||||
'bilancio_id' => $bilancio->id,
|
||||
'data_quadratura' => $request->data_quadratura,
|
||||
'saldo_banca_effettivo' => $request->saldo_banca_effettivo,
|
||||
'saldo_contabile_calcolato' => $saldoContabile,
|
||||
'differenza' => $differenza,
|
||||
'totale_crediti_condomini' => $totaleCrediti,
|
||||
'totale_debiti_condomini' => $totaleDebiti,
|
||||
'totale_rate_emesse' => $totaleRateEmesse,
|
||||
'totale_rate_incassate' => $totaleRateIncassate,
|
||||
'quadratura_ok' => abs($differenza) < 0.01,
|
||||
'verificato_da_user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Quadratura eseguita con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Chiusura esercizio
|
||||
*/
|
||||
public function chiusuraEsercizio(Request $request, Bilancio $bilancio)
|
||||
{
|
||||
$request->validate([
|
||||
'motivo_chiusura' => 'required|string',
|
||||
]);
|
||||
|
||||
// Verifica accesso e stato
|
||||
if ($bilancio->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if ($bilancio->stato !== 'approvato') {
|
||||
return back()->withErrors(['error' => 'Il bilancio deve essere approvato prima della chiusura.']);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Genera scritture di chiusura
|
||||
$bilancio->generaScritture ChiusuraEsercizio(Auth::id());
|
||||
|
||||
// Aggiorna stato bilancio
|
||||
$bilancio->update([
|
||||
'stato' => 'chiuso',
|
||||
'data_chiusura' => now(),
|
||||
'chiuso_da_user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
return back()->with('success', 'Esercizio chiuso con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore nella chiusura: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola saldo contabile alla data
|
||||
*/
|
||||
private function calcolaSaldoContabile(Bilancio $bilancio, $data)
|
||||
{
|
||||
// Implementazione calcolo saldo contabile
|
||||
return $bilancio->totale_entrate - $bilancio->totale_uscite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola totale rate emesse
|
||||
*/
|
||||
private function calcolaTotaleRateEmesse(Bilancio $bilancio)
|
||||
{
|
||||
// Implementazione calcolo rate emesse
|
||||
return 0; // Placeholder
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola totale rate incassate
|
||||
*/
|
||||
private function calcolaTotaleRateIncassate(Bilancio $bilancio)
|
||||
{
|
||||
// Implementazione calcolo rate incassate
|
||||
return 0; // Placeholder
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero scrittura
|
||||
*/
|
||||
private function generaNumeroScrittura(Bilancio $bilancio)
|
||||
{
|
||||
$ultimaScrittura = ScritturaBilancio::where('bilancio_id', $bilancio->id)
|
||||
->orderBy('numero_scrittura', 'desc')
|
||||
->first();
|
||||
|
||||
if ($ultimaScrittura) {
|
||||
$numero = intval(substr($ultimaScrittura->numero_scrittura, -4)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return 'SCR/' . $bilancio->anno_esercizio . '/' . str_pad($numero, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get conto default per tipo movimento
|
||||
*/
|
||||
private function getContoDefault($tipoMovimento)
|
||||
{
|
||||
// Implementazione per ottenere conto default
|
||||
return 1; // Placeholder
|
||||
}
|
||||
}
|
||||
273
app/Http/Controllers/Admin/ContabilitaController.php
Normal file
273
app/Http/Controllers/Admin/ContabilitaController.php
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\MovimentoContabile;
|
||||
use App\Models\Gestione;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\Fornitore;
|
||||
use App\Models\VoceSpesa;
|
||||
use App\Models\TabellaMillesimale;
|
||||
use App\Models\Documento;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ContabilitaController extends Controller
|
||||
{
|
||||
/**
|
||||
* Dashboard contabilità
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
// Statistiche generali
|
||||
$stats = [
|
||||
'movimenti_mese' => MovimentoContabile::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->whereMonth('data_registrazione', now()->month)->count(),
|
||||
|
||||
'importo_entrate_mese' => MovimentoContabile::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('tipo_movimento', 'entrata')
|
||||
->whereMonth('data_registrazione', now()->month)
|
||||
->sum('importo_totale'),
|
||||
|
||||
'importo_uscite_mese' => MovimentoContabile::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('tipo_movimento', 'uscita')
|
||||
->whereMonth('data_registrazione', now()->month)
|
||||
->sum('importo_totale'),
|
||||
];
|
||||
|
||||
// Ultimi movimenti
|
||||
$ultimiMovimenti = MovimentoContabile::with(['stabile', 'gestione', 'fornitore'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(10)
|
||||
->get();
|
||||
|
||||
return view('admin.contabilita.index', compact('stats', 'ultimiMovimenti'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista movimenti contabili
|
||||
*/
|
||||
public function movimenti(Request $request)
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$query = MovimentoContabile::with(['stabile', 'gestione', 'fornitore'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
});
|
||||
|
||||
// Filtri
|
||||
if ($request->filled('stabile_id')) {
|
||||
$query->where('stabile_id', $request->stabile_id);
|
||||
}
|
||||
|
||||
if ($request->filled('gestione_id')) {
|
||||
$query->where('gestione_id', $request->gestione_id);
|
||||
}
|
||||
|
||||
if ($request->filled('tipo_movimento')) {
|
||||
$query->where('tipo_movimento', $request->tipo_movimento);
|
||||
}
|
||||
|
||||
if ($request->filled('data_da')) {
|
||||
$query->where('data_registrazione', '>=', $request->data_da);
|
||||
}
|
||||
|
||||
if ($request->filled('data_a')) {
|
||||
$query->where('data_registrazione', '<=', $request->data_a);
|
||||
}
|
||||
|
||||
$movimenti = $query->orderBy('data_registrazione', 'desc')->paginate(20);
|
||||
|
||||
// Dati per i filtri
|
||||
$stabili = Stabile::where('amministratore_id', $amministratore_id)->get();
|
||||
$gestioni = Gestione::whereIn('stabile_id', $stabili->pluck('id_stabile'))->get();
|
||||
|
||||
return view('admin.contabilita.movimenti', compact('movimenti', 'stabili', 'gestioni'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form registrazione movimento
|
||||
*/
|
||||
public function registrazione()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get();
|
||||
$fornitori = Fornitore::where('amministratore_id', $amministratore_id)->get();
|
||||
|
||||
return view('admin.contabilita.registrazione', compact('stabili', 'fornitori'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store registrazione movimento
|
||||
*/
|
||||
public function storeRegistrazione(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'gestione_id' => 'required|exists:gestioni,id_gestione',
|
||||
'tipo_movimento' => 'required|in:entrata,uscita',
|
||||
'data_documento' => 'required|date',
|
||||
'numero_documento' => 'required|string|max:50',
|
||||
'descrizione' => 'required|string|max:255',
|
||||
'importo_totale' => 'required|numeric|min:0',
|
||||
'fornitore_id' => 'nullable|exists:fornitori,id_fornitore',
|
||||
'ritenuta_acconto' => 'nullable|numeric|min:0',
|
||||
'dettagli' => 'required|array|min:1',
|
||||
'dettagli.*.voce_spesa_id' => 'required|exists:voci_spesa,id',
|
||||
'dettagli.*.importo' => 'required|numeric|min:0',
|
||||
'dettagli.*.tabella_millesimale_id' => 'nullable|exists:tabelle_millesimali,id',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Genera protocollo univoco
|
||||
$protocollo = $this->generaProtocollo($request->stabile_id);
|
||||
|
||||
// Crea movimento principale
|
||||
$movimento = MovimentoContabile::create([
|
||||
'stabile_id' => $request->stabile_id,
|
||||
'gestione_id' => $request->gestione_id,
|
||||
'fornitore_id' => $request->fornitore_id,
|
||||
'protocollo' => $protocollo,
|
||||
'data_registrazione' => now(),
|
||||
'data_documento' => $request->data_documento,
|
||||
'numero_documento' => $request->numero_documento,
|
||||
'descrizione' => $request->descrizione,
|
||||
'tipo_movimento' => $request->tipo_movimento,
|
||||
'importo_totale' => $request->importo_totale,
|
||||
'ritenuta_acconto' => $request->ritenuta_acconto ?? 0,
|
||||
'importo_netto' => $request->importo_totale - ($request->ritenuta_acconto ?? 0),
|
||||
'stato' => 'registrato',
|
||||
]);
|
||||
|
||||
// Crea dettagli movimento (partita doppia)
|
||||
foreach ($request->dettagli as $dettaglio) {
|
||||
$movimento->dettagli()->create([
|
||||
'voce_spesa_id' => $dettaglio['voce_spesa_id'],
|
||||
'tabella_millesimale_id' => $dettaglio['tabella_millesimale_id'] ?? null,
|
||||
'descrizione' => $dettaglio['descrizione'] ?? '',
|
||||
'importo_dare' => $request->tipo_movimento === 'uscita' ? $dettaglio['importo'] : 0,
|
||||
'importo_avere' => $request->tipo_movimento === 'entrata' ? $dettaglio['importo'] : 0,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return redirect()->route('admin.contabilita.movimenti')
|
||||
->with('success', 'Movimento registrato con successo. Protocollo: ' . $protocollo);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore durante la registrazione: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import da XML (Fattura Elettronica)
|
||||
*/
|
||||
public function importXml(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'xml_file' => 'required|file|mimes:xml|max:2048',
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
]);
|
||||
|
||||
try {
|
||||
$xmlContent = file_get_contents($request->file('xml_file')->path());
|
||||
$xml = simplexml_load_string($xmlContent);
|
||||
|
||||
// Parsing XML fattura elettronica
|
||||
$fatturaData = $this->parseXmlFattura($xml);
|
||||
|
||||
// Salva documento
|
||||
$documento = Documento::create([
|
||||
'documentable_type' => Stabile::class,
|
||||
'documentable_id' => $request->stabile_id,
|
||||
'nome_file' => $request->file('xml_file')->getClientOriginalName(),
|
||||
'path_file' => $request->file('xml_file')->store('documenti/xml'),
|
||||
'tipo_documento' => 'fattura_elettronica',
|
||||
'xml_data' => $fatturaData,
|
||||
'mime_type' => 'application/xml',
|
||||
'dimensione_file' => $request->file('xml_file')->getSize(),
|
||||
]);
|
||||
|
||||
return view('admin.contabilita.import-xml-review', compact('fatturaData', 'documento'));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()->withErrors(['error' => 'Errore durante l\'importazione XML: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera protocollo univoco
|
||||
*/
|
||||
private function generaProtocollo($stabile_id)
|
||||
{
|
||||
$anno = date('Y');
|
||||
$ultimoProtocollo = MovimentoContabile::where('stabile_id', $stabile_id)
|
||||
->whereYear('data_registrazione', $anno)
|
||||
->max('protocollo');
|
||||
|
||||
if ($ultimoProtocollo) {
|
||||
$numero = intval(substr($ultimoProtocollo, -4)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return $stabile_id . '/' . $anno . '/' . str_pad($numero, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse XML Fattura Elettronica
|
||||
*/
|
||||
private function parseXmlFattura($xml)
|
||||
{
|
||||
$ns = $xml->getNamespaces(true);
|
||||
$xml->registerXPathNamespace('fe', $ns['']);
|
||||
|
||||
// Dati generali fattura
|
||||
$datiGenerali = [
|
||||
'numero' => (string) $xml->xpath('//DatiGeneraliDocumento/Numero')[0] ?? '',
|
||||
'data' => (string) $xml->xpath('//DatiGeneraliDocumento/Data')[0] ?? '',
|
||||
'importo_totale' => (float) $xml->xpath('//DatiGeneraliDocumento/ImportoTotaleDocumento')[0] ?? 0,
|
||||
];
|
||||
|
||||
// Dati fornitore
|
||||
$fornitore = [
|
||||
'denominazione' => (string) $xml->xpath('//CedentePrestatore//Denominazione')[0] ?? '',
|
||||
'partita_iva' => (string) $xml->xpath('//CedentePrestatore//IdFiscaleIVA/IdCodice')[0] ?? '',
|
||||
'codice_fiscale' => (string) $xml->xpath('//CedentePrestatore//CodiceFiscale')[0] ?? '',
|
||||
];
|
||||
|
||||
// Righe fattura
|
||||
$righe = [];
|
||||
$dettaglioLinee = $xml->xpath('//DettaglioLinee');
|
||||
foreach ($dettaglioLinee as $linea) {
|
||||
$righe[] = [
|
||||
'descrizione' => (string) $linea->Descrizione ?? '',
|
||||
'quantita' => (float) $linea->Quantita ?? 1,
|
||||
'prezzo_unitario' => (float) $linea->PrezzoUnitario ?? 0,
|
||||
'importo_totale' => (float) $linea->PrezzoTotale ?? 0,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'dati_generali' => $datiGenerali,
|
||||
'fornitore' => $fornitore,
|
||||
'righe' => $righe,
|
||||
];
|
||||
}
|
||||
}
|
||||
73
app/Http/Controllers/Admin/DashboardController.php
Normal file
73
app/Http/Controllers/Admin/DashboardController.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\Rata;
|
||||
use App\Models\Documento;
|
||||
use App\Models\MovimentoContabile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
// Statistiche principali
|
||||
$stats = [
|
||||
'stabili_gestiti' => Stabile::where('amministratore_id', $amministratore_id)->count(),
|
||||
'stabili_attivi' => Stabile::where('amministratore_id', $amministratore_id)->where('stato', 'attivo')->count(),
|
||||
'ticket_aperti' => Ticket::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione'])->count(),
|
||||
'ticket_urgenti' => Ticket::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('priorita', 'Urgente')->whereIn('stato', ['Aperto', 'Preso in Carico'])->count(),
|
||||
];
|
||||
|
||||
// Ticket aperti da lavorare
|
||||
$ticketsAperti = Ticket::with(['stabile', 'categoriaTicket'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione'])
|
||||
->orderBy('priorita', 'desc')
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// Scadenze imminenti (prossimi 30 giorni)
|
||||
$scadenzeImminenti = collect(); // Placeholder per quando implementeremo le rate
|
||||
|
||||
// Ultimi documenti caricati
|
||||
$ultimiDocumenti = Documento::with('documentable')
|
||||
->whereHasMorph('documentable', [Stabile::class], function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// Movimenti contabili recenti
|
||||
$ultimiMovimenti = MovimentoContabile::with(['stabile', 'fornitore'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
return view('admin.dashboard', compact(
|
||||
'stats',
|
||||
'ticketsAperti',
|
||||
'scadenzeImminenti',
|
||||
'ultimiDocumenti',
|
||||
'ultimiMovimenti'
|
||||
));
|
||||
}
|
||||
}
|
||||
150
app/Http/Controllers/Admin/DocumentoController.php
Normal file
150
app/Http/Controllers/Admin/DocumentoController.php
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Documento;
|
||||
use App\Models\Stabile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DocumentoController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$query = Documento::with('documentable')
|
||||
->whereHasMorph('documentable', [Stabile::class], function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
});
|
||||
|
||||
// Filtri
|
||||
if ($request->filled('tipo_documento')) {
|
||||
$query->where('tipo_documento', $request->tipo_documento);
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where(function($q) use ($request) {
|
||||
$q->where('nome_file', 'like', '%' . $request->search . '%')
|
||||
->orWhere('descrizione', 'like', '%' . $request->search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
$documenti = $query->orderBy('created_at', 'desc')->paginate(20);
|
||||
|
||||
// Tipi documento per filtro
|
||||
$tipiDocumento = Documento::whereHasMorph('documentable', [Stabile::class], function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->distinct()->pluck('tipo_documento')->filter();
|
||||
|
||||
return view('admin.documenti.index', compact('documenti', 'tipiDocumento'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
$stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get();
|
||||
|
||||
return view('admin.documenti.create', compact('stabili'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'documentable_type' => 'required|string',
|
||||
'documentable_id' => 'required|integer',
|
||||
'file' => 'required|file|max:10240', // 10MB max
|
||||
'tipo_documento' => 'required|string|max:100',
|
||||
'descrizione' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$file = $request->file('file');
|
||||
$path = $file->store('documenti', 'public');
|
||||
|
||||
Documento::create([
|
||||
'documentable_type' => $request->documentable_type,
|
||||
'documentable_id' => $request->documentable_id,
|
||||
'nome_file' => $file->getClientOriginalName(),
|
||||
'path_file' => $path,
|
||||
'tipo_documento' => $request->tipo_documento,
|
||||
'descrizione' => $request->descrizione,
|
||||
'mime_type' => $file->getMimeType(),
|
||||
'dimensione_file' => $file->getSize(),
|
||||
'hash_file' => hash_file('sha256', $file->path()),
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.documenti.index')
|
||||
->with('success', 'Documento caricato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Documento $documento)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($documento->documentable_type === Stabile::class) {
|
||||
$stabile = $documento->documentable;
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
return view('admin.documenti.show', compact('documento'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download del documento
|
||||
*/
|
||||
public function download(Documento $documento)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($documento->documentable_type === Stabile::class) {
|
||||
$stabile = $documento->documentable;
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Storage::disk('public')->exists($documento->path_file)) {
|
||||
abort(404, 'File non trovato');
|
||||
}
|
||||
|
||||
return Storage::disk('public')->download($documento->path_file, $documento->nome_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Documento $documento)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($documento->documentable_type === Stabile::class) {
|
||||
$stabile = $documento->documentable;
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
// Elimina il file fisico
|
||||
if (Storage::disk('public')->exists($documento->path_file)) {
|
||||
Storage::disk('public')->delete($documento->path_file);
|
||||
}
|
||||
|
||||
$documento->delete();
|
||||
|
||||
return redirect()->route('admin.documenti.index')
|
||||
->with('success', 'Documento eliminato con successo.');
|
||||
}
|
||||
}
|
||||
140
app/Http/Controllers/Admin/FornitoreController.php
Normal file
140
app/Http/Controllers/Admin/FornitoreController.php
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Fornitore;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class FornitoreController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$fornitori = Fornitore::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
|
||||
->paginate(10);
|
||||
|
||||
return view('admin.fornitori.index', compact('fornitori'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('admin.fornitori.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ragione_sociale' => 'required|string|max:255',
|
||||
'partita_iva' => 'nullable|string|max:20|unique:fornitori,partita_iva',
|
||||
'codice_fiscale' => 'nullable|string|max:20|unique:fornitori,codice_fiscale',
|
||||
'indirizzo' => 'nullable|string|max:255',
|
||||
'cap' => 'nullable|string|max:10',
|
||||
'citta' => 'nullable|string|max:60',
|
||||
'provincia' => 'nullable|string|max:2',
|
||||
'email' => 'nullable|email|max:255|unique:fornitori,email',
|
||||
'pec' => 'nullable|email|max:255',
|
||||
'telefono' => 'nullable|string|max:50',
|
||||
'old_id' => 'nullable|integer|unique:fornitori,old_id',
|
||||
]);
|
||||
|
||||
$fornitore = Fornitore::create([
|
||||
'amministratore_id' => Auth::user()->amministratore->id_amministratore ?? null,
|
||||
'ragione_sociale' => $request->ragione_sociale,
|
||||
'partita_iva' => $request->partita_iva,
|
||||
'codice_fiscale' => $request->codice_fiscale,
|
||||
'indirizzo' => $request->indirizzo,
|
||||
'cap' => $request->cap,
|
||||
'citta' => $request->citta,
|
||||
'provincia' => $request->provincia,
|
||||
'email' => $request->email,
|
||||
'pec' => $request->pec,
|
||||
'telefono' => $request->telefono,
|
||||
'old_id' => $request->old_id,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.fornitori.index')
|
||||
->with('success', 'Fornitore creato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Fornitore $fornitore)
|
||||
{
|
||||
// Verifica che l'utente possa accedere a questo fornitore
|
||||
if ($fornitore->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('admin.fornitori.show', compact('fornitore'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Fornitore $fornitore)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questo fornitore
|
||||
if ($fornitore->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('admin.fornitori.edit', compact('fornitore'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Fornitore $fornitore)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questo fornitore
|
||||
if ($fornitore->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'ragione_sociale' => 'required|string|max:255',
|
||||
'partita_iva' => 'nullable|string|max:20|unique:fornitori,partita_iva,' . $fornitore->id_fornitore . ',id_fornitore',
|
||||
'codice_fiscale' => 'nullable|string|max:20|unique:fornitori,codice_fiscale,' . $fornitore->id_fornitore . ',id_fornitore',
|
||||
'indirizzo' => 'nullable|string|max:255',
|
||||
'cap' => 'nullable|string|max:10',
|
||||
'citta' => 'nullable|string|max:60',
|
||||
'provincia' => 'nullable|string|max:2',
|
||||
'email' => 'nullable|email|max:255|unique:fornitori,email,' . $fornitore->id_fornitore . ',id_fornitore',
|
||||
'pec' => 'nullable|email|max:255',
|
||||
'telefono' => 'nullable|string|max:50',
|
||||
'old_id' => 'nullable|integer|unique:fornitori,old_id,' . $fornitore->id_fornitore . ',id_fornitore',
|
||||
]);
|
||||
|
||||
$fornitore->update($request->all());
|
||||
|
||||
return redirect()->route('admin.fornitori.index')
|
||||
->with('success', 'Fornitore aggiornato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Fornitore $fornitore)
|
||||
{
|
||||
// Verifica che l'utente possa eliminare questo fornitore
|
||||
if ($fornitore->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$fornitore->delete();
|
||||
|
||||
return redirect()->route('admin.fornitori.index')
|
||||
->with('success', 'Fornitore eliminato con successo.');
|
||||
}
|
||||
}
|
||||
289
app/Http/Controllers/Admin/PreventivoController.php
Normal file
289
app/Http/Controllers/Admin/PreventivoController.php
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Preventivo;
|
||||
use App\Models\VocePreventivo;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\TabellaMillesimale;
|
||||
use App\Models\VoceSpesa;
|
||||
use App\Models\LogModificaPreventivo;
|
||||
use App\Models\PianificazioneSpesa;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class PreventivoController extends Controller
|
||||
{
|
||||
/**
|
||||
* Dashboard preventivi
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
$preventivi = Preventivo::with(['stabile', 'approvatoDa'])
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->orderBy('anno_gestione', 'desc')
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(15);
|
||||
|
||||
// Statistiche
|
||||
$stats = [
|
||||
'preventivi_bozza' => Preventivo::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('stato', 'bozza')->count(),
|
||||
|
||||
'preventivi_approvati' => Preventivo::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('stato', 'approvato')->count(),
|
||||
|
||||
'importo_totale_anno' => Preventivo::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})->where('anno_gestione', date('Y'))->sum('importo_totale'),
|
||||
];
|
||||
|
||||
return view('admin.preventivi.index', compact('preventivi', 'stats'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form creazione preventivo
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
$stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get();
|
||||
|
||||
return view('admin.preventivi.create', compact('stabili'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store preventivo
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'anno_gestione' => 'required|integer|min:2020|max:2030',
|
||||
'tipo_gestione' => 'required|in:ordinaria,riscaldamento,straordinaria,acqua,altro',
|
||||
'descrizione' => 'required|string|max:255',
|
||||
'voci' => 'required|array|min:1',
|
||||
'voci.*.codice' => 'required|string|max:20',
|
||||
'voci.*.descrizione' => 'required|string|max:255',
|
||||
'voci.*.importo' => 'required|numeric|min:0',
|
||||
'voci.*.tabella_millesimale_id' => 'nullable|exists:tabelle_millesimali,id',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$preventivo = Preventivo::create([
|
||||
'stabile_id' => $request->stabile_id,
|
||||
'anno_gestione' => $request->anno_gestione,
|
||||
'tipo_gestione' => $request->tipo_gestione,
|
||||
'descrizione' => $request->descrizione,
|
||||
'stato' => 'bozza',
|
||||
'data_creazione' => now(),
|
||||
'versione' => 1,
|
||||
]);
|
||||
|
||||
$importoTotale = 0;
|
||||
foreach ($request->voci as $index => $voceData) {
|
||||
$voce = VocePreventivo::create([
|
||||
'preventivo_id' => $preventivo->id,
|
||||
'codice' => $voceData['codice'],
|
||||
'descrizione' => $voceData['descrizione'],
|
||||
'importo_preventivato' => $voceData['importo'],
|
||||
'tabella_millesimale_id' => $voceData['tabella_millesimale_id'] ?? null,
|
||||
'ordinamento' => $index + 1,
|
||||
]);
|
||||
|
||||
// Calcola ripartizione se specificata tabella millesimale
|
||||
if ($voce->tabella_millesimale_id) {
|
||||
$voce->calcolaRipartizione();
|
||||
}
|
||||
|
||||
$importoTotale += $voceData['importo'];
|
||||
}
|
||||
|
||||
$preventivo->update(['importo_totale' => $importoTotale]);
|
||||
|
||||
// Log creazione
|
||||
LogModificaPreventivo::create([
|
||||
'entita' => 'preventivo',
|
||||
'entita_id' => $preventivo->id,
|
||||
'versione_precedente' => 0,
|
||||
'versione_nuova' => 1,
|
||||
'utente_id' => Auth::id(),
|
||||
'tipo_operazione' => 'create',
|
||||
'motivo' => 'Creazione preventivo',
|
||||
'dati_nuovi' => $preventivo->toArray(),
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return redirect()->route('admin.preventivi.show', $preventivo)
|
||||
->with('success', 'Preventivo creato con successo.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return back()->withErrors(['error' => 'Errore durante la creazione: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visualizza preventivo
|
||||
*/
|
||||
public function show(Preventivo $preventivo)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$preventivo->load([
|
||||
'stabile',
|
||||
'voci.tabellaMillesimale',
|
||||
'voci.ripartizioni.unitaImmobiliare',
|
||||
'rate',
|
||||
'logModifiche.utente'
|
||||
]);
|
||||
|
||||
return view('admin.preventivi.show', compact('preventivo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Approva preventivo
|
||||
*/
|
||||
public function approva(Request $request, Preventivo $preventivo)
|
||||
{
|
||||
$request->validate([
|
||||
'motivo' => 'required|string',
|
||||
]);
|
||||
|
||||
// Verifica accesso
|
||||
if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$preventivo->creaVersione($request->motivo, Auth::id());
|
||||
|
||||
$preventivo->update([
|
||||
'stato' => 'approvato',
|
||||
'data_approvazione' => now(),
|
||||
'approvato_da_user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Preventivo approvato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera rate dal preventivo
|
||||
*/
|
||||
public function generaRate(Request $request, Preventivo $preventivo)
|
||||
{
|
||||
$request->validate([
|
||||
'numero_rate' => 'required|integer|min:1|max:12',
|
||||
'data_inizio' => 'required|date',
|
||||
]);
|
||||
|
||||
// Verifica accesso e stato
|
||||
if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if ($preventivo->stato !== 'approvato') {
|
||||
return back()->withErrors(['error' => 'Il preventivo deve essere approvato prima di generare le rate.']);
|
||||
}
|
||||
|
||||
$dataInizio = Carbon::parse($request->data_inizio);
|
||||
$rate = $preventivo->generaRate($request->numero_rate, $dataInizio, Auth::id());
|
||||
|
||||
return back()->with('success', 'Generate ' . count($rate) . ' rate con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard pianificazione
|
||||
*/
|
||||
public function pianificazione()
|
||||
{
|
||||
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
|
||||
|
||||
// Spese in scadenza
|
||||
$speseInScadenza = PianificazioneSpesa::with('stabile')
|
||||
->whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->where('data_scadenza_prevista', '<=', now()->addDays(30))
|
||||
->where('stato', 'pianificata')
|
||||
->orderBy('data_scadenza_prevista')
|
||||
->get();
|
||||
|
||||
// Cashflow previsto (prossimi 6 mesi)
|
||||
$cashflow = $this->calcolaCashflow($amministratore_id);
|
||||
|
||||
return view('admin.preventivi.pianificazione', compact('speseInScadenza', 'cashflow'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola cashflow previsto
|
||||
*/
|
||||
private function calcolaCashflow($amministratore_id)
|
||||
{
|
||||
$mesi = [];
|
||||
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$dataInizio = now()->startOfMonth()->addMonths($i);
|
||||
$dataFine = $dataInizio->copy()->endOfMonth();
|
||||
|
||||
// Entrate previste (rate)
|
||||
$entrate = DB::table('rate')
|
||||
->join('preventivi', 'rate.preventivo_id', '=', 'preventivi.id')
|
||||
->join('stabili', 'preventivi.stabile_id', '=', 'stabili.id_stabile')
|
||||
->where('stabili.amministratore_id', $amministratore_id)
|
||||
->whereBetween('rate.data_scadenza', [$dataInizio, $dataFine])
|
||||
->sum('rate.importo_totale');
|
||||
|
||||
// Uscite previste (spese pianificate)
|
||||
$uscite = PianificazioneSpesa::whereHas('stabile', function($q) use ($amministratore_id) {
|
||||
$q->where('amministratore_id', $amministratore_id);
|
||||
})
|
||||
->whereBetween('data_scadenza_prevista', [$dataInizio, $dataFine])
|
||||
->sum('importo_previsto');
|
||||
|
||||
$mesi[] = [
|
||||
'mese' => $dataInizio->format('M Y'),
|
||||
'entrate' => $entrate,
|
||||
'uscite' => $uscite,
|
||||
'saldo' => $entrate - $uscite,
|
||||
];
|
||||
}
|
||||
|
||||
return $mesi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Storico modifiche (stile GIT)
|
||||
*/
|
||||
public function storicoModifiche(Preventivo $preventivo)
|
||||
{
|
||||
// Verifica accesso
|
||||
if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$modifiche = LogModificaPreventivo::with('utente')
|
||||
->where(function($q) use ($preventivo) {
|
||||
$q->where('entita', 'preventivo')->where('entita_id', $preventivo->id)
|
||||
->orWhereIn('entita_id', $preventivo->voci->pluck('id'))
|
||||
->where('entita', 'voce');
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(20);
|
||||
|
||||
return view('admin.preventivi.storico', compact('preventivo', 'modifiche'));
|
||||
}
|
||||
}
|
||||
137
app/Http/Controllers/Admin/StabileController.php
Normal file
137
app/Http/Controllers/Admin/StabileController.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Stabile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class StabileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
|
||||
->paginate(10);
|
||||
|
||||
return view('admin.stabili.index', compact('stabili'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('admin.stabili.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'denominazione' => 'required|string|max:255',
|
||||
'codice_fiscale' => 'nullable|string|max:20|unique:stabili,codice_fiscale',
|
||||
'cod_fisc_amministratore' => 'nullable|string|max:20',
|
||||
'indirizzo' => 'required|string|max:255',
|
||||
'citta' => 'required|string|max:255',
|
||||
'cap' => 'required|string|max:10',
|
||||
'provincia' => 'nullable|string|max:2',
|
||||
'stato' => 'required|in:attivo,inattivo',
|
||||
'note' => 'nullable|string',
|
||||
'old_id' => 'nullable|integer|unique:stabili,old_id',
|
||||
]);
|
||||
|
||||
$stabile = Stabile::create([
|
||||
'amministratore_id' => Auth::user()->amministratore->id_amministratore ?? null,
|
||||
'denominazione' => $request->denominazione,
|
||||
'codice_fiscale' => $request->codice_fiscale,
|
||||
'cod_fisc_amministratore' => $request->cod_fisc_amministratore,
|
||||
'indirizzo' => $request->indirizzo,
|
||||
'citta' => $request->citta,
|
||||
'cap' => $request->cap,
|
||||
'provincia' => $request->provincia,
|
||||
'stato' => $request->stato,
|
||||
'note' => $request->note,
|
||||
'old_id' => $request->old_id,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.stabili.index')
|
||||
->with('success', 'Stabile creato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Stabile $stabile)
|
||||
{
|
||||
// Verifica che l'utente possa accedere a questo stabile
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('admin.stabili.show', compact('stabile'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Stabile $stabile)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questo stabile
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('admin.stabili.edit', compact('stabile'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Stabile $stabile)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questo stabile
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'denominazione' => 'required|string|max:255',
|
||||
'codice_fiscale' => 'nullable|string|max:20|unique:stabili,codice_fiscale,' . $stabile->id_stabile . ',id_stabile',
|
||||
'cod_fisc_amministratore' => 'nullable|string|max:20',
|
||||
'indirizzo' => 'required|string|max:255',
|
||||
'citta' => 'required|string|max:255',
|
||||
'cap' => 'required|string|max:10',
|
||||
'provincia' => 'nullable|string|max:2',
|
||||
'stato' => 'required|in:attivo,inattivo',
|
||||
'note' => 'nullable|string',
|
||||
'old_id' => 'nullable|integer|unique:stabili,old_id,' . $stabile->id_stabile . ',id_stabile',
|
||||
]);
|
||||
|
||||
$stabile->update($request->all());
|
||||
|
||||
return redirect()->route('admin.stabili.index')
|
||||
->with('success', 'Stabile aggiornato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Stabile $stabile)
|
||||
{
|
||||
// Verifica che l'utente possa eliminare questo stabile
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$stabile->delete();
|
||||
|
||||
return redirect()->route('admin.stabili.index')
|
||||
->with('success', 'Stabile eliminato con successo.');
|
||||
}
|
||||
}
|
||||
170
app/Http/Controllers/Admin/TicketController.php
Normal file
170
app/Http/Controllers/Admin/TicketController.php
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\CategoriaTicket;
|
||||
use App\Models\User;
|
||||
use App\Models\Fornitore;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$tickets = Ticket::with(['stabile', 'categoriaTicket', 'apertoUser', 'assegnatoUser', 'assegnatoFornitore'])
|
||||
->whereHas('stabile', function($query) {
|
||||
$query->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
||||
})
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(10);
|
||||
|
||||
return view('admin.tickets.index', compact('tickets'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
|
||||
->attivi()
|
||||
->get();
|
||||
|
||||
$categorieTicket = CategoriaTicket::all();
|
||||
$users = User::role(['admin', 'amministratore'])->get();
|
||||
$fornitori = Fornitore::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)->get();
|
||||
|
||||
return view('admin.tickets.create', compact('stabili', 'categorieTicket', 'users', 'fornitori'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'categoria_ticket_id' => 'nullable|exists:categorie_ticket,id',
|
||||
'titolo' => 'required|string|max:255',
|
||||
'descrizione' => 'nullable|string',
|
||||
'luogo_intervento' => 'nullable|string|max:255',
|
||||
'stato' => 'required|in:Aperto,Preso in Carico,In Lavorazione,In Attesa Approvazione,In Attesa Ricambi,Risolto,Chiuso,Annullato',
|
||||
'priorita' => 'required|in:Bassa,Media,Alta,Urgente',
|
||||
'assegnato_a_user_id' => 'nullable|exists:users,id',
|
||||
'assegnato_a_fornitore_id' => 'nullable|exists:fornitori,id_fornitore',
|
||||
'data_scadenza_prevista' => 'nullable|date',
|
||||
'data_risoluzione_effettiva' => 'nullable|date',
|
||||
'data_chiusura_effettiva' => 'nullable|date',
|
||||
]);
|
||||
|
||||
$ticket = Ticket::create([
|
||||
'stabile_id' => $request->stabile_id,
|
||||
'categoria_ticket_id' => $request->categoria_ticket_id,
|
||||
'aperto_da_user_id' => Auth::id(),
|
||||
'assegnato_a_user_id' => $request->assegnato_a_user_id,
|
||||
'assegnato_a_fornitore_id' => $request->assegnato_a_fornitore_id,
|
||||
'titolo' => $request->titolo,
|
||||
'descrizione' => $request->descrizione,
|
||||
'luogo_intervento' => $request->luogo_intervento,
|
||||
'data_apertura' => now(),
|
||||
'data_scadenza_prevista' => $request->data_scadenza_prevista,
|
||||
'data_risoluzione_effettiva' => $request->data_risoluzione_effettiva,
|
||||
'data_chiusura_effettiva' => $request->data_chiusura_effettiva,
|
||||
'stato' => $request->stato,
|
||||
'priorita' => $request->priorita,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.tickets.index')
|
||||
->with('success', 'Ticket creato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Ticket $ticket)
|
||||
{
|
||||
// Verifica che l'utente possa accedere a questo ticket
|
||||
if ($ticket->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$ticket->load(['stabile', 'categoriaTicket', 'apertoUser', 'assegnatoUser', 'assegnatoFornitore']);
|
||||
|
||||
return view('admin.tickets.show', compact('ticket'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Ticket $ticket)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questo ticket
|
||||
if ($ticket->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
|
||||
->attivi()
|
||||
->get();
|
||||
|
||||
$categorieTicket = CategoriaTicket::all();
|
||||
$users = User::role(['admin', 'amministratore'])->get();
|
||||
$fornitori = Fornitore::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)->get();
|
||||
|
||||
return view('admin.tickets.edit', compact('ticket', 'stabili', 'categorieTicket', 'users', 'fornitori'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Ticket $ticket)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questo ticket
|
||||
if ($ticket->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'categoria_ticket_id' => 'nullable|exists:categorie_ticket,id',
|
||||
'titolo' => 'required|string|max:255',
|
||||
'descrizione' => 'nullable|string',
|
||||
'luogo_intervento' => 'nullable|string|max:255',
|
||||
'stato' => 'required|in:Aperto,Preso in Carico,In Lavorazione,In Attesa Approvazione,In Attesa Ricambi,Risolto,Chiuso,Annullato',
|
||||
'priorita' => 'required|in:Bassa,Media,Alta,Urgente',
|
||||
'assegnato_a_user_id' => 'nullable|exists:users,id',
|
||||
'assegnato_a_fornitore_id' => 'nullable|exists:fornitori,id_fornitore',
|
||||
'data_scadenza_prevista' => 'nullable|date',
|
||||
'data_risoluzione_effettiva' => 'nullable|date',
|
||||
'data_chiusura_effettiva' => 'nullable|date',
|
||||
]);
|
||||
|
||||
$ticket->update($request->all());
|
||||
|
||||
return redirect()->route('admin.tickets.index')
|
||||
->with('success', 'Ticket aggiornato con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Ticket $ticket)
|
||||
{
|
||||
// Verifica che l'utente possa eliminare questo ticket
|
||||
if ($ticket->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$ticket->delete();
|
||||
|
||||
return redirect()->route('admin.tickets.index')
|
||||
->with('success', 'Ticket eliminato con successo.');
|
||||
}
|
||||
}
|
||||
115
app/Http/Controllers/Admin/UnitaImmobiliareController.php
Normal file
115
app/Http/Controllers/Admin/UnitaImmobiliareController.php
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\UnitaImmobiliare;
|
||||
use App\Models\Stabile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UnitaImmobiliareController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create(Stabile $stabile)
|
||||
{
|
||||
// Verifica che l'utente possa accedere a questo stabile
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('admin.unita_immobiliari.create', compact('stabile'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request, Stabile $stabile)
|
||||
{
|
||||
// Verifica che l'utente possa accedere a questo stabile
|
||||
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'interno' => 'nullable|string|max:255',
|
||||
'scala' => 'nullable|string|max:255',
|
||||
'piano' => 'nullable|string|max:255',
|
||||
'fabbricato' => 'nullable|string|max:255',
|
||||
'millesimi_proprieta' => 'nullable|numeric|min:0|max:9999.9999',
|
||||
'categoria_catastale' => 'nullable|string|max:255',
|
||||
'superficie' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'vani' => 'nullable|numeric|min:0|max:99.99',
|
||||
'indirizzo' => 'nullable|string|max:255',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$unitaImmobiliare = UnitaImmobiliare::create($request->all());
|
||||
|
||||
return redirect()->route('admin.stabili.show', $stabile)
|
||||
->with('success', 'Unità immobiliare creata con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(UnitaImmobiliare $unitaImmobiliare)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questa unità
|
||||
if ($unitaImmobiliare->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('admin.unita_immobiliari.edit', compact('unitaImmobiliare'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, UnitaImmobiliare $unitaImmobiliare)
|
||||
{
|
||||
// Verifica che l'utente possa modificare questa unità
|
||||
if ($unitaImmobiliare->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'stabile_id' => 'required|exists:stabili,id_stabile',
|
||||
'interno' => 'nullable|string|max:255',
|
||||
'scala' => 'nullable|string|max:255',
|
||||
'piano' => 'nullable|string|max:255',
|
||||
'fabbricato' => 'nullable|string|max:255',
|
||||
'millesimi_proprieta' => 'nullable|numeric|min:0|max:9999.9999',
|
||||
'categoria_catastale' => 'nullable|string|max:255',
|
||||
'superficie' => 'nullable|numeric|min:0|max:99999999.99',
|
||||
'vani' => 'nullable|numeric|min:0|max:99.99',
|
||||
'indirizzo' => 'nullable|string|max:255',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$unitaImmobiliare->update($request->all());
|
||||
|
||||
return redirect()->route('admin.stabili.show', $unitaImmobiliare->stabile)
|
||||
->with('success', 'Unità immobiliare aggiornata con successo.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(UnitaImmobiliare $unitaImmobiliare)
|
||||
{
|
||||
// Verifica che l'utente possa eliminare questa unità
|
||||
if ($unitaImmobiliare->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$stabile = $unitaImmobiliare->stabile;
|
||||
$unitaImmobiliare->delete();
|
||||
|
||||
return redirect()->route('admin.stabili.show', $stabile)
|
||||
->with('success', 'Unità immobiliare eliminata con successo.');
|
||||
}
|
||||
}
|
||||
60
app/Http/Controllers/Condomino/DashboardController.php
Normal file
60
app/Http/Controllers/Condomino/DashboardController.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Condomino;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\Rata;
|
||||
use App\Models\Documento;
|
||||
use App\Models\UnitaImmobiliare;
|
||||
use App\Models\Proprieta;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Trova le unità immobiliari associate all'utente
|
||||
$unitaImmobiliari = UnitaImmobiliare::whereHas('proprieta', function($q) use ($user) {
|
||||
$q->where('soggetto_id', $user->soggetto->id_soggetto ?? null);
|
||||
})->with(['stabile', 'proprieta.soggetto'])->get();
|
||||
|
||||
// Statistiche principali
|
||||
$stats = [
|
||||
'unita_possedute' => $unitaImmobiliari->count(),
|
||||
'ticket_aperti' => Ticket::where('soggetto_richiedente_id', $user->soggetto->id_soggetto ?? null)
|
||||
->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione'])->count(),
|
||||
'rate_scadute' => 0, // Implementeremo quando avremo le rate
|
||||
'documenti_disponibili' => Documento::whereHasMorph('documentable', ['App\Models\Stabile'], function($q) use ($unitaImmobiliari) {
|
||||
$q->whereIn('id_stabile', $unitaImmobiliari->pluck('stabile_id'));
|
||||
})->count(),
|
||||
];
|
||||
|
||||
// Ticket recenti
|
||||
$ticketRecenti = Ticket::where('soggetto_richiedente_id', $user->soggetto->id_soggetto ?? null)
|
||||
->with(['stabile', 'categoriaTicket'])
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// Rate in scadenza (placeholder)
|
||||
$rateInScadenza = collect();
|
||||
|
||||
// Ultimi documenti
|
||||
$ultimiDocumenti = Documento::whereHasMorph('documentable', ['App\Models\Stabile'], function($q) use ($unitaImmobiliari) {
|
||||
$q->whereIn('id_stabile', $unitaImmobiliari->pluck('stabile_id'));
|
||||
})->orderBy('created_at', 'desc')->take(5)->get();
|
||||
|
||||
return view('condomino.dashboard', compact(
|
||||
'stats',
|
||||
'unitaImmobiliari',
|
||||
'ticketRecenti',
|
||||
'rateInScadenza',
|
||||
'ultimiDocumenti'
|
||||
));
|
||||
}
|
||||
}
|
||||
70
app/Http/Controllers/Condomino/DocumentoController.php
Normal file
70
app/Http/Controllers/Condomino/DocumentoController.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Condomino;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Documento;
|
||||
use App\Models\UnitaImmobiliare;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DocumentoController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Trova gli stabili delle unità dell'utente
|
||||
$stabiliIds = UnitaImmobiliare::whereHas('proprieta', function($q) use ($user) {
|
||||
$q->where('soggetto_id', $user->soggetto->id_soggetto ?? null);
|
||||
})->pluck('stabile_id')->unique();
|
||||
|
||||
$query = Documento::whereHasMorph('documentable', ['App\Models\Stabile'], function($q) use ($stabiliIds) {
|
||||
$q->whereIn('id_stabile', $stabiliIds);
|
||||
})->with('documentable');
|
||||
|
||||
// Filtri
|
||||
if ($request->filled('tipo_documento')) {
|
||||
$query->where('tipo_documento', $request->tipo_documento);
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where(function($q) use ($request) {
|
||||
$q->where('nome_file', 'like', '%' . $request->search . '%')
|
||||
->orWhere('descrizione', 'like', '%' . $request->search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
$documenti = $query->orderBy('created_at', 'desc')->paginate(20);
|
||||
|
||||
// Tipi documento per filtro
|
||||
$tipiDocumento = Documento::whereHasMorph('documentable', ['App\Models\Stabile'], function($q) use ($stabiliIds) {
|
||||
$q->whereIn('id_stabile', $stabiliIds);
|
||||
})->distinct()->pluck('tipo_documento')->filter();
|
||||
|
||||
return view('condomino.documenti.index', compact('documenti', 'tipiDocumento'));
|
||||
}
|
||||
|
||||
public function download(Documento $documento)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Verifica accesso
|
||||
$stabiliIds = UnitaImmobiliare::whereHas('proprieta', function($q) use ($user) {
|
||||
$q->where('soggetto_id', $user->soggetto->id_soggetto ?? null);
|
||||
})->pluck('stabile_id')->unique();
|
||||
|
||||
if ($documento->documentable_type === 'App\Models\Stabile') {
|
||||
if (!$stabiliIds->contains($documento->documentable_id)) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Storage::disk('public')->exists($documento->path_file)) {
|
||||
abort(404, 'File non trovato');
|
||||
}
|
||||
|
||||
return Storage::disk('public')->download($documento->path_file, $documento->nome_file);
|
||||
}
|
||||
}
|
||||
105
app/Http/Controllers/Condomino/TicketController.php
Normal file
105
app/Http/Controllers/Condomino/TicketController.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Condomino;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\CategoriaTicket;
|
||||
use App\Models\UnitaImmobiliare;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$tickets = Ticket::where('soggetto_richiedente_id', $user->soggetto->id_soggetto ?? null)
|
||||
->with(['stabile', 'categoriaTicket', 'unitaImmobiliare'])
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(10);
|
||||
|
||||
return view('condomino.tickets.index', compact('tickets'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Unità immobiliari dell'utente
|
||||
$unitaImmobiliari = UnitaImmobiliare::whereHas('proprieta', function($q) use ($user) {
|
||||
$q->where('soggetto_id', $user->soggetto->id_soggetto ?? null);
|
||||
})->with('stabile')->get();
|
||||
|
||||
$categorieTicket = CategoriaTicket::all();
|
||||
|
||||
return view('condomino.tickets.create', compact('unitaImmobiliari', 'categorieTicket'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$request->validate([
|
||||
'unita_immobiliare_id' => 'required|exists:unita_immobiliari,id_unita',
|
||||
'categoria_ticket_id' => 'nullable|exists:categorie_ticket,id',
|
||||
'titolo' => 'required|string|max:255',
|
||||
'descrizione' => 'required|string',
|
||||
'luogo_intervento' => 'nullable|string|max:255',
|
||||
'priorita' => 'required|in:Bassa,Media,Alta,Urgente',
|
||||
'allegati.*' => 'nullable|file|max:10240', // 10MB per file
|
||||
]);
|
||||
|
||||
// Verifica che l'unità appartenga all'utente
|
||||
$unitaImmobiliare = UnitaImmobiliare::whereHas('proprieta', function($q) use ($user) {
|
||||
$q->where('soggetto_id', $user->soggetto->id_soggetto ?? null);
|
||||
})->findOrFail($request->unita_immobiliare_id);
|
||||
|
||||
$ticket = Ticket::create([
|
||||
'stabile_id' => $unitaImmobiliare->stabile_id,
|
||||
'unita_immobiliare_id' => $request->unita_immobiliare_id,
|
||||
'soggetto_richiedente_id' => $user->soggetto->id_soggetto,
|
||||
'categoria_ticket_id' => $request->categoria_ticket_id,
|
||||
'aperto_da_user_id' => $user->id,
|
||||
'titolo' => $request->titolo,
|
||||
'descrizione' => $request->descrizione,
|
||||
'luogo_intervento' => $request->luogo_intervento,
|
||||
'data_apertura' => now(),
|
||||
'stato' => 'Aperto',
|
||||
'priorita' => $request->priorita,
|
||||
]);
|
||||
|
||||
// Gestione allegati
|
||||
if ($request->hasFile('allegati')) {
|
||||
foreach ($request->file('allegati') as $file) {
|
||||
$path = $file->store('ticket-allegati', 'public');
|
||||
|
||||
$ticket->documenti()->create([
|
||||
'nome_file' => $file->getClientOriginalName(),
|
||||
'path_file' => $path,
|
||||
'tipo_documento' => 'Allegato Ticket',
|
||||
'mime_type' => $file->getMimeType(),
|
||||
'dimensione_file' => $file->getSize(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('condomino.tickets.index')
|
||||
->with('success', 'Ticket creato con successo.');
|
||||
}
|
||||
|
||||
public function show(Ticket $ticket)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Verifica che il ticket appartenga all'utente
|
||||
if ($ticket->soggetto_richiedente_id !== $user->soggetto->id_soggetto ?? null) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$ticket->load(['stabile', 'categoriaTicket', 'unitaImmobiliare', 'documenti']);
|
||||
|
||||
return view('condomino.tickets.show', compact('ticket'));
|
||||
}
|
||||
}
|
||||
73
app/Http/Controllers/Condomino/UnitaController.php
Normal file
73
app/Http/Controllers/Condomino/UnitaController.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Condomino;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\UnitaImmobiliare;
|
||||
use App\Models\RichiestaModifica;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UnitaController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$unitaImmobiliari = UnitaImmobiliare::whereHas('proprieta', function($q) use ($user) {
|
||||
$q->where('soggetto_id', $user->soggetto->id_soggetto ?? null);
|
||||
})->with(['stabile', 'proprieta.soggetto'])->get();
|
||||
|
||||
return view('condomino.unita.index', compact('unitaImmobiliari'));
|
||||
}
|
||||
|
||||
public function show(UnitaImmobiliare $unitaImmobiliare)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Verifica accesso
|
||||
$hasAccess = $unitaImmobiliare->proprieta()
|
||||
->where('soggetto_id', $user->soggetto->id_soggetto ?? null)
|
||||
->exists();
|
||||
|
||||
if (!$hasAccess) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$unitaImmobiliare->load(['stabile', 'proprieta.soggetto']);
|
||||
|
||||
return view('condomino.unita.show', compact('unitaImmobiliare'));
|
||||
}
|
||||
|
||||
public function richiestaModifica(Request $request, UnitaImmobiliare $unitaImmobiliare)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// Verifica accesso
|
||||
$hasAccess = $unitaImmobiliare->proprieta()
|
||||
->where('soggetto_id', $user->soggetto->id_soggetto ?? null)
|
||||
->exists();
|
||||
|
||||
if (!$hasAccess) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'tipo_modifica' => 'required|in:anagrafica,catastale,proprieta',
|
||||
'descrizione' => 'required|string',
|
||||
'dati_proposti' => 'required|array',
|
||||
]);
|
||||
|
||||
RichiestaModifica::create([
|
||||
'unita_immobiliare_id' => $unitaImmobiliare->id_unita,
|
||||
'soggetto_richiedente_id' => $user->soggetto->id_soggetto,
|
||||
'tipo_modifica' => $request->tipo_modifica,
|
||||
'descrizione' => $request->descrizione,
|
||||
'dati_attuali' => $unitaImmobiliare->toArray(),
|
||||
'dati_proposti' => $request->dati_proposti,
|
||||
'stato' => 'in_attesa',
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Richiesta di modifica inviata con successo.');
|
||||
}
|
||||
}
|
||||
266
app/Models/Assemblea.php
Normal file
266
app/Models/Assemblea.php
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Assemblea extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'assemblee';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'tipo',
|
||||
'data_prima_convocazione',
|
||||
'data_seconda_convocazione',
|
||||
'luogo',
|
||||
'note',
|
||||
'stato',
|
||||
'data_convocazione',
|
||||
'data_svolgimento',
|
||||
'creato_da_user_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_prima_convocazione' => 'datetime',
|
||||
'data_seconda_convocazione' => 'datetime',
|
||||
'data_convocazione' => 'date',
|
||||
'data_svolgimento' => 'date',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Ordine del Giorno
|
||||
*/
|
||||
public function ordineGiorno()
|
||||
{
|
||||
return $this->hasMany(OrdineGiorno::class, 'assemblea_id')->orderBy('numero_punto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Convocazioni
|
||||
*/
|
||||
public function convocazioni()
|
||||
{
|
||||
return $this->hasMany(Convocazione::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Presenze
|
||||
*/
|
||||
public function presenze()
|
||||
{
|
||||
return $this->hasMany(PresenzaAssemblea::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Verbale
|
||||
*/
|
||||
public function verbale()
|
||||
{
|
||||
return $this->hasOne(Verbale::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Documenti
|
||||
*/
|
||||
public function documenti()
|
||||
{
|
||||
return $this->hasMany(DocumentoAssemblea::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User creatore
|
||||
*/
|
||||
public function creatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'creato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia convocazioni massive
|
||||
*/
|
||||
public function inviaConvocazioni($canali = ['email'], $userId)
|
||||
{
|
||||
$unitaImmobiliari = $this->stabile->unitaImmobiliari()->with('proprieta.soggetto')->get();
|
||||
$convocazioniInviate = 0;
|
||||
|
||||
foreach ($unitaImmobiliari as $unita) {
|
||||
foreach ($unita->proprieta as $proprieta) {
|
||||
$soggetto = $proprieta->soggetto;
|
||||
|
||||
foreach ($canali as $canale) {
|
||||
if ($this->verificaCanaleDisponibile($soggetto, $canale)) {
|
||||
$convocazione = $this->creaConvocazione($soggetto, $unita, $canale);
|
||||
|
||||
if ($this->inviaConvocazione($convocazione)) {
|
||||
$convocazioniInviate++;
|
||||
|
||||
// Registra nel protocollo
|
||||
$this->registraProtocollo($convocazione, $userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aggiorna stato assemblea
|
||||
$this->update([
|
||||
'stato' => 'convocata',
|
||||
'data_convocazione' => now(),
|
||||
]);
|
||||
|
||||
return $convocazioniInviate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica se il canale è disponibile per il soggetto
|
||||
*/
|
||||
private function verificaCanaleDisponibile($soggetto, $canale)
|
||||
{
|
||||
switch ($canale) {
|
||||
case 'email':
|
||||
return !empty($soggetto->email);
|
||||
case 'pec':
|
||||
return !empty($soggetto->pec);
|
||||
case 'whatsapp':
|
||||
case 'telegram':
|
||||
return !empty($soggetto->telefono);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea record convocazione
|
||||
*/
|
||||
private function creaConvocazione($soggetto, $unita, $canale)
|
||||
{
|
||||
return Convocazione::create([
|
||||
'assemblea_id' => $this->id,
|
||||
'soggetto_id' => $soggetto->id_soggetto,
|
||||
'unita_immobiliare_id' => $unita->id_unita,
|
||||
'canale_invio' => $canale,
|
||||
'data_invio' => now(),
|
||||
'esito_invio' => 'inviato',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia singola convocazione
|
||||
*/
|
||||
private function inviaConvocazione($convocazione)
|
||||
{
|
||||
// Implementazione invio basata sul canale
|
||||
// Qui si integrerebbe con servizi email, SMS, etc.
|
||||
|
||||
// Simula invio riuscito
|
||||
$convocazione->update([
|
||||
'esito_invio' => 'consegnato',
|
||||
'riferimento_invio' => 'REF-' . time(),
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra comunicazione nel protocollo
|
||||
*/
|
||||
private function registraProtocollo($convocazione, $userId)
|
||||
{
|
||||
$numeroProtocollo = $this->generaNumeroProtocollo();
|
||||
|
||||
RegistroProtocollo::create([
|
||||
'numero_protocollo' => $numeroProtocollo,
|
||||
'tipo_comunicazione' => 'convocazione',
|
||||
'assemblea_id' => $this->id,
|
||||
'soggetto_destinatario_id' => $convocazione->soggetto_id,
|
||||
'oggetto' => "Convocazione Assemblea {$this->tipo} - {$this->data_prima_convocazione->format('d/m/Y')}",
|
||||
'canale' => $convocazione->canale_invio,
|
||||
'data_invio' => $convocazione->data_invio,
|
||||
'esito' => $convocazione->esito_invio,
|
||||
'riferimento_esterno' => $convocazione->riferimento_invio,
|
||||
'creato_da_user_id' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero protocollo progressivo
|
||||
*/
|
||||
private function generaNumeroProtocollo()
|
||||
{
|
||||
$anno = date('Y');
|
||||
$ultimoProtocollo = RegistroProtocollo::whereYear('created_at', $anno)
|
||||
->orderBy('numero_protocollo', 'desc')
|
||||
->first();
|
||||
|
||||
if ($ultimoProtocollo) {
|
||||
$numero = intval(substr($ultimoProtocollo->numero_protocollo, -4)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return 'PROT/' . $anno . '/' . str_pad($numero, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola quorum assemblea
|
||||
*/
|
||||
public function calcolaQuorum()
|
||||
{
|
||||
$totaleMillesimi = $this->stabile->unitaImmobiliari()->sum('millesimi_proprieta');
|
||||
$millesimiPresenti = $this->presenze()->sum('millesimi_rappresentati');
|
||||
|
||||
return [
|
||||
'totale_millesimi' => $totaleMillesimi,
|
||||
'millesimi_presenti' => $millesimiPresenti,
|
||||
'percentuale_presenza' => $totaleMillesimi > 0 ? ($millesimiPresenti / $totaleMillesimi) * 100 : 0,
|
||||
'quorum_raggiunto' => $millesimiPresenti >= ($totaleMillesimi / 2),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera report presenze
|
||||
*/
|
||||
public function generaReportPresenze()
|
||||
{
|
||||
$presenze = $this->presenze()->with(['soggetto', 'unitaImmobiliare'])->get();
|
||||
$quorum = $this->calcolaQuorum();
|
||||
|
||||
return [
|
||||
'quorum' => $quorum,
|
||||
'presenze' => $presenze,
|
||||
'totale_presenti' => $presenze->where('tipo_presenza', 'presente')->count(),
|
||||
'totale_delegati' => $presenze->where('tipo_presenza', 'delegato')->count(),
|
||||
'totale_assenti' => $this->stabile->unitaImmobiliari()->count() - $presenze->count(),
|
||||
];
|
||||
}
|
||||
}
|
||||
70
app/Models/Banca.php
Normal file
70
app/Models/Banca.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Banca extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'banche';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'denominazione',
|
||||
'iban',
|
||||
'bic_swift',
|
||||
'agenzia',
|
||||
'indirizzo_agenzia',
|
||||
'tipo_conto',
|
||||
'saldo_iniziale',
|
||||
'data_apertura',
|
||||
'stato',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'saldo_iniziale' => 'decimal:2',
|
||||
'data_apertura' => 'date',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Movimenti Bancari
|
||||
*/
|
||||
public function movimentiBancari()
|
||||
{
|
||||
return $this->hasMany(MovimentoBancario::class, 'banca_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per conti attivi
|
||||
*/
|
||||
public function scopeAttivi($query)
|
||||
{
|
||||
return $query->where('stato', 'attivo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola il saldo attuale
|
||||
*/
|
||||
public function getSaldoAttualeAttribute()
|
||||
{
|
||||
$movimenti = $this->movimentiBancari()
|
||||
->selectRaw('SUM(CASE WHEN tipo_movimento = "entrata" THEN importo ELSE -importo END) as saldo')
|
||||
->first();
|
||||
|
||||
return $this->saldo_iniziale + ($movimenti->saldo ?? 0);
|
||||
}
|
||||
}
|
||||
279
app/Models/Bilancio.php
Normal file
279
app/Models/Bilancio.php
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Bilancio extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'gestione_id',
|
||||
'anno_esercizio',
|
||||
'data_inizio_esercizio',
|
||||
'data_fine_esercizio',
|
||||
'tipo_gestione',
|
||||
'descrizione',
|
||||
'stato',
|
||||
'totale_entrate',
|
||||
'totale_uscite',
|
||||
'risultato_gestione',
|
||||
'data_approvazione',
|
||||
'approvato_da_user_id',
|
||||
'data_chiusura',
|
||||
'chiuso_da_user_id',
|
||||
'note',
|
||||
'versione',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'anno_esercizio' => 'integer',
|
||||
'data_inizio_esercizio' => 'date',
|
||||
'data_fine_esercizio' => 'date',
|
||||
'totale_entrate' => 'decimal:2',
|
||||
'totale_uscite' => 'decimal:2',
|
||||
'risultato_gestione' => 'decimal:2',
|
||||
'data_approvazione' => 'date',
|
||||
'data_chiusura' => 'date',
|
||||
'versione' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Gestione
|
||||
*/
|
||||
public function gestione()
|
||||
{
|
||||
return $this->belongsTo(Gestione::class, 'gestione_id', 'id_gestione');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Scritture
|
||||
*/
|
||||
public function scritture()
|
||||
{
|
||||
return $this->hasMany(ScritturaBilancio::class, 'bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Conguagli
|
||||
*/
|
||||
public function conguagli()
|
||||
{
|
||||
return $this->hasMany(Conguaglio::class, 'bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Quadrature
|
||||
*/
|
||||
public function quadrature()
|
||||
{
|
||||
return $this->hasMany(Quadratura::class, 'bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Rimborsi Assicurativi
|
||||
*/
|
||||
public function rimborsiAssicurativi()
|
||||
{
|
||||
return $this->hasMany(RimborsoAssicurativo::class, 'bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User che ha approvato
|
||||
*/
|
||||
public function approvatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'approvato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User che ha chiuso
|
||||
*/
|
||||
public function chiusoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'chiuso_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola totali da scritture
|
||||
*/
|
||||
public function calcolaTotali()
|
||||
{
|
||||
$entrate = $this->scritture()
|
||||
->whereHas('dettagli', function($q) {
|
||||
$q->whereHas('conto', function($c) {
|
||||
$c->where('tipo_conto', 'ricavo');
|
||||
});
|
||||
})
|
||||
->sum('importo_totale');
|
||||
|
||||
$uscite = $this->scritture()
|
||||
->whereHas('dettagli', function($q) {
|
||||
$q->whereHas('conto', function($c) {
|
||||
$c->where('tipo_conto', 'costo');
|
||||
});
|
||||
})
|
||||
->sum('importo_totale');
|
||||
|
||||
$this->update([
|
||||
'totale_entrate' => $entrate,
|
||||
'totale_uscite' => $uscite,
|
||||
'risultato_gestione' => $entrate - $uscite,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola conguagli per tutte le unità
|
||||
*/
|
||||
public function calcolaConguagli()
|
||||
{
|
||||
$unitaImmobiliari = $this->stabile->unitaImmobiliari;
|
||||
|
||||
foreach ($unitaImmobiliari as $unita) {
|
||||
$this->calcolaConguaglioUnita($unita);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola conguaglio per singola unità
|
||||
*/
|
||||
public function calcolaConguaglioUnita($unitaImmobiliare)
|
||||
{
|
||||
// Calcola totale rate pagate
|
||||
$totaleRatePagate = $this->calcolaTotaleRatePagate($unitaImmobiliare);
|
||||
|
||||
// Calcola totale spese effettive ripartite
|
||||
$totaleSpese = $this->calcolaTotaleSpese($unitaImmobiliare);
|
||||
|
||||
// Calcola conguaglio
|
||||
$conguaglioDovuto = $totaleRatePagate - $totaleSpese;
|
||||
|
||||
$tipoConguaglio = $conguaglioDovuto > 0 ? 'a_credito' :
|
||||
($conguaglioDovuto < 0 ? 'a_debito' : 'pareggio');
|
||||
|
||||
// Trova il soggetto principale dell'unità
|
||||
$soggetto = $unitaImmobiliare->proprieta()
|
||||
->where('tipo_diritto', 'proprietario')
|
||||
->first()?->soggetto;
|
||||
|
||||
if (!$soggetto) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Conguaglio::updateOrCreate(
|
||||
[
|
||||
'bilancio_id' => $this->id,
|
||||
'unita_immobiliare_id' => $unitaImmobiliare->id_unita,
|
||||
'soggetto_id' => $soggetto->id_soggetto,
|
||||
],
|
||||
[
|
||||
'totale_rate_pagate' => $totaleRatePagate,
|
||||
'totale_spese_effettive' => $totaleSpese,
|
||||
'conguaglio_dovuto' => abs($conguaglioDovuto),
|
||||
'tipo_conguaglio' => $tipoConguaglio,
|
||||
'data_calcolo' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola totale rate pagate per unità
|
||||
*/
|
||||
private function calcolaTotaleRatePagate($unitaImmobiliare)
|
||||
{
|
||||
// Implementazione del calcolo rate pagate
|
||||
// Collegamento con tabella rate e incassi
|
||||
return 0; // Placeholder
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola totale spese per unità
|
||||
*/
|
||||
private function calcolaTotaleSpese($unitaImmobiliare)
|
||||
{
|
||||
return $this->scritture()
|
||||
->whereHas('ripartizioni', function($q) use ($unitaImmobiliare) {
|
||||
$q->where('unita_immobiliare_id', $unitaImmobiliare->id_unita);
|
||||
})
|
||||
->sum('quota_finale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera scritture di chiusura
|
||||
*/
|
||||
public function generaScritture ChiusuraEsercizio($userId)
|
||||
{
|
||||
// Scrittura di chiusura costi
|
||||
$totaleCosti = $this->scritture()
|
||||
->whereHas('dettagli.conto', function($q) {
|
||||
$q->where('tipo_conto', 'costo');
|
||||
})
|
||||
->sum('importo_totale');
|
||||
|
||||
// Scrittura di chiusura ricavi
|
||||
$totaleRicavi = $this->scritture()
|
||||
->whereHas('dettagli.conto', function($q) {
|
||||
$q->where('tipo_conto', 'ricavo');
|
||||
})
|
||||
->sum('importo_totale');
|
||||
|
||||
// Crea scrittura di chiusura
|
||||
$scritturaChiusura = ScritturaBilancio::create([
|
||||
'bilancio_id' => $this->id,
|
||||
'numero_scrittura' => $this->generaNumeroScrittura('CHIUS'),
|
||||
'data_scrittura' => $this->data_fine_esercizio,
|
||||
'descrizione' => 'Chiusura esercizio ' . $this->anno_esercizio,
|
||||
'tipo_scrittura' => 'chiusura',
|
||||
'importo_totale' => abs($totaleRicavi - $totaleCosti),
|
||||
'creato_da_user_id' => $userId,
|
||||
]);
|
||||
|
||||
return $scritturaChiusura;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero scrittura progressivo
|
||||
*/
|
||||
private function generaNumeroScrittura($prefisso = 'SCR')
|
||||
{
|
||||
$ultimaScrittura = $this->scritture()
|
||||
->where('numero_scrittura', 'like', $prefisso . '%')
|
||||
->orderBy('numero_scrittura', 'desc')
|
||||
->first();
|
||||
|
||||
if ($ultimaScrittura) {
|
||||
$numero = intval(substr($ultimaScrittura->numero_scrittura, -4)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return $prefisso . '/' . $this->anno_esercizio . '/' . str_pad($numero, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
136
app/Models/Conguaglio.php
Normal file
136
app/Models/Conguaglio.php
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Conguaglio extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'bilancio_id',
|
||||
'unita_immobiliare_id',
|
||||
'soggetto_id',
|
||||
'totale_rate_pagate',
|
||||
'totale_spese_effettive',
|
||||
'conguaglio_dovuto',
|
||||
'tipo_conguaglio',
|
||||
'stato',
|
||||
'data_calcolo',
|
||||
'data_pagamento',
|
||||
'importo_pagato',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'totale_rate_pagate' => 'decimal:2',
|
||||
'totale_spese_effettive' => 'decimal:2',
|
||||
'conguaglio_dovuto' => 'decimal:2',
|
||||
'data_calcolo' => 'date',
|
||||
'data_pagamento' => 'date',
|
||||
'importo_pagato' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Bilancio
|
||||
*/
|
||||
public function bilancio()
|
||||
{
|
||||
return $this->belongsTo(Bilancio::class, 'bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Unità Immobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto
|
||||
*/
|
||||
public function soggetto()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Rate Conguaglio
|
||||
*/
|
||||
public function rateConguaglio()
|
||||
{
|
||||
return $this->hasMany(RataConguaglio::class, 'conguaglio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo conguaglio
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_conguaglio', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera rate per conguaglio
|
||||
*/
|
||||
public function generaRate($numeroRate, $dataInizio, $userId)
|
||||
{
|
||||
if ($this->tipo_conguaglio === 'pareggio') {
|
||||
return collect();
|
||||
}
|
||||
|
||||
$importoPerRata = $this->conguaglio_dovuto / $numeroRate;
|
||||
$rate = collect();
|
||||
|
||||
for ($i = 1; $i <= $numeroRate; $i++) {
|
||||
$dataScadenza = $dataInizio->copy()->addMonths($i - 1);
|
||||
$numeroRata = $this->generaNumeroRata($i);
|
||||
|
||||
$rata = RataConguaglio::create([
|
||||
'conguaglio_id' => $this->id,
|
||||
'numero_rata' => $numeroRata,
|
||||
'descrizione' => "Conguaglio rata {$i} di {$numeroRate} - " . $this->unitaImmobiliare->identificazione_completa,
|
||||
'data_scadenza' => $dataScadenza,
|
||||
'importo_rata' => $importoPerRata,
|
||||
'rateizzato' => $numeroRate > 1,
|
||||
'numero_rate_totali' => $numeroRate,
|
||||
'numero_rata_corrente' => $i,
|
||||
]);
|
||||
|
||||
$rate->push($rata);
|
||||
}
|
||||
|
||||
return $rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero rata univoco
|
||||
*/
|
||||
private function generaNumeroRata($numeroRata)
|
||||
{
|
||||
$prefisso = $this->tipo_conguaglio === 'a_credito' ? 'RIMB' : 'CONG';
|
||||
return $prefisso . '/' . $this->bilancio->anno_esercizio . '/' .
|
||||
$this->unita_immobiliare_id . '/' . str_pad($numeroRata, 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola importo residuo
|
||||
*/
|
||||
public function getImportoResiduoAttribute()
|
||||
{
|
||||
return $this->conguaglio_dovuto - $this->importo_pagato;
|
||||
}
|
||||
}
|
||||
128
app/Models/Convocazione.php
Normal file
128
app/Models/Convocazione.php
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Convocazione extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'convocazioni';
|
||||
|
||||
protected $fillable = [
|
||||
'assemblea_id',
|
||||
'soggetto_id',
|
||||
'unita_immobiliare_id',
|
||||
'canale_invio',
|
||||
'data_invio',
|
||||
'esito_invio',
|
||||
'data_lettura',
|
||||
'riferimento_invio',
|
||||
'note_invio',
|
||||
'delega_presente',
|
||||
'delegato_soggetto_id',
|
||||
'documento_delega',
|
||||
'presenza_confermata',
|
||||
'data_conferma_presenza',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_invio' => 'datetime',
|
||||
'data_lettura' => 'datetime',
|
||||
'delega_presente' => 'boolean',
|
||||
'presenza_confermata' => 'boolean',
|
||||
'data_conferma_presenza' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Assemblea
|
||||
*/
|
||||
public function assemblea()
|
||||
{
|
||||
return $this->belongsTo(Assemblea::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto destinatario
|
||||
*/
|
||||
public function soggetto()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Unità Immobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto delegato
|
||||
*/
|
||||
public function delegato()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'delegato_soggetto_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per esito
|
||||
*/
|
||||
public function scopeEsito($query, $esito)
|
||||
{
|
||||
return $query->where('esito_invio', $esito);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per canale
|
||||
*/
|
||||
public function scopeCanale($query, $canale)
|
||||
{
|
||||
return $query->where('canale_invio', $canale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conferma lettura
|
||||
*/
|
||||
public function confermaLettura()
|
||||
{
|
||||
$this->update([
|
||||
'esito_invio' => 'letto',
|
||||
'data_lettura' => now(),
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conferma presenza
|
||||
*/
|
||||
public function confermaPresenza()
|
||||
{
|
||||
$this->update([
|
||||
'presenza_confermata' => true,
|
||||
'data_conferma_presenza' => now(),
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carica delega
|
||||
*/
|
||||
public function caricaDelega($delegatoId, $documentoPath)
|
||||
{
|
||||
$this->update([
|
||||
'delega_presente' => true,
|
||||
'delegato_soggetto_id' => $delegatoId,
|
||||
'documento_delega' => $documentoPath,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
79
app/Models/DettaglioMovimento.php
Normal file
79
app/Models/DettaglioMovimento.php
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DettaglioMovimento extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'dettagli_movimenti';
|
||||
|
||||
protected $fillable = [
|
||||
'movimento_id',
|
||||
'conto_id',
|
||||
'voce_spesa_id',
|
||||
'tabella_millesimale_id',
|
||||
'descrizione',
|
||||
'importo_dare',
|
||||
'importo_avere',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'importo_dare' => 'decimal:2',
|
||||
'importo_avere' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Movimento Contabile
|
||||
*/
|
||||
public function movimento()
|
||||
{
|
||||
return $this->belongsTo(MovimentoContabile::class, 'movimento_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Piano Conti
|
||||
*/
|
||||
public function conto()
|
||||
{
|
||||
return $this->belongsTo(PianoConti::class, 'conto_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Voce Spesa
|
||||
*/
|
||||
public function voceSpesa()
|
||||
{
|
||||
return $this->belongsTo(VoceSpesa::class, 'voce_spesa_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tabella Millesimale
|
||||
*/
|
||||
public function tabellaMillesimale()
|
||||
{
|
||||
return $this->belongsTo(TabellaMillesimale::class, 'tabella_millesimale_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per dare
|
||||
*/
|
||||
public function scopeDare($query)
|
||||
{
|
||||
return $query->where('importo_dare', '>', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per avere
|
||||
*/
|
||||
public function scopeAvere($query)
|
||||
{
|
||||
return $query->where('importo_avere', '>', 0);
|
||||
}
|
||||
}
|
||||
42
app/Models/DettaglioTabellaMillesimale.php
Normal file
42
app/Models/DettaglioTabellaMillesimale.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DettaglioTabellaMillesimale extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'dettagli_tabelle_millesimali';
|
||||
|
||||
protected $fillable = [
|
||||
'tabella_millesimale_id',
|
||||
'unita_immobiliare_id',
|
||||
'millesimi',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'millesimi' => 'decimal:4',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Tabella Millesimale
|
||||
*/
|
||||
public function tabellaMillesimale()
|
||||
{
|
||||
return $this->belongsTo(TabellaMillesimale::class, 'tabella_millesimale_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Unità Immobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
}
|
||||
73
app/Models/Documento.php
Normal file
73
app/Models/Documento.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class Documento extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'documenti';
|
||||
|
||||
protected $fillable = [
|
||||
'documentable_id',
|
||||
'documentable_type',
|
||||
'nome_file',
|
||||
'path_file',
|
||||
'tipo_documento',
|
||||
'dimensione_file',
|
||||
'mime_type',
|
||||
'descrizione',
|
||||
'xml_data',
|
||||
'hash_file',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'xml_data' => 'array',
|
||||
'dimensione_file' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione polimorfica
|
||||
*/
|
||||
public function documentable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo documento
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_documento', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per URL download
|
||||
*/
|
||||
public function getUrlDownloadAttribute()
|
||||
{
|
||||
return route('admin.documenti.download', $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per dimensione leggibile
|
||||
*/
|
||||
public function getDimensioneLeggibileAttribute()
|
||||
{
|
||||
$bytes = $this->dimensione_file;
|
||||
$units = ['B', 'KB', 'MB', 'GB'];
|
||||
|
||||
for ($i = 0; $bytes > 1024; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes, 2) . ' ' . $units[$i];
|
||||
}
|
||||
}
|
||||
81
app/Models/Fornitore.php
Normal file
81
app/Models/Fornitore.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Fornitore extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'fornitori';
|
||||
protected $primaryKey = 'id_fornitore';
|
||||
|
||||
protected $fillable = [
|
||||
'amministratore_id',
|
||||
'ragione_sociale',
|
||||
'partita_iva',
|
||||
'codice_fiscale',
|
||||
'indirizzo',
|
||||
'cap',
|
||||
'citta',
|
||||
'provincia',
|
||||
'email',
|
||||
'pec',
|
||||
'telefono',
|
||||
'old_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Amministratore
|
||||
*/
|
||||
public function amministratore()
|
||||
{
|
||||
return $this->belongsTo(Amministratore::class, 'amministratore_id', 'id_amministratore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tickets assegnati
|
||||
*/
|
||||
public function ticketsAssegnati()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'assegnato_a_fornitore_id', 'id_fornitore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per l'indirizzo completo
|
||||
*/
|
||||
public function getIndirizzoCompletoAttribute()
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
if ($this->indirizzo) $parts[] = $this->indirizzo;
|
||||
if ($this->cap && $this->citta) {
|
||||
$parts[] = $this->cap . ' ' . $this->citta;
|
||||
} elseif ($this->citta) {
|
||||
$parts[] = $this->citta;
|
||||
}
|
||||
if ($this->provincia) $parts[] = '(' . $this->provincia . ')';
|
||||
|
||||
return implode(', ', $parts) ?: '-';
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per ricerca
|
||||
*/
|
||||
public function scopeSearch($query, $search)
|
||||
{
|
||||
return $query->where(function($q) use ($search) {
|
||||
$q->where('ragione_sociale', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%")
|
||||
->orWhere('telefono', 'like', "%{$search}%")
|
||||
->orWhere('partita_iva', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
}
|
||||
95
app/Models/Gestione.php
Normal file
95
app/Models/Gestione.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Gestione extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'gestioni';
|
||||
protected $primaryKey = 'id_gestione';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'anno_gestione',
|
||||
'tipo_gestione',
|
||||
'data_inizio',
|
||||
'data_fine',
|
||||
'descrizione',
|
||||
'stato',
|
||||
'preventivo_approvato',
|
||||
'data_approvazione',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_inizio' => 'date',
|
||||
'data_fine' => 'date',
|
||||
'data_approvazione' => 'date',
|
||||
'preventivo_approvato' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Preventivi
|
||||
*/
|
||||
public function preventivi()
|
||||
{
|
||||
return $this->hasMany(Preventivo::class, 'gestione_id', 'id_gestione');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Movimenti Contabili
|
||||
*/
|
||||
public function movimentiContabili()
|
||||
{
|
||||
return $this->hasMany(MovimentoContabile::class, 'gestione_id', 'id_gestione');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Rate
|
||||
*/
|
||||
public function rate()
|
||||
{
|
||||
return $this->hasMany(Rata::class, 'gestione_id', 'id_gestione');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per gestioni attive
|
||||
*/
|
||||
public function scopeAttive($query)
|
||||
{
|
||||
return $query->where('stato', 'attiva');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo gestione
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_gestione', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per il nome completo della gestione
|
||||
*/
|
||||
public function getNomeCompletoAttribute()
|
||||
{
|
||||
return $this->anno_gestione . ' - ' . ucfirst($this->tipo_gestione) .
|
||||
($this->descrizione ? ' (' . $this->descrizione . ')' : '');
|
||||
}
|
||||
}
|
||||
83
app/Models/LogModificaPreventivo.php
Normal file
83
app/Models/LogModificaPreventivo.php
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class LogModificaPreventivo extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'log_modifiche_preventivo';
|
||||
|
||||
protected $fillable = [
|
||||
'entita',
|
||||
'entita_id',
|
||||
'versione_precedente',
|
||||
'versione_nuova',
|
||||
'utente_id',
|
||||
'tipo_operazione',
|
||||
'motivo',
|
||||
'dati_precedenti',
|
||||
'dati_nuovi',
|
||||
'diff',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'versione_precedente' => 'integer',
|
||||
'versione_nuova' => 'integer',
|
||||
'dati_precedenti' => 'array',
|
||||
'dati_nuovi' => 'array',
|
||||
'diff' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con User
|
||||
*/
|
||||
public function utente()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'utente_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione polimorfica con entità
|
||||
*/
|
||||
public function entita()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per entità
|
||||
*/
|
||||
public function scopePerEntita($query, $entita, $entitaId)
|
||||
{
|
||||
return $query->where('entita', $entita)->where('entita_id', $entitaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera diff stile GIT
|
||||
*/
|
||||
public static function generaDiff($datiPrecedenti, $datiNuovi)
|
||||
{
|
||||
$diff = [];
|
||||
|
||||
foreach ($datiNuovi as $campo => $valoreNuovo) {
|
||||
$valorePrecedente = $datiPrecedenti[$campo] ?? null;
|
||||
|
||||
if ($valorePrecedente != $valoreNuovo) {
|
||||
$diff[] = [
|
||||
'campo' => $campo,
|
||||
'da' => $valorePrecedente,
|
||||
'a' => $valoreNuovo,
|
||||
'tipo' => $valorePrecedente === null ? 'aggiunto' : 'modificato',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
}
|
||||
68
app/Models/MovimentoBancario.php
Normal file
68
app/Models/MovimentoBancario.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MovimentoBancario extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'movimenti_bancari';
|
||||
|
||||
protected $fillable = [
|
||||
'banca_id',
|
||||
'movimento_contabile_id',
|
||||
'data_valuta',
|
||||
'data_contabile',
|
||||
'tipo_movimento',
|
||||
'importo',
|
||||
'causale',
|
||||
'beneficiario',
|
||||
'ordinante',
|
||||
'cro_tro',
|
||||
'stato_riconciliazione',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_valuta' => 'date',
|
||||
'data_contabile' => 'date',
|
||||
'importo' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Banca
|
||||
*/
|
||||
public function banca()
|
||||
{
|
||||
return $this->belongsTo(Banca::class, 'banca_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Movimento Contabile
|
||||
*/
|
||||
public function movimentoContabile()
|
||||
{
|
||||
return $this->belongsTo(MovimentoContabile::class, 'movimento_contabile_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo movimento
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_movimento', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato riconciliazione
|
||||
*/
|
||||
public function scopeRiconciliazione($query, $stato)
|
||||
{
|
||||
return $query->where('stato_riconciliazione', $stato);
|
||||
}
|
||||
}
|
||||
99
app/Models/MovimentoContabile.php
Normal file
99
app/Models/MovimentoContabile.php
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class MovimentoContabile extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'movimenti_contabili';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'gestione_id',
|
||||
'fornitore_id',
|
||||
'documento_id',
|
||||
'protocollo',
|
||||
'data_registrazione',
|
||||
'data_documento',
|
||||
'numero_documento',
|
||||
'descrizione',
|
||||
'tipo_movimento',
|
||||
'importo_totale',
|
||||
'ritenuta_acconto',
|
||||
'importo_netto',
|
||||
'stato',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_registrazione' => 'date',
|
||||
'data_documento' => 'date',
|
||||
'importo_totale' => 'decimal:2',
|
||||
'ritenuta_acconto' => 'decimal:2',
|
||||
'importo_netto' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Gestione
|
||||
*/
|
||||
public function gestione()
|
||||
{
|
||||
return $this->belongsTo(Gestione::class, 'gestione_id', 'id_gestione');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Fornitore
|
||||
*/
|
||||
public function fornitore()
|
||||
{
|
||||
return $this->belongsTo(Fornitore::class, 'fornitore_id', 'id_fornitore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Documento
|
||||
*/
|
||||
public function documento()
|
||||
{
|
||||
return $this->belongsTo(Documento::class, 'documento_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Dettagli Movimento (partita doppia)
|
||||
*/
|
||||
public function dettagli()
|
||||
{
|
||||
return $this->hasMany(DettaglioMovimento::class, 'movimento_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo movimento
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_movimento', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
}
|
||||
244
app/Models/OrdineGiorno.php
Normal file
244
app/Models/OrdineGiorno.php
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class OrdineGiorno extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'ordine_giorno';
|
||||
|
||||
protected $fillable = [
|
||||
'assemblea_id',
|
||||
'numero_punto',
|
||||
'titolo',
|
||||
'descrizione',
|
||||
'tipo_voce',
|
||||
'collegamento_preventivo_id',
|
||||
'importo_spesa',
|
||||
'tabella_millesimale_id',
|
||||
'esito_votazione',
|
||||
'voti_favorevoli',
|
||||
'voti_contrari',
|
||||
'astenuti',
|
||||
'millesimi_favorevoli',
|
||||
'millesimi_contrari',
|
||||
'millesimi_astenuti',
|
||||
'note_delibera',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'numero_punto' => 'integer',
|
||||
'importo_spesa' => 'decimal:2',
|
||||
'voti_favorevoli' => 'integer',
|
||||
'voti_contrari' => 'integer',
|
||||
'astenuti' => 'integer',
|
||||
'millesimi_favorevoli' => 'decimal:4',
|
||||
'millesimi_contrari' => 'decimal:4',
|
||||
'millesimi_astenuti' => 'decimal:4',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Assemblea
|
||||
*/
|
||||
public function assemblea()
|
||||
{
|
||||
return $this->belongsTo(Assemblea::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Preventivo collegato
|
||||
*/
|
||||
public function preventivo()
|
||||
{
|
||||
return $this->belongsTo(Preventivo::class, 'collegamento_preventivo_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tabella Millesimale
|
||||
*/
|
||||
public function tabellaMillesimale()
|
||||
{
|
||||
return $this->belongsTo(TabellaMillesimale::class, 'tabella_millesimale_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Votazioni
|
||||
*/
|
||||
public function votazioni()
|
||||
{
|
||||
return $this->hasMany(Votazione::class, 'ordine_giorno_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Delibera
|
||||
*/
|
||||
public function delibera()
|
||||
{
|
||||
return $this->hasOne(Delibera::class, 'ordine_giorno_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra votazione
|
||||
*/
|
||||
public function registraVotazione($soggetto, $unitaImmobiliare, $voto, $millesimi, $motivazione = null)
|
||||
{
|
||||
return Votazione::updateOrCreate(
|
||||
[
|
||||
'ordine_giorno_id' => $this->id,
|
||||
'soggetto_id' => $soggetto->id_soggetto,
|
||||
'unita_immobiliare_id' => $unitaImmobiliare->id_unita,
|
||||
],
|
||||
[
|
||||
'voto' => $voto,
|
||||
'millesimi_voto' => $millesimi,
|
||||
'data_voto' => now(),
|
||||
'motivazione' => $motivazione,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola risultato votazione
|
||||
*/
|
||||
public function calcolaRisultato()
|
||||
{
|
||||
$votazioni = $this->votazioni;
|
||||
|
||||
$favorevoli = $votazioni->where('voto', 'favorevole');
|
||||
$contrari = $votazioni->where('voto', 'contrario');
|
||||
$astenuti = $votazioni->where('voto', 'astenuto');
|
||||
|
||||
$totaleMillesimiFavorevoli = $favorevoli->sum('millesimi_voto');
|
||||
$totaleMillesimiContrari = $contrari->sum('millesimi_voto');
|
||||
$totaleMillesimiAstenuti = $astenuti->sum('millesimi_voto');
|
||||
|
||||
$totaleMillesimiVotanti = $totaleMillesimiFavorevoli + $totaleMillesimiContrari + $totaleMillesimiAstenuti;
|
||||
|
||||
// Calcola maggioranza (50% + 1 dei millesimi votanti)
|
||||
$maggioranzaRichiesta = ($totaleMillesimiVotanti / 2) + 0.0001;
|
||||
$maggioranzaRaggiunta = $totaleMillesimiFavorevoli >= $maggioranzaRichiesta;
|
||||
|
||||
$esito = $maggioranzaRaggiunta ? 'approvato' : 'respinto';
|
||||
|
||||
// Aggiorna il punto dell'ordine del giorno
|
||||
$this->update([
|
||||
'esito_votazione' => $esito,
|
||||
'voti_favorevoli' => $favorevoli->count(),
|
||||
'voti_contrari' => $contrari->count(),
|
||||
'astenuti' => $astenuti->count(),
|
||||
'millesimi_favorevoli' => $totaleMillesimiFavorevoli,
|
||||
'millesimi_contrari' => $totaleMillesimiContrari,
|
||||
'millesimi_astenuti' => $totaleMillesimiAstenuti,
|
||||
]);
|
||||
|
||||
// Crea delibera
|
||||
if ($esito === 'approvato') {
|
||||
$this->creaDelibera($totaleMillesimiFavorevoli, $totaleMillesimiContrari, $totaleMillesimiAstenuti);
|
||||
}
|
||||
|
||||
return [
|
||||
'esito' => $esito,
|
||||
'maggioranza_raggiunta' => $maggioranzaRaggiunta,
|
||||
'percentuale_favorevoli' => $totaleMillesimiVotanti > 0 ? ($totaleMillesimiFavorevoli / $totaleMillesimiVotanti) * 100 : 0,
|
||||
'dettagli' => [
|
||||
'favorevoli' => ['voti' => $favorevoli->count(), 'millesimi' => $totaleMillesimiFavorevoli],
|
||||
'contrari' => ['voti' => $contrari->count(), 'millesimi' => $totaleMillesimiContrari],
|
||||
'astenuti' => ['voti' => $astenuti->count(), 'millesimi' => $totaleMillesimiAstenuti],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea delibera se approvata
|
||||
*/
|
||||
private function creaDelibera($millFav, $millContr, $millAst)
|
||||
{
|
||||
$numeroDelibera = $this->generaNumeroDelibera();
|
||||
|
||||
$delibera = Delibera::create([
|
||||
'ordine_giorno_id' => $this->id,
|
||||
'numero_delibera' => $numeroDelibera,
|
||||
'esito' => 'approvata',
|
||||
'testo_delibera' => $this->generaTestoDelibera(),
|
||||
'totale_voti_favorevoli' => $this->voti_favorevoli,
|
||||
'totale_voti_contrari' => $this->voti_contrari,
|
||||
'totale_astenuti' => $this->astenuti,
|
||||
'totale_millesimi_favorevoli' => $millFav,
|
||||
'totale_millesimi_contrari' => $millContr,
|
||||
'totale_millesimi_astenuti' => $millAst,
|
||||
'percentuale_approvazione' => ($millFav / ($millFav + $millContr + $millAst)) * 100,
|
||||
'maggioranza_raggiunta' => true,
|
||||
'data_delibera' => now(),
|
||||
]);
|
||||
|
||||
// Se è una spesa approvata, avvia automazione
|
||||
if ($this->tipo_voce === 'spesa' && $this->importo_spesa > 0) {
|
||||
$this->avviaAutomazioneSpesa($delibera);
|
||||
}
|
||||
|
||||
return $delibera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero delibera
|
||||
*/
|
||||
private function generaNumeroDelibera()
|
||||
{
|
||||
$anno = $this->assemblea->data_prima_convocazione->year;
|
||||
$ultimaDelibera = Delibera::whereHas('ordineGiorno.assemblea', function($q) use ($anno) {
|
||||
$q->whereYear('data_prima_convocazione', $anno);
|
||||
})->orderBy('numero_delibera', 'desc')->first();
|
||||
|
||||
if ($ultimaDelibera) {
|
||||
$numero = intval(substr($ultimaDelibera->numero_delibera, -3)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return 'DEL/' . $anno . '/' . str_pad($numero, 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera testo delibera
|
||||
*/
|
||||
private function generaTestoDelibera()
|
||||
{
|
||||
$testo = "DELIBERA N. {$this->generaNumeroDelibera()}\n\n";
|
||||
$testo .= "OGGETTO: {$this->titolo}\n\n";
|
||||
$testo .= "DESCRIZIONE:\n{$this->descrizione}\n\n";
|
||||
|
||||
if ($this->tipo_voce === 'spesa' && $this->importo_spesa > 0) {
|
||||
$testo .= "IMPORTO APPROVATO: € " . number_format($this->importo_spesa, 2, ',', '.') . "\n";
|
||||
if ($this->tabellaMillesimale) {
|
||||
$testo .= "RIPARTIZIONE: {$this->tabellaMillesimale->nome}\n";
|
||||
}
|
||||
}
|
||||
|
||||
$testo .= "\nRISULTATO VOTAZIONE:\n";
|
||||
$testo .= "- Voti favorevoli: {$this->voti_favorevoli} (millesimi: {$this->millesimi_favorevoli})\n";
|
||||
$testo .= "- Voti contrari: {$this->voti_contrari} (millesimi: {$this->millesimi_contrari})\n";
|
||||
$testo .= "- Astenuti: {$this->astenuti} (millesimi: {$this->millesimi_astenuti})\n";
|
||||
|
||||
return $testo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avvia automazione per spese approvate
|
||||
*/
|
||||
private function avviaAutomazioneSpesa($delibera)
|
||||
{
|
||||
AutomazioneSpesaApprovata::create([
|
||||
'delibera_id' => $delibera->id,
|
||||
'stato_automazione' => 'in_attesa',
|
||||
]);
|
||||
|
||||
// Qui si potrebbe implementare una coda per processare l'automazione
|
||||
// Ad esempio: dispatch(new ProcessaSpesaApprovata($delibera));
|
||||
}
|
||||
}
|
||||
156
app/Models/Preventivo.php
Normal file
156
app/Models/Preventivo.php
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Preventivo extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'anno_gestione',
|
||||
'tipo_gestione',
|
||||
'descrizione',
|
||||
'stato',
|
||||
'importo_totale',
|
||||
'data_creazione',
|
||||
'data_approvazione',
|
||||
'approvato_da_user_id',
|
||||
'note',
|
||||
'versione',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_creazione' => 'date',
|
||||
'data_approvazione' => 'date',
|
||||
'importo_totale' => 'decimal:2',
|
||||
'versione' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Voci Preventivo
|
||||
*/
|
||||
public function voci()
|
||||
{
|
||||
return $this->hasMany(VocePreventivo::class, 'preventivo_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Rate
|
||||
*/
|
||||
public function rate()
|
||||
{
|
||||
return $this->hasMany(Rata::class, 'preventivo_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User che ha approvato
|
||||
*/
|
||||
public function approvatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'approvato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Log Modifiche
|
||||
*/
|
||||
public function logModifiche()
|
||||
{
|
||||
return $this->morphMany(LogModificaPreventivo::class, 'entita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo gestione
|
||||
*/
|
||||
public function scopeTipoGestione($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_gestione', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola importo totale dalle voci
|
||||
*/
|
||||
public function calcolaImportoTotale()
|
||||
{
|
||||
$this->importo_totale = $this->voci()->sum('importo_preventivato');
|
||||
$this->save();
|
||||
return $this->importo_totale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea nuova versione del preventivo
|
||||
*/
|
||||
public function creaVersione($motivo, $userId)
|
||||
{
|
||||
$nuovaVersione = $this->versione + 1;
|
||||
|
||||
// Log della modifica
|
||||
LogModificaPreventivo::create([
|
||||
'entita' => 'preventivo',
|
||||
'entita_id' => $this->id,
|
||||
'versione_precedente' => $this->versione,
|
||||
'versione_nuova' => $nuovaVersione,
|
||||
'utente_id' => $userId,
|
||||
'tipo_operazione' => 'update',
|
||||
'motivo' => $motivo,
|
||||
'dati_precedenti' => $this->getOriginal(),
|
||||
'dati_nuovi' => $this->getAttributes(),
|
||||
]);
|
||||
|
||||
$this->versione = $nuovaVersione;
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera rate dal preventivo
|
||||
*/
|
||||
public function generaRate($numeroRate, $dataInizio, $userId)
|
||||
{
|
||||
$importoPerRata = $this->importo_totale / $numeroRate;
|
||||
$rate = [];
|
||||
|
||||
for ($i = 1; $i <= $numeroRate; $i++) {
|
||||
$dataScadenza = $dataInizio->copy()->addMonths($i - 1);
|
||||
$numeroRata = $this->stabile_id . '/' . $this->anno_gestione . '/' . str_pad($i, 3, '0', STR_PAD_LEFT);
|
||||
|
||||
$rata = Rata::create([
|
||||
'preventivo_id' => $this->id,
|
||||
'numero_rata' => $numeroRata,
|
||||
'descrizione' => "Rata {$i} di {$numeroRate} - {$this->descrizione}",
|
||||
'data_scadenza' => $dataScadenza,
|
||||
'stato' => 'emessa',
|
||||
'importo_totale' => $importoPerRata,
|
||||
'creato_da_user_id' => $userId,
|
||||
]);
|
||||
|
||||
$rate[] = $rata;
|
||||
}
|
||||
|
||||
return $rate;
|
||||
}
|
||||
}
|
||||
96
app/Models/Rata.php
Normal file
96
app/Models/Rata.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Rata extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'rate';
|
||||
|
||||
protected $fillable = [
|
||||
'gestione_id',
|
||||
'unita_immobiliare_id',
|
||||
'soggetto_id',
|
||||
'numero_rata',
|
||||
'descrizione',
|
||||
'importo',
|
||||
'data_scadenza',
|
||||
'data_pagamento',
|
||||
'importo_pagato',
|
||||
'stato',
|
||||
'tipo_rata',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'importo' => 'decimal:2',
|
||||
'importo_pagato' => 'decimal:2',
|
||||
'data_scadenza' => 'date',
|
||||
'data_pagamento' => 'date',
|
||||
'numero_rata' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Gestione
|
||||
*/
|
||||
public function gestione()
|
||||
{
|
||||
return $this->belongsTo(Gestione::class, 'gestione_id', 'id_gestione');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Unità Immobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto
|
||||
*/
|
||||
public function soggetto()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per scadute
|
||||
*/
|
||||
public function scopeScadute($query)
|
||||
{
|
||||
return $query->where('data_scadenza', '<', now())
|
||||
->where('stato', '!=', 'pagata');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per in scadenza
|
||||
*/
|
||||
public function scopeInScadenza($query, $giorni = 30)
|
||||
{
|
||||
return $query->whereBetween('data_scadenza', [now(), now()->addDays($giorni)])
|
||||
->where('stato', '!=', 'pagata');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per importo residuo
|
||||
*/
|
||||
public function getImportoResiduoAttribute()
|
||||
{
|
||||
return $this->importo - $this->importo_pagato;
|
||||
}
|
||||
}
|
||||
108
app/Models/RegistroProtocollo.php
Normal file
108
app/Models/RegistroProtocollo.php
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class RegistroProtocollo extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'registro_protocollo';
|
||||
|
||||
protected $fillable = [
|
||||
'numero_protocollo',
|
||||
'tipo_comunicazione',
|
||||
'assemblea_id',
|
||||
'soggetto_destinatario_id',
|
||||
'soggetto_mittente_id',
|
||||
'oggetto',
|
||||
'contenuto',
|
||||
'canale',
|
||||
'data_invio',
|
||||
'esito',
|
||||
'data_consegna',
|
||||
'data_lettura',
|
||||
'riferimento_esterno',
|
||||
'allegati',
|
||||
'note',
|
||||
'creato_da_user_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_invio' => 'datetime',
|
||||
'data_consegna' => 'datetime',
|
||||
'data_lettura' => 'datetime',
|
||||
'allegati' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Assemblea
|
||||
*/
|
||||
public function assemblea()
|
||||
{
|
||||
return $this->belongsTo(Assemblea::class, 'assemblea_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto destinatario
|
||||
*/
|
||||
public function soggettoDestinatario()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_destinatario_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto mittente
|
||||
*/
|
||||
public function soggettoMittente()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_mittente_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User creatore
|
||||
*/
|
||||
public function creatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'creato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo comunicazione
|
||||
*/
|
||||
public function scopeTipo($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_comunicazione', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per periodo
|
||||
*/
|
||||
public function scopePeriodo($query, $dataInizio, $dataFine)
|
||||
{
|
||||
return $query->whereBetween('data_invio', [$dataInizio, $dataFine]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero protocollo automatico
|
||||
*/
|
||||
public static function generaNumeroProtocollo()
|
||||
{
|
||||
$anno = date('Y');
|
||||
$ultimoProtocollo = self::whereYear('created_at', $anno)
|
||||
->orderBy('numero_protocollo', 'desc')
|
||||
->first();
|
||||
|
||||
if ($ultimoProtocollo) {
|
||||
$numero = intval(substr($ultimoProtocollo->numero_protocollo, -4)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return 'PROT/' . $anno . '/' . str_pad($numero, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
66
app/Models/RichiestaModifica.php
Normal file
66
app/Models/RichiestaModifica.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class RichiestaModifica extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'richieste_modifiche';
|
||||
|
||||
protected $fillable = [
|
||||
'unita_immobiliare_id',
|
||||
'soggetto_richiedente_id',
|
||||
'tipo_modifica',
|
||||
'descrizione',
|
||||
'dati_attuali',
|
||||
'dati_proposti',
|
||||
'stato',
|
||||
'note_amministratore',
|
||||
'data_approvazione',
|
||||
'approvato_da_user_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'dati_attuali' => 'array',
|
||||
'dati_proposti' => 'array',
|
||||
'data_approvazione' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Unità Immobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto richiedente
|
||||
*/
|
||||
public function soggettoRichiedente()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_richiedente_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User che ha approvato
|
||||
*/
|
||||
public function approvatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'approvato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stato
|
||||
*/
|
||||
public function scopeStato($query, $stato)
|
||||
{
|
||||
return $query->where('stato', $stato);
|
||||
}
|
||||
}
|
||||
98
app/Models/RipartizionePreventivo.php
Normal file
98
app/Models/RipartizionePreventivo.php
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class RipartizionePreventivo extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'ripartizioni_preventivo';
|
||||
|
||||
protected $fillable = [
|
||||
'voce_preventivo_id',
|
||||
'unita_immobiliare_id',
|
||||
'quota_calcolata',
|
||||
'quota_modificata',
|
||||
'quota_finale',
|
||||
'versione',
|
||||
'modificato_da_user_id',
|
||||
'motivo_modifica',
|
||||
'data_modifica',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'quota_calcolata' => 'decimal:2',
|
||||
'quota_modificata' => 'decimal:2',
|
||||
'quota_finale' => 'decimal:2',
|
||||
'versione' => 'integer',
|
||||
'data_modifica' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Voce Preventivo
|
||||
*/
|
||||
public function vocePreventivo()
|
||||
{
|
||||
return $this->belongsTo(VocePreventivo::class, 'voce_preventivo_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Unità Immobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User che ha modificato
|
||||
*/
|
||||
public function modificatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'modificato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifica quota con versionamento
|
||||
*/
|
||||
public function modificaQuota($nuovaQuota, $motivo, $userId)
|
||||
{
|
||||
$versionePrecedente = $this->versione;
|
||||
$quotaPrecedente = $this->quota_finale;
|
||||
|
||||
// Log della modifica
|
||||
LogModificaPreventivo::create([
|
||||
'entita' => 'ripartizione',
|
||||
'entita_id' => $this->id,
|
||||
'versione_precedente' => $versionePrecedente,
|
||||
'versione_nuova' => $versionePrecedente + 1,
|
||||
'utente_id' => $userId,
|
||||
'tipo_operazione' => 'update',
|
||||
'motivo' => $motivo,
|
||||
'dati_precedenti' => ['quota_finale' => $quotaPrecedente],
|
||||
'dati_nuovi' => ['quota_finale' => $nuovaQuota],
|
||||
'diff' => [
|
||||
'campo' => 'quota_finale',
|
||||
'da' => $quotaPrecedente,
|
||||
'a' => $nuovaQuota,
|
||||
'differenza' => $nuovaQuota - $quotaPrecedente,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->update([
|
||||
'quota_modificata' => $nuovaQuota,
|
||||
'quota_finale' => $nuovaQuota,
|
||||
'versione' => $versionePrecedente + 1,
|
||||
'modificato_da_user_id' => $userId,
|
||||
'motivo_modifica' => $motivo,
|
||||
'data_modifica' => now(),
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
141
app/Models/ScritturaBilancio.php
Normal file
141
app/Models/ScritturaBilancio.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ScritturaBilancio extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'scritture_bilancio';
|
||||
|
||||
protected $fillable = [
|
||||
'bilancio_id',
|
||||
'numero_scrittura',
|
||||
'data_scrittura',
|
||||
'descrizione',
|
||||
'tipo_scrittura',
|
||||
'importo_totale',
|
||||
'riferimento_documento',
|
||||
'movimento_contabile_id',
|
||||
'creato_da_user_id',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_scrittura' => 'date',
|
||||
'importo_totale' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Bilancio
|
||||
*/
|
||||
public function bilancio()
|
||||
{
|
||||
return $this->belongsTo(Bilancio::class, 'bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Dettagli (Dare/Avere)
|
||||
*/
|
||||
public function dettagli()
|
||||
{
|
||||
return $this->hasMany(DettaglioScritturaBilancio::class, 'scrittura_bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Ripartizioni
|
||||
*/
|
||||
public function ripartizioni()
|
||||
{
|
||||
return $this->hasMany(RipartizioneBilancio::class, 'scrittura_bilancio_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Movimento Contabile
|
||||
*/
|
||||
public function movimentoContabile()
|
||||
{
|
||||
return $this->belongsTo(MovimentoContabile::class, 'movimento_contabile_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User creatore
|
||||
*/
|
||||
public function creatoDa()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'creato_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica quadratura dare/avere
|
||||
*/
|
||||
public function verificaQuadratura()
|
||||
{
|
||||
$totaleDare = $this->dettagli()->sum('importo_dare');
|
||||
$totaleAvere = $this->dettagli()->sum('importo_avere');
|
||||
|
||||
return abs($totaleDare - $totaleAvere) < 0.01; // Tolleranza centesimi
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea scrittura in partita doppia
|
||||
*/
|
||||
public static function creaScrittura($bilancioId, $data, $descrizione, $dettagli, $userId)
|
||||
{
|
||||
$importoTotale = collect($dettagli)->sum(function($dettaglio) {
|
||||
return max($dettaglio['dare'] ?? 0, $dettaglio['avere'] ?? 0);
|
||||
});
|
||||
|
||||
$scrittura = self::create([
|
||||
'bilancio_id' => $bilancioId,
|
||||
'numero_scrittura' => self::generaNumeroScrittura($bilancioId),
|
||||
'data_scrittura' => $data,
|
||||
'descrizione' => $descrizione,
|
||||
'tipo_scrittura' => 'gestione',
|
||||
'importo_totale' => $importoTotale,
|
||||
'creato_da_user_id' => $userId,
|
||||
]);
|
||||
|
||||
// Crea dettagli dare/avere
|
||||
foreach ($dettagli as $dettaglio) {
|
||||
DettaglioScritturaBilancio::create([
|
||||
'scrittura_bilancio_id' => $scrittura->id,
|
||||
'conto_id' => $dettaglio['conto_id'],
|
||||
'importo_dare' => $dettaglio['dare'] ?? 0,
|
||||
'importo_avere' => $dettaglio['avere'] ?? 0,
|
||||
'descrizione_dettaglio' => $dettaglio['descrizione'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
// Verifica quadratura
|
||||
if (!$scrittura->verificaQuadratura()) {
|
||||
throw new \Exception('Scrittura non quadra: totale dare diverso da totale avere');
|
||||
}
|
||||
|
||||
return $scrittura;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera numero scrittura progressivo
|
||||
*/
|
||||
private static function generaNumeroScrittura($bilancioId)
|
||||
{
|
||||
$bilancio = Bilancio::find($bilancioId);
|
||||
$ultimaScrittura = self::where('bilancio_id', $bilancioId)
|
||||
->orderBy('numero_scrittura', 'desc')
|
||||
->first();
|
||||
|
||||
if ($ultimaScrittura) {
|
||||
$numero = intval(substr($ultimaScrittura->numero_scrittura, -4)) + 1;
|
||||
} else {
|
||||
$numero = 1;
|
||||
}
|
||||
|
||||
return 'SCR/' . $bilancio->anno_esercizio . '/' . str_pad($numero, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
74
app/Models/Stabile.php
Normal file
74
app/Models/Stabile.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Stabile extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'stabili';
|
||||
protected $primaryKey = 'id_stabile';
|
||||
|
||||
protected $fillable = [
|
||||
'amministratore_id',
|
||||
'denominazione',
|
||||
'codice_fiscale',
|
||||
'cod_fisc_amministratore',
|
||||
'indirizzo',
|
||||
'citta',
|
||||
'cap',
|
||||
'provincia',
|
||||
'note',
|
||||
'old_id',
|
||||
'stato',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Amministratore
|
||||
*/
|
||||
public function amministratore()
|
||||
{
|
||||
return $this->belongsTo(Amministratore::class, 'amministratore_id', 'id_amministratore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con UnitaImmobiliari
|
||||
*/
|
||||
public function unitaImmobiliari()
|
||||
{
|
||||
return $this->hasMany(UnitaImmobiliare::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tickets
|
||||
*/
|
||||
public function tickets()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per stabili attivi
|
||||
*/
|
||||
public function scopeAttivi($query)
|
||||
{
|
||||
return $query->where('stato', 'attivo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per il nome completo dell'indirizzo
|
||||
*/
|
||||
public function getIndirizzoCompletoAttribute()
|
||||
{
|
||||
return $this->indirizzo . ', ' . $this->cap . ' ' . $this->citta .
|
||||
($this->provincia ? ' (' . $this->provincia . ')' : '');
|
||||
}
|
||||
}
|
||||
71
app/Models/TabellaMillesimale.php
Normal file
71
app/Models/TabellaMillesimale.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TabellaMillesimale extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'tabelle_millesimali';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'nome',
|
||||
'descrizione',
|
||||
'tipo',
|
||||
'attiva',
|
||||
'data_approvazione',
|
||||
'ordinamento',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'attiva' => 'boolean',
|
||||
'data_approvazione' => 'date',
|
||||
'ordinamento' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Dettagli Millesimi
|
||||
*/
|
||||
public function dettagli()
|
||||
{
|
||||
return $this->hasMany(DettaglioTabellaMillesimale::class, 'tabella_millesimale_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tabelle attive
|
||||
*/
|
||||
public function scopeAttive($query)
|
||||
{
|
||||
return $query->where('attiva', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope ordinato
|
||||
*/
|
||||
public function scopeOrdinato($query)
|
||||
{
|
||||
return $query->orderBy('ordinamento')->orderBy('nome');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola il totale millesimi
|
||||
*/
|
||||
public function getTotaleMillesimiAttribute()
|
||||
{
|
||||
return $this->dettagli()->sum('millesimi');
|
||||
}
|
||||
}
|
||||
119
app/Models/Ticket.php
Normal file
119
app/Models/Ticket.php
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Ticket extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'unita_immobiliare_id',
|
||||
'soggetto_richiedente_id',
|
||||
'categoria_ticket_id',
|
||||
'aperto_da_user_id',
|
||||
'assegnato_a_user_id',
|
||||
'assegnato_a_fornitore_id',
|
||||
'titolo',
|
||||
'descrizione',
|
||||
'luogo_intervento',
|
||||
'data_apertura',
|
||||
'data_scadenza_prevista',
|
||||
'data_risoluzione_effettiva',
|
||||
'data_chiusura_effettiva',
|
||||
'stato',
|
||||
'priorita',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data_apertura' => 'datetime',
|
||||
'data_scadenza_prevista' => 'date',
|
||||
'data_risoluzione_effettiva' => 'date',
|
||||
'data_chiusura_effettiva' => 'date',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con UnitaImmobiliare
|
||||
*/
|
||||
public function unitaImmobiliare()
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Soggetto richiedente
|
||||
*/
|
||||
public function soggettoRichiedente()
|
||||
{
|
||||
return $this->belongsTo(Soggetto::class, 'soggetto_richiedente_id', 'id_soggetto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con CategoriaTicket
|
||||
*/
|
||||
public function categoriaTicket()
|
||||
{
|
||||
return $this->belongsTo(CategoriaTicket::class, 'categoria_ticket_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User che ha aperto il ticket
|
||||
*/
|
||||
public function apertoUser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'aperto_da_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con User assegnato
|
||||
*/
|
||||
public function assegnatoUser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assegnato_a_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Fornitore assegnato
|
||||
*/
|
||||
public function assegnatoFornitore()
|
||||
{
|
||||
return $this->belongsTo(Fornitore::class, 'assegnato_a_fornitore_id', 'id_fornitore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per ticket aperti
|
||||
*/
|
||||
public function scopeAperti($query)
|
||||
{
|
||||
return $query->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per ticket chiusi
|
||||
*/
|
||||
public function scopeChiusi($query)
|
||||
{
|
||||
return $query->whereIn('stato', ['Risolto', 'Chiuso']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per priorità
|
||||
*/
|
||||
public function scopePriorita($query, $priorita)
|
||||
{
|
||||
return $query->where('priorita', $priorita);
|
||||
}
|
||||
}
|
||||
75
app/Models/UnitaImmobiliare.php
Normal file
75
app/Models/UnitaImmobiliare.php
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UnitaImmobiliare extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'unita_immobiliari';
|
||||
protected $primaryKey = 'id_unita';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'interno',
|
||||
'scala',
|
||||
'piano',
|
||||
'fabbricato',
|
||||
'millesimi_proprieta',
|
||||
'categoria_catastale',
|
||||
'superficie',
|
||||
'vani',
|
||||
'indirizzo',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'millesimi_proprieta' => 'decimal:4',
|
||||
'superficie' => 'decimal:2',
|
||||
'vani' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tickets
|
||||
*/
|
||||
public function tickets()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'unita_immobiliare_id', 'id_unita');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per identificazione completa dell'unità
|
||||
*/
|
||||
public function getIdentificazioneCompiletaAttribute()
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
if ($this->fabbricato) $parts[] = 'Fabb. ' . $this->fabbricato;
|
||||
if ($this->scala) $parts[] = 'Scala ' . $this->scala;
|
||||
if ($this->piano) $parts[] = 'Piano ' . $this->piano;
|
||||
if ($this->interno) $parts[] = 'Int. ' . $this->interno;
|
||||
|
||||
return implode(', ', $parts) ?: 'N/A';
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor per l'indirizzo completo
|
||||
*/
|
||||
public function getIndirizzoCompletoAttribute()
|
||||
{
|
||||
return $this->indirizzo ?: $this->stabile->indirizzo_completo;
|
||||
}
|
||||
}
|
||||
101
app/Models/VocePreventivo.php
Normal file
101
app/Models/VocePreventivo.php
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class VocePreventivo extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'voci_preventivo';
|
||||
|
||||
protected $fillable = [
|
||||
'preventivo_id',
|
||||
'codice',
|
||||
'descrizione',
|
||||
'importo_preventivato',
|
||||
'importo_effettivo',
|
||||
'tabella_millesimale_id',
|
||||
'voce_spesa_id',
|
||||
'ricorrente',
|
||||
'frequenza',
|
||||
'data_scadenza_prevista',
|
||||
'ordinamento',
|
||||
'note',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'importo_preventivato' => 'decimal:2',
|
||||
'importo_effettivo' => 'decimal:2',
|
||||
'ricorrente' => 'boolean',
|
||||
'data_scadenza_prevista' => 'date',
|
||||
'ordinamento' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Preventivo
|
||||
*/
|
||||
public function preventivo()
|
||||
{
|
||||
return $this->belongsTo(Preventivo::class, 'preventivo_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tabella Millesimale
|
||||
*/
|
||||
public function tabellaMillesimale()
|
||||
{
|
||||
return $this->belongsTo(TabellaMillesimale::class, 'tabella_millesimale_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Voce Spesa
|
||||
*/
|
||||
public function voceSpesa()
|
||||
{
|
||||
return $this->belongsTo(VoceSpesa::class, 'voce_spesa_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Ripartizioni
|
||||
*/
|
||||
public function ripartizioni()
|
||||
{
|
||||
return $this->hasMany(RipartizionePreventivo::class, 'voce_preventivo_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola ripartizione automatica
|
||||
*/
|
||||
public function calcolaRipartizione()
|
||||
{
|
||||
if (!$this->tabella_millesimale_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dettagliMillesimi = $this->tabellaMillesimale->dettagli;
|
||||
$totaleMillesimi = $dettagliMillesimi->sum('millesimi');
|
||||
|
||||
foreach ($dettagliMillesimi as $dettaglio) {
|
||||
$quota = ($this->importo_preventivato * $dettaglio->millesimi) / $totaleMillesimi;
|
||||
|
||||
RipartizionePreventivo::updateOrCreate(
|
||||
[
|
||||
'voce_preventivo_id' => $this->id,
|
||||
'unita_immobiliare_id' => $dettaglio->unita_immobiliare_id,
|
||||
],
|
||||
[
|
||||
'quota_calcolata' => $quota,
|
||||
'quota_finale' => $quota,
|
||||
'versione' => 1,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
73
app/Models/VoceSpesa.php
Normal file
73
app/Models/VoceSpesa.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class VoceSpesa extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'voci_spesa';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'codice',
|
||||
'descrizione',
|
||||
'tipo_gestione',
|
||||
'categoria',
|
||||
'tabella_millesimale_default_id',
|
||||
'ritenuta_acconto_default',
|
||||
'attiva',
|
||||
'ordinamento',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'ritenuta_acconto_default' => 'decimal:2',
|
||||
'attiva' => 'boolean',
|
||||
'ordinamento' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relazione con Stabile
|
||||
*/
|
||||
public function stabile()
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relazione con Tabella Millesimale Default
|
||||
*/
|
||||
public function tabellaMillesimaleDefault()
|
||||
{
|
||||
return $this->belongsTo(TabellaMillesimale::class, 'tabella_millesimale_default_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per voci attive
|
||||
*/
|
||||
public function scopeAttive($query)
|
||||
{
|
||||
return $query->where('attiva', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope per tipo gestione
|
||||
*/
|
||||
public function scopeTipoGestione($query, $tipo)
|
||||
{
|
||||
return $query->where('tipo_gestione', $tipo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope ordinato
|
||||
*/
|
||||
public function scopeOrdinato($query)
|
||||
{
|
||||
return $query->orderBy('ordinamento')->orderBy('descrizione');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// Rinomina la tabella condomini in stabili
|
||||
Schema::rename('condomini', 'stabili');
|
||||
|
||||
// Rinomina la colonna nome in denominazione
|
||||
Schema::table('stabili', function (Blueprint $table) {
|
||||
$table->renameColumn('nome', 'denominazione');
|
||||
$table->renameColumn('id_condominio', 'id_stabile');
|
||||
});
|
||||
|
||||
// Aggiorna le foreign key nelle tabelle correlate
|
||||
if (Schema::hasTable('unita_immobiliari')) {
|
||||
Schema::table('unita_immobiliari', function (Blueprint $table) {
|
||||
$table->dropForeign(['condominio_id']);
|
||||
$table->renameColumn('condominio_id', 'stabile_id');
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasTable('tickets')) {
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropForeign(['condominio_id']);
|
||||
$table->renameColumn('condominio_id', 'stabile_id');
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Ripristina le foreign key nelle tabelle correlate
|
||||
if (Schema::hasTable('tickets')) {
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropForeign(['stabile_id']);
|
||||
$table->renameColumn('stabile_id', 'condominio_id');
|
||||
$table->foreign('condominio_id')->references('id_condominio')->on('condomini')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasTable('unita_immobiliari')) {
|
||||
Schema::table('unita_immobiliari', function (Blueprint $table) {
|
||||
$table->dropForeign(['stabile_id']);
|
||||
$table->renameColumn('stabile_id', 'condominio_id');
|
||||
$table->foreign('condominio_id')->references('id_condominio')->on('condomini')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
// Ripristina le colonne originali
|
||||
Schema::table('stabili', function (Blueprint $table) {
|
||||
$table->renameColumn('denominazione', 'nome');
|
||||
$table->renameColumn('id_stabile', 'id_condominio');
|
||||
});
|
||||
|
||||
// Rinomina la tabella stabili in condomini
|
||||
Schema::rename('stabili', 'condomini');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Tabella gestioni
|
||||
Schema::create('gestioni', function (Blueprint $table) {
|
||||
$table->id('id_gestione');
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->year('anno_gestione');
|
||||
$table->enum('tipo_gestione', ['ordinaria', 'riscaldamento', 'straordinaria', 'acqua', 'altro']);
|
||||
$table->date('data_inizio');
|
||||
$table->date('data_fine');
|
||||
$table->string('descrizione')->nullable();
|
||||
$table->enum('stato', ['bozza', 'attiva', 'chiusa'])->default('bozza');
|
||||
$table->boolean('preventivo_approvato')->default(false);
|
||||
$table->date('data_approvazione')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->index(['stabile_id', 'anno_gestione', 'tipo_gestione']);
|
||||
});
|
||||
|
||||
// Tabella voci_spesa
|
||||
Schema::create('voci_spesa', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->string('codice', 20);
|
||||
$table->string('descrizione');
|
||||
$table->enum('tipo_gestione', ['ordinaria', 'riscaldamento', 'straordinaria', 'acqua', 'altro']);
|
||||
$table->string('categoria', 100)->nullable();
|
||||
$table->unsignedBigInteger('tabella_millesimale_default_id')->nullable();
|
||||
$table->decimal('ritenuta_acconto_default', 5, 2)->default(0);
|
||||
$table->boolean('attiva')->default(true);
|
||||
$table->integer('ordinamento')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->unique(['stabile_id', 'codice']);
|
||||
});
|
||||
|
||||
// Tabella tabelle_millesimali
|
||||
Schema::create('tabelle_millesimali', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->string('nome');
|
||||
$table->text('descrizione')->nullable();
|
||||
$table->enum('tipo', ['proprieta', 'riscaldamento', 'ascensore', 'scale', 'altro']);
|
||||
$table->boolean('attiva')->default(true);
|
||||
$table->date('data_approvazione')->nullable();
|
||||
$table->integer('ordinamento')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella dettagli_tabelle_millesimali
|
||||
Schema::create('dettagli_tabelle_millesimali', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tabella_millesimale_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->decimal('millesimi', 10, 4);
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('tabella_millesimale_id')->references('id')->on('tabelle_millesimali')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->unique(['tabella_millesimale_id', 'unita_immobiliare_id'], 'unique_tabella_unita');
|
||||
});
|
||||
|
||||
// Tabella documenti
|
||||
Schema::create('documenti', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('documentable');
|
||||
$table->string('nome_file');
|
||||
$table->string('path_file');
|
||||
$table->string('tipo_documento', 100);
|
||||
$table->unsignedBigInteger('dimensione_file')->nullable();
|
||||
$table->string('mime_type', 100)->nullable();
|
||||
$table->text('descrizione')->nullable();
|
||||
$table->json('xml_data')->nullable();
|
||||
$table->string('hash_file', 64)->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['documentable_type', 'documentable_id']);
|
||||
$table->index('tipo_documento');
|
||||
});
|
||||
|
||||
// Tabella movimenti_contabili
|
||||
Schema::create('movimenti_contabili', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->unsignedBigInteger('gestione_id');
|
||||
$table->unsignedBigInteger('fornitore_id')->nullable();
|
||||
$table->unsignedBigInteger('documento_id')->nullable();
|
||||
$table->string('protocollo', 50)->unique();
|
||||
$table->date('data_registrazione');
|
||||
$table->date('data_documento');
|
||||
$table->string('numero_documento', 50);
|
||||
$table->text('descrizione');
|
||||
$table->enum('tipo_movimento', ['entrata', 'uscita']);
|
||||
$table->decimal('importo_totale', 10, 2);
|
||||
$table->decimal('ritenuta_acconto', 10, 2)->default(0);
|
||||
$table->decimal('importo_netto', 10, 2);
|
||||
$table->enum('stato', ['bozza', 'registrato', 'contabilizzato', 'annullato'])->default('bozza');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->foreign('gestione_id')->references('id_gestione')->on('gestioni')->onDelete('cascade');
|
||||
$table->foreign('fornitore_id')->references('id_fornitore')->on('fornitori')->onDelete('set null');
|
||||
$table->foreign('documento_id')->references('id')->on('documenti')->onDelete('set null');
|
||||
|
||||
$table->index(['stabile_id', 'data_registrazione']);
|
||||
$table->index(['gestione_id', 'tipo_movimento']);
|
||||
});
|
||||
|
||||
// Tabella dettagli_movimenti (partita doppia)
|
||||
Schema::create('dettagli_movimenti', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('movimento_id');
|
||||
$table->unsignedBigInteger('conto_id')->nullable();
|
||||
$table->unsignedBigInteger('voce_spesa_id')->nullable();
|
||||
$table->unsignedBigInteger('tabella_millesimale_id')->nullable();
|
||||
$table->text('descrizione')->nullable();
|
||||
$table->decimal('importo_dare', 10, 2)->default(0);
|
||||
$table->decimal('importo_avere', 10, 2)->default(0);
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('movimento_id')->references('id')->on('movimenti_contabili')->onDelete('cascade');
|
||||
$table->foreign('voce_spesa_id')->references('id')->on('voci_spesa')->onDelete('set null');
|
||||
$table->foreign('tabella_millesimale_id')->references('id')->on('tabelle_millesimali')->onDelete('set null');
|
||||
});
|
||||
|
||||
// Tabella banche
|
||||
Schema::create('banche', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->string('denominazione');
|
||||
$table->string('iban', 34);
|
||||
$table->string('bic_swift', 11)->nullable();
|
||||
$table->string('agenzia')->nullable();
|
||||
$table->string('indirizzo_agenzia')->nullable();
|
||||
$table->enum('tipo_conto', ['corrente', 'deposito', 'altro'])->default('corrente');
|
||||
$table->decimal('saldo_iniziale', 10, 2)->default(0);
|
||||
$table->date('data_apertura')->nullable();
|
||||
$table->enum('stato', ['attivo', 'chiuso'])->default('attivo');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->unique(['stabile_id', 'iban']);
|
||||
});
|
||||
|
||||
// Tabella movimenti_bancari
|
||||
Schema::create('movimenti_bancari', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('banca_id');
|
||||
$table->unsignedBigInteger('movimento_contabile_id')->nullable();
|
||||
$table->date('data_valuta');
|
||||
$table->date('data_contabile');
|
||||
$table->enum('tipo_movimento', ['entrata', 'uscita']);
|
||||
$table->decimal('importo', 10, 2);
|
||||
$table->text('causale');
|
||||
$table->string('beneficiario')->nullable();
|
||||
$table->string('ordinante')->nullable();
|
||||
$table->string('cro_tro', 50)->nullable();
|
||||
$table->enum('stato_riconciliazione', ['da_riconciliare', 'riconciliato', 'sospeso'])->default('da_riconciliare');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('banca_id')->references('id')->on('banche')->onDelete('cascade');
|
||||
$table->foreign('movimento_contabile_id')->references('id')->on('movimenti_contabili')->onDelete('set null');
|
||||
|
||||
$table->index(['banca_id', 'data_valuta']);
|
||||
$table->index('stato_riconciliazione');
|
||||
});
|
||||
|
||||
// Tabella rate
|
||||
Schema::create('rate', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('gestione_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->unsignedBigInteger('soggetto_id');
|
||||
$table->integer('numero_rata');
|
||||
$table->string('descrizione');
|
||||
$table->decimal('importo', 10, 2);
|
||||
$table->date('data_scadenza');
|
||||
$table->date('data_pagamento')->nullable();
|
||||
$table->decimal('importo_pagato', 10, 2)->default(0);
|
||||
$table->enum('stato', ['da_pagare', 'pagata', 'parziale', 'insoluta'])->default('da_pagare');
|
||||
$table->enum('tipo_rata', ['ordinaria', 'riscaldamento', 'straordinaria', 'conguaglio'])->default('ordinaria');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('gestione_id')->references('id_gestione')->on('gestioni')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
|
||||
$table->index(['gestione_id', 'data_scadenza']);
|
||||
$table->index(['unita_immobiliare_id', 'stato']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('rate');
|
||||
Schema::dropIfExists('movimenti_bancari');
|
||||
Schema::dropIfExists('banche');
|
||||
Schema::dropIfExists('dettagli_movimenti');
|
||||
Schema::dropIfExists('movimenti_contabili');
|
||||
Schema::dropIfExists('documenti');
|
||||
Schema::dropIfExists('dettagli_tabelle_millesimali');
|
||||
Schema::dropIfExists('tabelle_millesimali');
|
||||
Schema::dropIfExists('voci_spesa');
|
||||
Schema::dropIfExists('gestioni');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('richieste_modifiche', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->unsignedBigInteger('soggetto_richiedente_id');
|
||||
$table->enum('tipo_modifica', ['anagrafica', 'catastale', 'proprieta']);
|
||||
$table->text('descrizione');
|
||||
$table->json('dati_attuali');
|
||||
$table->json('dati_proposti');
|
||||
$table->enum('stato', ['in_attesa', 'approvata', 'rifiutata'])->default('in_attesa');
|
||||
$table->text('note_amministratore')->nullable();
|
||||
$table->datetime('data_approvazione')->nullable();
|
||||
$table->unsignedBigInteger('approvato_da_user_id')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('soggetto_richiedente_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
$table->foreign('approvato_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
|
||||
$table->index(['stato', 'created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('richieste_modifiche');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Tabella preventivi
|
||||
Schema::create('preventivi', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->year('anno_gestione');
|
||||
$table->enum('tipo_gestione', ['ordinaria', 'riscaldamento', 'straordinaria', 'acqua', 'altro']);
|
||||
$table->string('descrizione');
|
||||
$table->enum('stato', ['bozza', 'provvisorio', 'definitivo', 'approvato', 'archiviato'])->default('bozza');
|
||||
$table->decimal('importo_totale', 12, 2)->default(0);
|
||||
$table->date('data_creazione');
|
||||
$table->date('data_approvazione')->nullable();
|
||||
$table->unsignedBigInteger('approvato_da_user_id')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->integer('versione')->default(1);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->foreign('approvato_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
$table->index(['stabile_id', 'anno_gestione', 'tipo_gestione']);
|
||||
});
|
||||
|
||||
// Tabella voci preventivo
|
||||
Schema::create('voci_preventivo', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('preventivo_id');
|
||||
$table->string('codice', 20);
|
||||
$table->string('descrizione');
|
||||
$table->decimal('importo_preventivato', 10, 2);
|
||||
$table->decimal('importo_effettivo', 10, 2)->default(0);
|
||||
$table->unsignedBigInteger('tabella_millesimale_id')->nullable();
|
||||
$table->unsignedBigInteger('voce_spesa_id')->nullable();
|
||||
$table->boolean('ricorrente')->default(false);
|
||||
$table->enum('frequenza', ['mensile', 'trimestrale', 'semestrale', 'annuale'])->nullable();
|
||||
$table->date('data_scadenza_prevista')->nullable();
|
||||
$table->integer('ordinamento')->default(0);
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('preventivo_id')->references('id')->on('preventivi')->onDelete('cascade');
|
||||
$table->foreign('tabella_millesimale_id')->references('id')->on('tabelle_millesimali')->onDelete('set null');
|
||||
$table->foreign('voce_spesa_id')->references('id')->on('voci_spesa')->onDelete('set null');
|
||||
});
|
||||
|
||||
// Tabella ripartizioni preventivo
|
||||
Schema::create('ripartizioni_preventivo', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('voce_preventivo_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->decimal('quota_calcolata', 10, 2);
|
||||
$table->decimal('quota_modificata', 10, 2)->nullable();
|
||||
$table->decimal('quota_finale', 10, 2);
|
||||
$table->integer('versione')->default(1);
|
||||
$table->unsignedBigInteger('modificato_da_user_id')->nullable();
|
||||
$table->string('motivo_modifica')->nullable();
|
||||
$table->timestamp('data_modifica')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('voce_preventivo_id')->references('id')->on('voci_preventivo')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('modificato_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
$table->unique(['voce_preventivo_id', 'unita_immobiliare_id'], 'unique_voce_unita');
|
||||
});
|
||||
|
||||
// Tabella rate
|
||||
Schema::create('rate', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('preventivo_id');
|
||||
$table->string('numero_rata', 50)->unique();
|
||||
$table->string('descrizione');
|
||||
$table->date('data_scadenza');
|
||||
$table->enum('stato', ['bozza', 'emessa', 'modificata', 'annullata'])->default('bozza');
|
||||
$table->decimal('importo_totale', 12, 2);
|
||||
$table->integer('versione')->default(1);
|
||||
$table->unsignedBigInteger('creato_da_user_id');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('preventivo_id')->references('id')->on('preventivi')->onDelete('cascade');
|
||||
$table->foreign('creato_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella rate unità (dettaglio rate per ogni unità)
|
||||
Schema::create('rate_unita', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('rata_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->unsignedBigInteger('soggetto_id');
|
||||
$table->decimal('importo_originale', 10, 2);
|
||||
$table->decimal('importo_modificato', 10, 2)->nullable();
|
||||
$table->decimal('importo_finale', 10, 2);
|
||||
$table->decimal('importo_pagato', 10, 2)->default(0);
|
||||
$table->enum('stato_pagamento', ['da_pagare', 'parziale', 'pagata', 'insoluta'])->default('da_pagare');
|
||||
$table->date('data_pagamento')->nullable();
|
||||
$table->integer('versione')->default(1);
|
||||
$table->unsignedBigInteger('modificato_da_user_id')->nullable();
|
||||
$table->string('motivo_modifica')->nullable();
|
||||
$table->timestamp('data_modifica')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('rata_id')->references('id')->on('rate')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
$table->foreign('modificato_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
$table->unique(['rata_id', 'unita_immobiliare_id'], 'unique_rata_unita');
|
||||
});
|
||||
|
||||
// Tabella incassi
|
||||
Schema::create('incassi', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('rata_unita_id');
|
||||
$table->date('data_incasso');
|
||||
$table->decimal('importo', 10, 2);
|
||||
$table->enum('metodo_pagamento', ['bonifico', 'contanti', 'assegno', 'pos', 'altro']);
|
||||
$table->string('riferimento_bancario')->nullable();
|
||||
$table->string('numero_documento')->nullable();
|
||||
$table->unsignedBigInteger('movimento_bancario_id')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('rata_unita_id')->references('id')->on('rate_unita')->onDelete('cascade');
|
||||
$table->index(['data_incasso', 'metodo_pagamento']);
|
||||
});
|
||||
|
||||
// Tabella log modifiche (audit trail stile GIT)
|
||||
Schema::create('log_modifiche_preventivo', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('entita'); // 'preventivo', 'voce', 'ripartizione', 'rata'
|
||||
$table->unsignedBigInteger('entita_id');
|
||||
$table->integer('versione_precedente');
|
||||
$table->integer('versione_nuova');
|
||||
$table->unsignedBigInteger('utente_id');
|
||||
$table->string('tipo_operazione'); // 'create', 'update', 'delete'
|
||||
$table->text('motivo');
|
||||
$table->json('dati_precedenti')->nullable();
|
||||
$table->json('dati_nuovi');
|
||||
$table->json('diff')->nullable(); // Differenze stile GIT
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('utente_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index(['entita', 'entita_id', 'versione_nuova']);
|
||||
});
|
||||
|
||||
// Tabella pianificazione spese
|
||||
Schema::create('pianificazione_spese', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->unsignedBigInteger('preventivo_id')->nullable();
|
||||
$table->string('descrizione');
|
||||
$table->decimal('importo_previsto', 10, 2);
|
||||
$table->date('data_scadenza_prevista');
|
||||
$table->enum('tipo', ['ricorrente', 'straordinaria', 'manutenzione']);
|
||||
$table->enum('stato', ['pianificata', 'confermata', 'pagata', 'annullata'])->default('pianificata');
|
||||
$table->boolean('notifica_inviata')->default(false);
|
||||
$table->integer('giorni_preavviso')->default(30);
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->foreign('preventivo_id')->references('id')->on('preventivi')->onDelete('set null');
|
||||
$table->index(['data_scadenza_prevista', 'stato']);
|
||||
});
|
||||
|
||||
// Tabella configurazione banche per automazione
|
||||
Schema::create('configurazioni_banche', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->string('nome_banca');
|
||||
$table->string('iban', 34);
|
||||
$table->string('api_endpoint')->nullable();
|
||||
$table->text('credenziali_api')->nullable(); // Encrypted
|
||||
$table->enum('tipo_importazione', ['api', 'csv', 'cbi', 'manuale']);
|
||||
$table->json('mapping_campi')->nullable();
|
||||
$table->boolean('attiva')->default(true);
|
||||
$table->timestamp('ultima_sincronizzazione')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('configurazioni_banche');
|
||||
Schema::dropIfExists('pianificazione_spese');
|
||||
Schema::dropIfExists('log_modifiche_preventivo');
|
||||
Schema::dropIfExists('incassi');
|
||||
Schema::dropIfExists('rate_unita');
|
||||
Schema::dropIfExists('rate');
|
||||
Schema::dropIfExists('ripartizioni_preventivo');
|
||||
Schema::dropIfExists('voci_preventivo');
|
||||
Schema::dropIfExists('preventivi');
|
||||
}
|
||||
};
|
||||
273
database/migrations/2024_01_05_000001_create_bilanci_tables.php
Normal file
273
database/migrations/2024_01_05_000001_create_bilanci_tables.php
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Tabella bilanci
|
||||
Schema::create('bilanci', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->unsignedBigInteger('gestione_id');
|
||||
$table->year('anno_esercizio');
|
||||
$table->date('data_inizio_esercizio');
|
||||
$table->date('data_fine_esercizio');
|
||||
$table->enum('tipo_gestione', ['ordinaria', 'riscaldamento', 'straordinaria', 'acqua', 'altro']);
|
||||
$table->string('descrizione');
|
||||
$table->enum('stato', ['bozza', 'provvisorio', 'definitivo', 'approvato', 'chiuso'])->default('bozza');
|
||||
$table->decimal('totale_entrate', 12, 2)->default(0);
|
||||
$table->decimal('totale_uscite', 12, 2)->default(0);
|
||||
$table->decimal('risultato_gestione', 12, 2)->default(0); // Avanzo/Disavanzo
|
||||
$table->date('data_approvazione')->nullable();
|
||||
$table->unsignedBigInteger('approvato_da_user_id')->nullable();
|
||||
$table->date('data_chiusura')->nullable();
|
||||
$table->unsignedBigInteger('chiuso_da_user_id')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->integer('versione')->default(1);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->foreign('gestione_id')->references('id_gestione')->on('gestioni')->onDelete('cascade');
|
||||
$table->foreign('approvato_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
$table->foreign('chiuso_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
$table->index(['stabile_id', 'anno_esercizio', 'tipo_gestione']);
|
||||
});
|
||||
|
||||
// Tabella piano dei conti
|
||||
Schema::create('piano_conti', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->string('codice', 20);
|
||||
$table->string('descrizione');
|
||||
$table->enum('tipo_conto', ['attivo', 'passivo', 'costo', 'ricavo']);
|
||||
$table->enum('categoria', ['patrimoniale', 'economico']);
|
||||
$table->unsignedBigInteger('conto_padre_id')->nullable();
|
||||
$table->integer('livello')->default(1);
|
||||
$table->boolean('attivo')->default(true);
|
||||
$table->integer('ordinamento')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->foreign('conto_padre_id')->references('id')->on('piano_conti')->onDelete('set null');
|
||||
$table->unique(['stabile_id', 'codice']);
|
||||
});
|
||||
|
||||
// Tabella scritture contabili (partita doppia)
|
||||
Schema::create('scritture_bilancio', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('bilancio_id');
|
||||
$table->string('numero_scrittura', 50);
|
||||
$table->date('data_scrittura');
|
||||
$table->string('descrizione');
|
||||
$table->enum('tipo_scrittura', ['apertura', 'gestione', 'chiusura', 'rettifica']);
|
||||
$table->decimal('importo_totale', 12, 2);
|
||||
$table->string('riferimento_documento')->nullable();
|
||||
$table->unsignedBigInteger('movimento_contabile_id')->nullable();
|
||||
$table->unsignedBigInteger('creato_da_user_id');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('bilancio_id')->references('id')->on('bilanci')->onDelete('cascade');
|
||||
$table->foreign('movimento_contabile_id')->references('id')->on('movimenti_contabili')->onDelete('set null');
|
||||
$table->foreign('creato_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index(['bilancio_id', 'data_scrittura']);
|
||||
});
|
||||
|
||||
// Tabella dettagli scritture (dare/avere)
|
||||
Schema::create('dettagli_scritture_bilancio', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('scrittura_bilancio_id');
|
||||
$table->unsignedBigInteger('conto_id');
|
||||
$table->decimal('importo_dare', 12, 2)->default(0);
|
||||
$table->decimal('importo_avere', 12, 2)->default(0);
|
||||
$table->string('descrizione_dettaglio')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('scrittura_bilancio_id')->references('id')->on('scritture_bilancio')->onDelete('cascade');
|
||||
$table->foreign('conto_id')->references('id')->on('piano_conti')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella ripartizioni bilancio
|
||||
Schema::create('ripartizioni_bilancio', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('scrittura_bilancio_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->unsignedBigInteger('tabella_millesimale_id');
|
||||
$table->decimal('quota_calcolata', 10, 2);
|
||||
$table->decimal('quota_modificata', 10, 2)->nullable();
|
||||
$table->decimal('quota_finale', 10, 2);
|
||||
$table->integer('versione')->default(1);
|
||||
$table->unsignedBigInteger('modificato_da_user_id')->nullable();
|
||||
$table->string('motivo_modifica')->nullable();
|
||||
$table->timestamp('data_modifica')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('scrittura_bilancio_id')->references('id')->on('scritture_bilancio')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('tabella_millesimale_id')->references('id')->on('tabelle_millesimali')->onDelete('cascade');
|
||||
$table->foreign('modificato_da_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
});
|
||||
|
||||
// Tabella conguagli
|
||||
Schema::create('conguagli', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('bilancio_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->unsignedBigInteger('soggetto_id');
|
||||
$table->decimal('totale_rate_pagate', 10, 2)->default(0);
|
||||
$table->decimal('totale_spese_effettive', 10, 2)->default(0);
|
||||
$table->decimal('conguaglio_dovuto', 10, 2)->default(0); // Positivo = a credito, Negativo = a debito
|
||||
$table->enum('tipo_conguaglio', ['a_credito', 'a_debito', 'pareggio']);
|
||||
$table->enum('stato', ['calcolato', 'confermato', 'pagato', 'rimborsato'])->default('calcolato');
|
||||
$table->date('data_calcolo');
|
||||
$table->date('data_pagamento')->nullable();
|
||||
$table->decimal('importo_pagato', 10, 2)->default(0);
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('bilancio_id')->references('id')->on('bilanci')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
$table->index(['bilancio_id', 'tipo_conguaglio']);
|
||||
});
|
||||
|
||||
// Tabella rate conguaglio
|
||||
Schema::create('rate_conguaglio', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('conguaglio_id');
|
||||
$table->string('numero_rata', 50)->unique();
|
||||
$table->string('descrizione');
|
||||
$table->date('data_scadenza');
|
||||
$table->decimal('importo_rata', 10, 2);
|
||||
$table->enum('stato_pagamento', ['da_pagare', 'parziale', 'pagata', 'insoluta'])->default('da_pagare');
|
||||
$table->decimal('importo_pagato', 10, 2)->default(0);
|
||||
$table->date('data_pagamento')->nullable();
|
||||
$table->boolean('rateizzato')->default(false);
|
||||
$table->integer('numero_rate_totali')->default(1);
|
||||
$table->integer('numero_rata_corrente')->default(1);
|
||||
$table->integer('versione')->default(1);
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('conguaglio_id')->references('id')->on('conguagli')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella quadrature
|
||||
Schema::create('quadrature', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('bilancio_id');
|
||||
$table->date('data_quadratura');
|
||||
$table->decimal('saldo_banca_effettivo', 12, 2);
|
||||
$table->decimal('saldo_contabile_calcolato', 12, 2);
|
||||
$table->decimal('differenza', 12, 2);
|
||||
$table->decimal('totale_crediti_condomini', 12, 2);
|
||||
$table->decimal('totale_debiti_condomini', 12, 2);
|
||||
$table->decimal('totale_rate_emesse', 12, 2);
|
||||
$table->decimal('totale_rate_incassate', 12, 2);
|
||||
$table->boolean('quadratura_ok')->default(false);
|
||||
$table->text('note_differenze')->nullable();
|
||||
$table->unsignedBigInteger('verificato_da_user_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('bilancio_id')->references('id')->on('bilanci')->onDelete('cascade');
|
||||
$table->foreign('verificato_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella rimborsi assicurativi
|
||||
Schema::create('rimborsi_assicurativi', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('bilancio_id');
|
||||
$table->string('numero_sinistro');
|
||||
$table->string('compagnia_assicurativa');
|
||||
$table->date('data_sinistro');
|
||||
$table->date('data_denuncia');
|
||||
$table->decimal('importo_richiesto', 10, 2);
|
||||
$table->decimal('importo_liquidato', 10, 2)->default(0);
|
||||
$table->enum('stato', ['denunciato', 'in_valutazione', 'liquidato', 'rifiutato', 'chiuso']);
|
||||
$table->enum('tipo_accredito', ['rate_condomini', 'pagamento_diretto', 'fondo_comune']);
|
||||
$table->date('data_liquidazione')->nullable();
|
||||
$table->text('descrizione_sinistro');
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('bilancio_id')->references('id')->on('bilanci')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella reminder e ticket automatici
|
||||
Schema::create('reminder_bilancio', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('bilancio_id');
|
||||
$table->enum('tipo_reminder', ['scadenza_spesa', 'rinnovo_contratto', 'verifica_quadratura', 'chiusura_esercizio']);
|
||||
$table->string('descrizione');
|
||||
$table->date('data_scadenza');
|
||||
$table->boolean('ricorrente')->default(false);
|
||||
$table->enum('frequenza', ['mensile', 'trimestrale', 'semestrale', 'annuale'])->nullable();
|
||||
$table->integer('giorni_preavviso')->default(30);
|
||||
$table->enum('stato', ['attivo', 'eseguito', 'annullato'])->default('attivo');
|
||||
$table->boolean('notifica_inviata')->default(false);
|
||||
$table->timestamp('data_notifica')->nullable();
|
||||
$table->unsignedBigInteger('ticket_generato_id')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('bilancio_id')->references('id')->on('bilanci')->onDelete('cascade');
|
||||
$table->foreign('ticket_generato_id')->references('id')->on('tickets')->onDelete('set null');
|
||||
});
|
||||
|
||||
// Tabella log modifiche bilancio (audit trail)
|
||||
Schema::create('log_modifiche_bilancio', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('entita'); // 'bilancio', 'scrittura', 'ripartizione', 'conguaglio'
|
||||
$table->unsignedBigInteger('entita_id');
|
||||
$table->integer('versione_precedente');
|
||||
$table->integer('versione_nuova');
|
||||
$table->unsignedBigInteger('utente_id');
|
||||
$table->string('tipo_operazione'); // 'create', 'update', 'delete', 'approve', 'close'
|
||||
$table->text('motivo');
|
||||
$table->json('dati_precedenti')->nullable();
|
||||
$table->json('dati_nuovi');
|
||||
$table->json('diff')->nullable(); // Differenze stile GIT
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('utente_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index(['entita', 'entita_id', 'versione_nuova']);
|
||||
});
|
||||
|
||||
// Tabella automazioni fine anno
|
||||
Schema::create('automazioni_fine_anno', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('bilancio_id');
|
||||
$table->enum('tipo_automazione', ['chiusura_conti', 'riporto_saldi', 'calcolo_conguagli', 'generazione_rate']);
|
||||
$table->string('descrizione');
|
||||
$table->enum('stato', ['programmata', 'in_esecuzione', 'completata', 'errore']);
|
||||
$table->date('data_programmata');
|
||||
$table->timestamp('data_esecuzione')->nullable();
|
||||
$table->json('parametri')->nullable();
|
||||
$table->json('risultato')->nullable();
|
||||
$table->text('log_esecuzione')->nullable();
|
||||
$table->text('errori')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('bilancio_id')->references('id')->on('bilanci')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('automazioni_fine_anno');
|
||||
Schema::dropIfExists('log_modifiche_bilancio');
|
||||
Schema::dropIfExists('reminder_bilancio');
|
||||
Schema::dropIfExists('rimborsi_assicurativi');
|
||||
Schema::dropIfExists('quadrature');
|
||||
Schema::dropIfExists('rate_conguaglio');
|
||||
Schema::dropIfExists('conguagli');
|
||||
Schema::dropIfExists('ripartizioni_bilancio');
|
||||
Schema::dropIfExists('dettagli_scritture_bilancio');
|
||||
Schema::dropIfExists('scritture_bilancio');
|
||||
Schema::dropIfExists('piano_conti');
|
||||
Schema::dropIfExists('bilanci');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Tabella assemblee
|
||||
Schema::create('assemblee', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('stabile_id');
|
||||
$table->enum('tipo', ['ordinaria', 'straordinaria']);
|
||||
$table->datetime('data_prima_convocazione');
|
||||
$table->datetime('data_seconda_convocazione');
|
||||
$table->string('luogo');
|
||||
$table->text('note')->nullable();
|
||||
$table->enum('stato', ['bozza', 'convocata', 'svolta', 'chiusa', 'archiviata'])->default('bozza');
|
||||
$table->date('data_convocazione')->nullable();
|
||||
$table->date('data_svolgimento')->nullable();
|
||||
$table->unsignedBigInteger('creato_da_user_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('stabile_id')->references('id_stabile')->on('stabili')->onDelete('cascade');
|
||||
$table->foreign('creato_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index(['stabile_id', 'data_prima_convocazione']);
|
||||
});
|
||||
|
||||
// Tabella ordine del giorno
|
||||
Schema::create('ordine_giorno', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('assemblea_id');
|
||||
$table->integer('numero_punto');
|
||||
$table->string('titolo');
|
||||
$table->text('descrizione');
|
||||
$table->enum('tipo_voce', ['discussione', 'delibera', 'spesa', 'preventivo', 'altro']);
|
||||
$table->unsignedBigInteger('collegamento_preventivo_id')->nullable();
|
||||
$table->decimal('importo_spesa', 12, 2)->nullable();
|
||||
$table->unsignedBigInteger('tabella_millesimale_id')->nullable();
|
||||
$table->enum('esito_votazione', ['non_votato', 'approvato', 'respinto', 'rinviato'])->default('non_votato');
|
||||
$table->integer('voti_favorevoli')->default(0);
|
||||
$table->integer('voti_contrari')->default(0);
|
||||
$table->integer('astenuti')->default(0);
|
||||
$table->decimal('millesimi_favorevoli', 10, 4)->default(0);
|
||||
$table->decimal('millesimi_contrari', 10, 4)->default(0);
|
||||
$table->decimal('millesimi_astenuti', 10, 4)->default(0);
|
||||
$table->text('note_delibera')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('assemblea_id')->references('id')->on('assemblee')->onDelete('cascade');
|
||||
$table->foreign('collegamento_preventivo_id')->references('id')->on('preventivi')->onDelete('set null');
|
||||
$table->foreign('tabella_millesimale_id')->references('id')->on('tabelle_millesimali')->onDelete('set null');
|
||||
});
|
||||
|
||||
// Tabella convocazioni (tracciamento invii)
|
||||
Schema::create('convocazioni', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('assemblea_id');
|
||||
$table->unsignedBigInteger('soggetto_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->enum('canale_invio', ['email', 'pec', 'whatsapp', 'telegram', 'raccomandata', 'mano', 'portiere', 'postale']);
|
||||
$table->datetime('data_invio');
|
||||
$table->enum('esito_invio', ['inviato', 'consegnato', 'letto', 'errore', 'rifiutato']);
|
||||
$table->datetime('data_lettura')->nullable();
|
||||
$table->string('riferimento_invio')->nullable(); // ID email, numero raccomandata, etc.
|
||||
$table->text('note_invio')->nullable();
|
||||
$table->boolean('delega_presente')->default(false);
|
||||
$table->unsignedBigInteger('delegato_soggetto_id')->nullable();
|
||||
$table->string('documento_delega')->nullable();
|
||||
$table->boolean('presenza_confermata')->default(false);
|
||||
$table->datetime('data_conferma_presenza')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('assemblea_id')->references('id')->on('assemblee')->onDelete('cascade');
|
||||
$table->foreign('soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('delegato_soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('set null');
|
||||
$table->index(['assemblea_id', 'soggetto_id']);
|
||||
});
|
||||
|
||||
// Tabella presenze assemblea
|
||||
Schema::create('presenze_assemblea', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('assemblea_id');
|
||||
$table->unsignedBigInteger('soggetto_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->enum('tipo_presenza', ['presente', 'delegato', 'assente']);
|
||||
$table->datetime('ora_arrivo')->nullable();
|
||||
$table->datetime('ora_uscita')->nullable();
|
||||
$table->string('firma_digitale')->nullable();
|
||||
$table->string('qr_code')->nullable();
|
||||
$table->boolean('firma_fisica')->default(false);
|
||||
$table->decimal('millesimi_rappresentati', 10, 4);
|
||||
$table->unsignedBigInteger('delegante_soggetto_id')->nullable(); // Se è un delegato
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('assemblea_id')->references('id')->on('assemblee')->onDelete('cascade');
|
||||
$table->foreign('soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->foreign('delegante_soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('set null');
|
||||
});
|
||||
|
||||
// Tabella votazioni
|
||||
Schema::create('votazioni', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('ordine_giorno_id');
|
||||
$table->unsignedBigInteger('soggetto_id');
|
||||
$table->unsignedBigInteger('unita_immobiliare_id');
|
||||
$table->enum('voto', ['favorevole', 'contrario', 'astenuto', 'non_votante']);
|
||||
$table->decimal('millesimi_voto', 10, 4);
|
||||
$table->datetime('data_voto');
|
||||
$table->text('motivazione')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('ordine_giorno_id')->references('id')->on('ordine_giorno')->onDelete('cascade');
|
||||
$table->foreign('soggetto_id')->references('id_soggetto')->on('soggetti')->onDelete('cascade');
|
||||
$table->foreign('unita_immobiliare_id')->references('id_unita')->on('unita_immobiliari')->onDelete('cascade');
|
||||
$table->unique(['ordine_giorno_id', 'soggetto_id', 'unita_immobiliare_id'], 'unique_voto');
|
||||
});
|
||||
|
||||
// Tabella delibere (risultati votazioni)
|
||||
Schema::create('delibere', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('ordine_giorno_id');
|
||||
$table->string('numero_delibera');
|
||||
$table->enum('esito', ['approvata', 'respinta', 'rinviata']);
|
||||
$table->text('testo_delibera');
|
||||
$table->integer('totale_voti_favorevoli');
|
||||
$table->integer('totale_voti_contrari');
|
||||
$table->integer('totale_astenuti');
|
||||
$table->decimal('totale_millesimi_favorevoli', 10, 4);
|
||||
$table->decimal('totale_millesimi_contrari', 10, 4);
|
||||
$table->decimal('totale_millesimi_astenuti', 10, 4);
|
||||
$table->decimal('percentuale_approvazione', 5, 2);
|
||||
$table->boolean('maggioranza_raggiunta');
|
||||
$table->date('data_delibera');
|
||||
$table->json('allegati')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('ordine_giorno_id')->references('id')->on('ordine_giorno')->onDelete('cascade');
|
||||
$table->unique('numero_delibera');
|
||||
});
|
||||
|
||||
// Tabella verbali
|
||||
Schema::create('verbali', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('assemblea_id');
|
||||
$table->string('numero_verbale');
|
||||
$table->text('testo_verbale');
|
||||
$table->json('allegati')->nullable();
|
||||
$table->date('data_redazione');
|
||||
$table->unsignedBigInteger('redatto_da_user_id');
|
||||
$table->string('firma_digitale')->nullable();
|
||||
$table->boolean('inviato_condomini')->default(false);
|
||||
$table->datetime('data_invio_condomini')->nullable();
|
||||
$table->enum('stato', ['bozza', 'definitivo', 'inviato', 'archiviato'])->default('bozza');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('assemblea_id')->references('id')->on('assemblee')->onDelete('cascade');
|
||||
$table->foreign('redatto_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella registro protocollo (per tutte le comunicazioni)
|
||||
Schema::create('registro_protocollo', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('numero_protocollo')->unique();
|
||||
$table->enum('tipo_comunicazione', ['convocazione', 'verbale', 'delibera', 'comunicazione', 'delega', 'altro']);
|
||||
$table->unsignedBigInteger('assemblea_id')->nullable();
|
||||
$table->unsignedBigInteger('soggetto_destinatario_id')->nullable();
|
||||
$table->unsignedBigInteger('soggetto_mittente_id')->nullable();
|
||||
$table->string('oggetto');
|
||||
$table->text('contenuto')->nullable();
|
||||
$table->enum('canale', ['email', 'pec', 'whatsapp', 'telegram', 'raccomandata', 'mano', 'portiere', 'postale']);
|
||||
$table->datetime('data_invio');
|
||||
$table->enum('esito', ['inviato', 'consegnato', 'letto', 'errore', 'rifiutato']);
|
||||
$table->datetime('data_consegna')->nullable();
|
||||
$table->datetime('data_lettura')->nullable();
|
||||
$table->string('riferimento_esterno')->nullable();
|
||||
$table->json('allegati')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->unsignedBigInteger('creato_da_user_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('assemblea_id')->references('id')->on('assemblee')->onDelete('set null');
|
||||
$table->foreign('soggetto_destinatario_id')->references('id_soggetto')->on('soggetti')->onDelete('set null');
|
||||
$table->foreign('soggetto_mittente_id')->references('id_soggetto')->on('soggetti')->onDelete('set null');
|
||||
$table->foreign('creato_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index(['data_invio', 'tipo_comunicazione']);
|
||||
});
|
||||
|
||||
// Tabella documenti assemblea
|
||||
Schema::create('documenti_assemblea', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('assemblea_id');
|
||||
$table->string('nome_documento');
|
||||
$table->string('tipo_documento'); // convocazione, verbale, allegato, delega, etc.
|
||||
$table->string('path_file');
|
||||
$table->string('mime_type');
|
||||
$table->unsignedBigInteger('dimensione_file');
|
||||
$table->string('hash_file');
|
||||
$table->text('descrizione')->nullable();
|
||||
$table->unsignedBigInteger('caricato_da_user_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('assemblea_id')->references('id')->on('assemblee')->onDelete('cascade');
|
||||
$table->foreign('caricato_da_user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Tabella automazioni spese approvate
|
||||
Schema::create('automazioni_spese_approvate', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('delibera_id');
|
||||
$table->unsignedBigInteger('preventivo_generato_id')->nullable();
|
||||
$table->unsignedBigInteger('ripartizione_generata_id')->nullable();
|
||||
$table->json('rate_generate')->nullable();
|
||||
$table->enum('stato_automazione', ['in_attesa', 'in_corso', 'completata', 'errore']);
|
||||
$table->text('log_automazione')->nullable();
|
||||
$table->datetime('data_esecuzione')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('delibera_id')->references('id')->on('delibere')->onDelete('cascade');
|
||||
$table->foreign('preventivo_generato_id')->references('id')->on('preventivi')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('automazioni_spese_approvate');
|
||||
Schema::dropIfExists('documenti_assemblea');
|
||||
Schema::dropIfExists('registro_protocollo');
|
||||
Schema::dropIfExists('verbali');
|
||||
Schema::dropIfExists('delibere');
|
||||
Schema::dropIfExists('votazioni');
|
||||
Schema::dropIfExists('presenze_assemblea');
|
||||
Schema::dropIfExists('convocazioni');
|
||||
Schema::dropIfExists('ordine_giorno');
|
||||
Schema::dropIfExists('assemblee');
|
||||
}
|
||||
};
|
||||
154
resources/views/admin/api_tokens/index.blade.php
Normal file
154
resources/views/admin/api_tokens/index.blade.php
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('API Tokens') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Gestione API Tokens</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-2">Crea e gestisci i token di accesso per le API</p>
|
||||
</div>
|
||||
|
||||
<!-- Form Creazione Token -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg mb-8">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Crea Nuovo Token</h4>
|
||||
<form method="POST" action="{{ route('admin.api-tokens.store') }}">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nome Token -->
|
||||
<div>
|
||||
<x-input-label for="token_name" :value="__('Nome Token')" />
|
||||
<x-text-input id="token_name" name="token_name" type="text" class="mt-1 block w-full"
|
||||
:value="old('token_name')" required
|
||||
placeholder="Es. API Mobile App, Integrazione CRM..." />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('token_name')" />
|
||||
</div>
|
||||
|
||||
<!-- Abilità -->
|
||||
<div>
|
||||
<x-input-label for="abilities" :value="__('Abilità')" />
|
||||
<select id="abilities" name="abilities[]" multiple
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="read">Lettura</option>
|
||||
<option value="write">Scrittura</option>
|
||||
<option value="delete">Eliminazione</option>
|
||||
<option value="admin">Amministrazione</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('abilities')" />
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Tieni premuto Ctrl/Cmd per selezionare più opzioni</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<x-primary-button>
|
||||
{{ __('Crea Token') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Lista Token Esistenti -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Token Esistenti</h4>
|
||||
|
||||
@if(isset($tokens) && $tokens->count() > 0)
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Nome
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Abilità
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Ultimo Utilizzo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Creato il
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@foreach($tokens as $token)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $token->name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
@if($token->abilities)
|
||||
@foreach(json_decode($token->abilities, true) as $ability)
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100 mr-1">
|
||||
{{ ucfirst($ability) }}
|
||||
</span>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-gray-500 dark:text-gray-400">Tutte</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $token->last_used_at ? $token->last_used_at->format('d/m/Y H:i') : 'Mai utilizzato' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $token->created_at->format('d/m/Y H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<form method="POST" action="{{ route('admin.api-tokens.destroy', $token->id) }}"
|
||||
class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo token? Questa azione non può essere annullata.')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300">
|
||||
Elimina
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<div class="text-center py-8">
|
||||
<p class="text-gray-500 dark:text-gray-400">Nessun token API creato</p>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500 mt-2">Crea il tuo primo token utilizzando il form sopra</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Informazioni di Sicurezza -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-4 rounded-lg mt-6">
|
||||
<h5 class="text-sm font-medium text-yellow-800 dark:text-yellow-200 mb-2">⚠️ Informazioni di Sicurezza</h5>
|
||||
<ul class="text-sm text-yellow-700 dark:text-yellow-300 space-y-1">
|
||||
<li>• I token API forniscono accesso completo al tuo account</li>
|
||||
<li>• Non condividere mai i tuoi token con terze parti non autorizzate</li>
|
||||
<li>• Elimina immediatamente i token che non utilizzi più</li>
|
||||
<li>• Monitora regolarmente l'utilizzo dei tuoi token</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
0
resources/views/admin/assemblee/index.blade.php
Normal file
0
resources/views/admin/assemblee/index.blade.php
Normal file
235
resources/views/admin/bilanci/index.blade.php
Normal file
235
resources/views/admin/bilanci/index.blade.php
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Gestione Bilanci e Consuntivi') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Statistiche -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Bilanci Aperti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['bilanci_aperti'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Bilanci Approvati</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['bilanci_approvati'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Conguagli da Pagare</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['conguagli_da_pagare'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M8.433 7.418c.155-.103.346-.196.567-.267v1.698a2.305 2.305 0 01-.567-.267C8.07 8.34 8 8.114 8 8c0-.114.07-.34.433-.582zM11 12.849v-1.698c.22.071.412.164.567.267.364.243.433.468.433.582 0 .114-.07.34-.433.582a2.305 2.305 0 01-.567.267z"></path>
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0v.092a4.535 4.535 0 00-1.676.662C6.602 6.234 6 7.009 6 8c0 .99.602 1.765 1.324 2.246.48.32 1.054.545 1.676.662v1.941c-.391-.127-.68-.317-.843-.504a1 1 0 10-1.51 1.31c.562.649 1.413 1.076 2.353 1.253V15a1 1 0 102 0v-.092a4.535 4.535 0 001.676-.662C13.398 13.766 14 12.991 14 12c0-.99-.602-1.765-1.324-2.246A4.535 4.535 0 0011 9.092V7.151c.391.127.68.317.843.504a1 1 0 101.511-1.31c-.563-.649-1.413-1.076-2.354-1.253V5z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Totale Avanzi</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">€ {{ number_format($stats['totale_avanzi'], 2, ',', '.') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azioni Rapide -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Azioni Rapide</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
<a href="{{ route('admin.bilanci.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Bilancio
|
||||
</a>
|
||||
<a href="{{ route('admin.bilanci.quadrature') }}"
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Quadrature
|
||||
</a>
|
||||
<a href="{{ route('admin.bilanci.conguagli') }}"
|
||||
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Conguagli
|
||||
</a>
|
||||
<a href="{{ route('admin.bilanci.rimborsi') }}"
|
||||
class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Rimborsi
|
||||
</a>
|
||||
<a href="{{ route('admin.bilanci.automazioni') }}"
|
||||
class="bg-yellow-500 hover:bg-yellow-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Automazioni
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista Bilanci -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Bilanci e Consuntivi</h3>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Anno/Tipo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stabile
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Periodo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Risultato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Versione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($bilanci as $bilancio)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<div>
|
||||
<div class="font-medium">{{ $bilancio->anno_esercizio }}</div>
|
||||
<div class="text-gray-500 dark:text-gray-400">{{ ucfirst($bilancio->tipo_gestione) }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $bilancio->stabile->denominazione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<div>
|
||||
<div>{{ $bilancio->data_inizio_esercizio->format('d/m/Y') }}</div>
|
||||
<div class="text-gray-500 dark:text-gray-400">{{ $bilancio->data_fine_esercizio->format('d/m/Y') }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<div>
|
||||
<div class="font-medium {{ $bilancio->risultato_gestione >= 0 ? 'text-green-600' : 'text-red-600' }}">
|
||||
{{ $bilancio->risultato_gestione >= 0 ? 'Avanzo' : 'Disavanzo' }}
|
||||
</div>
|
||||
<div class="text-sm">€ {{ number_format(abs($bilancio->risultato_gestione), 2, ',', '.') }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($bilancio->stato)
|
||||
@case('bozza') bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100 @break
|
||||
@case('provvisorio') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@case('definitivo') bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100 @break
|
||||
@case('approvato') bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100 @break
|
||||
@case('chiuso') bg-purple-100 text-purple-800 dark:bg-purple-800 dark:text-purple-100 @break
|
||||
@endswitch">
|
||||
{{ ucfirst($bilancio->stato) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
v{{ $bilancio->versione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.bilanci.show', $bilancio) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
@if(in_array($bilancio->stato, ['bozza', 'provvisorio']))
|
||||
<a href="{{ route('admin.bilanci.edit', $bilancio) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
|
||||
Modifica
|
||||
</a>
|
||||
@endif
|
||||
<a href="{{ route('admin.bilanci.storico', $bilancio) }}"
|
||||
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300">
|
||||
Storico
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun bilancio trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $bilanci->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
161
resources/views/admin/contabilita/index.blade.php
Normal file
161
resources/views/admin/contabilita/index.blade.php
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dashboard Contabilità') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<!-- Statistiche -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Movimenti Mese</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['movimenti_mese'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12 2l3.09 6.26L22 9l-5 4.87L18.18 22 12 18.27 5.82 22 7 13.87 2 9l6.91-.74L12 2z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Entrate Mese</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">€ {{ number_format($stats['importo_entrate_mese'], 2, ',', '.') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"></path>
|
||||
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Uscite Mese</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">€ {{ number_format($stats['importo_uscite_mese'], 2, ',', '.') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azioni Rapide -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg mb-8">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Azioni Rapide</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<a href="{{ route('admin.contabilita.registrazione') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuova Registrazione
|
||||
</a>
|
||||
<a href="{{ route('admin.contabilita.import-xml') }}"
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Import XML
|
||||
</a>
|
||||
<a href="{{ route('admin.contabilita.movimenti') }}"
|
||||
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Tutti i Movimenti
|
||||
</a>
|
||||
<a href="{{ route('admin.contabilita.riconciliazione') }}"
|
||||
class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Riconciliazione
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ultimi Movimenti -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Ultimi Movimenti</h3>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Data
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Protocollo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Descrizione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Tipo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Importo
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($ultimiMovimenti as $movimento)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $movimento->data_registrazione->format('d/m/Y') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $movimento->protocollo }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $movimento->descrizione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
{{ $movimento->tipo_movimento === 'entrata' ? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' : 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100' }}">
|
||||
{{ ucfirst($movimento->tipo_movimento) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
€ {{ number_format($movimento->importo_totale, 2, ',', '.') }}
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun movimento registrato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
210
resources/views/admin/contabilita/registrazione.blade.php
Normal file
210
resources/views/admin/contabilita/registrazione.blade.php
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Registrazione Movimento Contabile') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Nuova Registrazione Contabile</h3>
|
||||
<a href="{{ route('admin.contabilita.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.contabilita.store-registrazione') }}" id="registrazione-form">
|
||||
@csrf
|
||||
|
||||
<!-- Sezione Dati Generali -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Dati Generali Documento</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Stabile -->
|
||||
<div>
|
||||
<x-input-label for="stabile_id" :value="__('Stabile')" />
|
||||
<select id="stabile_id" name="stabile_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="">Seleziona stabile</option>
|
||||
@foreach($stabili as $stabile)
|
||||
<option value="{{ $stabile->id_stabile }}" {{ old('stabile_id') == $stabile->id_stabile ? 'selected' : '' }}>
|
||||
{{ $stabile->denominazione }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('stabile_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Gestione -->
|
||||
<div>
|
||||
<x-input-label for="gestione_id" :value="__('Gestione')" />
|
||||
<select id="gestione_id" name="gestione_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="">Seleziona gestione</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('gestione_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Tipo Movimento -->
|
||||
<div>
|
||||
<x-input-label for="tipo_movimento" :value="__('Tipo Movimento')" />
|
||||
<select id="tipo_movimento" name="tipo_movimento" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="">Seleziona tipo</option>
|
||||
<option value="entrata" {{ old('tipo_movimento') == 'entrata' ? 'selected' : '' }}>Entrata</option>
|
||||
<option value="uscita" {{ old('tipo_movimento') == 'uscita' ? 'selected' : '' }}>Uscita</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('tipo_movimento')" />
|
||||
</div>
|
||||
|
||||
<!-- Data Documento -->
|
||||
<div>
|
||||
<x-input-label for="data_documento" :value="__('Data Documento')" />
|
||||
<x-text-input id="data_documento" name="data_documento" type="date" class="mt-1 block w-full"
|
||||
:value="old('data_documento', date('Y-m-d'))" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('data_documento')" />
|
||||
</div>
|
||||
|
||||
<!-- Numero Documento -->
|
||||
<div>
|
||||
<x-input-label for="numero_documento" :value="__('Numero Documento')" />
|
||||
<x-text-input id="numero_documento" name="numero_documento" type="text" class="mt-1 block w-full"
|
||||
:value="old('numero_documento')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('numero_documento')" />
|
||||
</div>
|
||||
|
||||
<!-- Fornitore -->
|
||||
<div>
|
||||
<x-input-label for="fornitore_id" :value="__('Fornitore')" />
|
||||
<select id="fornitore_id" name="fornitore_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona fornitore</option>
|
||||
@foreach($fornitori as $fornitore)
|
||||
<option value="{{ $fornitore->id_fornitore }}" {{ old('fornitore_id') == $fornitore->id_fornitore ? 'selected' : '' }}>
|
||||
{{ $fornitore->ragione_sociale }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('fornitore_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Descrizione -->
|
||||
<div class="md:col-span-2">
|
||||
<x-input-label for="descrizione" :value="__('Descrizione')" />
|
||||
<x-text-input id="descrizione" name="descrizione" type="text" class="mt-1 block w-full"
|
||||
:value="old('descrizione')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('descrizione')" />
|
||||
</div>
|
||||
|
||||
<!-- Importo Totale -->
|
||||
<div>
|
||||
<x-input-label for="importo_totale" :value="__('Importo Totale')" />
|
||||
<x-text-input id="importo_totale" name="importo_totale" type="number" step="0.01" class="mt-1 block w-full"
|
||||
:value="old('importo_totale')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('importo_totale')" />
|
||||
</div>
|
||||
|
||||
<!-- Ritenuta d'Acconto -->
|
||||
<div>
|
||||
<x-input-label for="ritenuta_acconto" :value="__('Ritenuta d\'Acconto')" />
|
||||
<x-text-input id="ritenuta_acconto" name="ritenuta_acconto" type="number" step="0.01" class="mt-1 block w-full"
|
||||
:value="old('ritenuta_acconto', '0')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('ritenuta_acconto')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Dettaglio Spese -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg mb-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100">Dettaglio Spese</h4>
|
||||
<button type="button" id="aggiungi-dettaglio" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Aggiungi Voce
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="dettagli-container">
|
||||
<!-- I dettagli verranno aggiunti dinamicamente qui -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.contabilita.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Registra Movimento') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let dettaglioIndex = 0;
|
||||
|
||||
document.getElementById('aggiungi-dettaglio').addEventListener('click', function() {
|
||||
aggiungiDettaglio();
|
||||
});
|
||||
|
||||
function aggiungiDettaglio() {
|
||||
const container = document.getElementById('dettagli-container');
|
||||
const dettaglioHtml = `
|
||||
<div class="dettaglio-item border border-gray-300 dark:border-gray-600 p-4 rounded-lg mb-4" data-index="${dettaglioIndex}">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Voce Spesa</label>
|
||||
<select name="dettagli[${dettaglioIndex}][voce_spesa_id]" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="">Seleziona voce</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Importo</label>
|
||||
<input type="number" step="0.01" name="dettagli[${dettaglioIndex}][importo]" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Tabella Millesimale</label>
|
||||
<select name="dettagli[${dettaglioIndex}][tabella_millesimale_id]" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona tabella</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button type="button" onclick="rimuoviDettaglio(${dettaglioIndex})" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
|
||||
Rimuovi
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.insertAdjacentHTML('beforeend', dettaglioHtml);
|
||||
dettaglioIndex++;
|
||||
}
|
||||
|
||||
function rimuoviDettaglio(index) {
|
||||
const item = document.querySelector(`[data-index="${index}"]`);
|
||||
if (item) {
|
||||
item.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Aggiungi il primo dettaglio automaticamente
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
aggiungiDettaglio();
|
||||
});
|
||||
</script>
|
||||
</x-app-layout>
|
||||
269
resources/views/admin/dashboard.blade.php
Normal file
269
resources/views/admin/dashboard.blade.php
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dashboard Amministratore') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Statistiche Principali -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Stabili Gestiti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['stabili_gestiti'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Stabili Attivi</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['stabili_attivi'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Ticket Aperti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['ticket_aperti'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Ticket Urgenti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['ticket_urgenti'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azioni Rapide -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Azioni Rapide</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
||||
<a href="{{ route('admin.stabili.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Stabile
|
||||
</a>
|
||||
<a href="{{ route('admin.tickets.create') }}"
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Ticket
|
||||
</a>
|
||||
<a href="{{ route('admin.fornitori.create') }}"
|
||||
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Fornitore
|
||||
</a>
|
||||
<a href="{{ route('admin.soggetti.create') }}"
|
||||
class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Soggetto
|
||||
</a>
|
||||
<a href="{{ route('admin.contabilita.registrazione') }}"
|
||||
class="bg-yellow-500 hover:bg-yellow-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Registrazione
|
||||
</a>
|
||||
<a href="{{ route('admin.documenti.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Documenti
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenuto principale in due colonne -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
<!-- Ticket da Lavorare -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Ticket da Lavorare</h3>
|
||||
<a href="{{ route('admin.tickets.index') }}" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Vedi tutti
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@forelse($ticketsAperti as $ticket)
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
<a href="{{ route('admin.tickets.show', $ticket) }}" class="hover:text-blue-600">
|
||||
{{ $ticket->titolo }}
|
||||
</a>
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ $ticket->stabile->denominazione }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-end space-y-1">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium
|
||||
@switch($ticket->priorita)
|
||||
@case('Urgente') bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100 @break
|
||||
@case('Alta') bg-orange-100 text-orange-800 dark:bg-orange-800 dark:text-orange-100 @break
|
||||
@case('Media') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@default bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100
|
||||
@endswitch">
|
||||
{{ $ticket->priorita }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $ticket->created_at->diffForHumans() }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-gray-500 dark:text-gray-400 text-center py-4">Nessun ticket aperto</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ultimi Documenti -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Ultimi Documenti</h3>
|
||||
<a href="{{ route('admin.documenti.index') }}" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Vedi tutti
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@forelse($ultimiDocumenti as $documento)
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $documento->nome_file }}
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ $documento->tipo_documento }} - {{ $documento->documentable->denominazione ?? 'N/A' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-end space-y-1">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $documento->created_at->diffForHumans() }}
|
||||
</span>
|
||||
<a href="{{ $documento->url_download }}" class="text-blue-600 hover:text-blue-900 text-xs">
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-gray-500 dark:text-gray-400 text-center py-4">Nessun documento caricato</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ultimi Movimenti Contabili -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Ultimi Movimenti Contabili</h3>
|
||||
<a href="{{ route('admin.contabilita.movimenti') }}" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Vedi tutti
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Data</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Descrizione</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Fornitore</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Importo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($ultimiMovimenti as $movimento)
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $movimento->data_registrazione->format('d/m/Y') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $movimento->descrizione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $movimento->fornitore->ragione_sociale ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="font-medium {{ $movimento->tipo_movimento === 'entrata' ? 'text-green-600' : 'text-red-600' }}">
|
||||
{{ $movimento->tipo_movimento === 'entrata' ? '+' : '-' }}€ {{ number_format($movimento->importo_totale, 2, ',', '.') }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun movimento registrato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
117
resources/views/admin/documenti/create.blade.php
Normal file
117
resources/views/admin/documenti/create.blade.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Carica Nuovo Documento') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Carica Nuovo Documento</h3>
|
||||
<a href="{{ route('admin.documenti.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna all'Archivio
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.documenti.store') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Sezione File -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">File da Caricare</h4>
|
||||
<div>
|
||||
<x-input-label for="file" :value="__('Seleziona File')" />
|
||||
<input type="file" id="file" name="file" required
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('file')" />
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Formati supportati: PDF, DOC, DOCX, XLS, XLSX, JPG, PNG, XML. Dimensione massima: 10MB
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Classificazione -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Classificazione Documento</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Collega a Stabile -->
|
||||
<div>
|
||||
<x-input-label for="documentable_id" :value="__('Collega a Stabile')" />
|
||||
<select id="documentable_id" name="documentable_id" required
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona uno stabile</option>
|
||||
@foreach($stabili as $stabile)
|
||||
<option value="{{ $stabile->id_stabile }}" {{ old('documentable_id') == $stabile->id_stabile ? 'selected' : '' }}>
|
||||
{{ $stabile->denominazione }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<input type="hidden" name="documentable_type" value="App\Models\Stabile">
|
||||
<x-input-error class="mt-2" :messages="$errors->get('documentable_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Tipo Documento -->
|
||||
<div>
|
||||
<x-input-label for="tipo_documento" :value="__('Tipo Documento')" />
|
||||
<select id="tipo_documento" name="tipo_documento" required
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona tipo</option>
|
||||
<option value="Fattura" {{ old('tipo_documento') === 'Fattura' ? 'selected' : '' }}>Fattura</option>
|
||||
<option value="Verbale Assemblea" {{ old('tipo_documento') === 'Verbale Assemblea' ? 'selected' : '' }}>Verbale Assemblea</option>
|
||||
<option value="Bilancio" {{ old('tipo_documento') === 'Bilancio' ? 'selected' : '' }}>Bilancio</option>
|
||||
<option value="Contratto" {{ old('tipo_documento') === 'Contratto' ? 'selected' : '' }}>Contratto</option>
|
||||
<option value="Comunicazione" {{ old('tipo_documento') === 'Comunicazione' ? 'selected' : '' }}>Comunicazione</option>
|
||||
<option value="Certificato" {{ old('tipo_documento') === 'Certificato' ? 'selected' : '' }}>Certificato</option>
|
||||
<option value="Planimetria" {{ old('tipo_documento') === 'Planimetria' ? 'selected' : '' }}>Planimetria</option>
|
||||
<option value="Foto" {{ old('tipo_documento') === 'Foto' ? 'selected' : '' }}>Foto</option>
|
||||
<option value="Altro" {{ old('tipo_documento') === 'Altro' ? 'selected' : '' }}>Altro</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('tipo_documento')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Descrizione -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Descrizione</h4>
|
||||
<div>
|
||||
<x-input-label for="descrizione" :value="__('Descrizione (opzionale)')" />
|
||||
<textarea id="descrizione" name="descrizione" rows="3"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm"
|
||||
placeholder="Aggiungi una descrizione per identificare meglio il documento...">{{ old('descrizione') }}</textarea>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('descrizione')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.documenti.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Carica Documento') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
146
resources/views/admin/documenti/index.blade.php
Normal file
146
resources/views/admin/documenti/index.blade.php
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Gestione Documenti') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Archivio Documenti</h3>
|
||||
<a href="{{ route('admin.documenti.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Carica Documento
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Filtri -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg mb-6">
|
||||
<form method="GET" action="{{ route('admin.documenti.index') }}">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<!-- Ricerca -->
|
||||
<div>
|
||||
<x-input-label for="search" :value="__('Cerca')" />
|
||||
<x-text-input id="search" name="search" type="text" class="mt-1 block w-full"
|
||||
:value="request('search')" placeholder="Nome file o descrizione..." />
|
||||
</div>
|
||||
|
||||
<!-- Tipo Documento -->
|
||||
<div>
|
||||
<x-input-label for="tipo_documento" :value="__('Tipo Documento')" />
|
||||
<select id="tipo_documento" name="tipo_documento" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Tutti i tipi</option>
|
||||
@foreach($tipiDocumento as $tipo)
|
||||
<option value="{{ $tipo }}" {{ request('tipo_documento') === $tipo ? 'selected' : '' }}>
|
||||
{{ $tipo }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-end space-x-2">
|
||||
<x-primary-button type="submit">
|
||||
{{ __('Filtra') }}
|
||||
</x-primary-button>
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.documenti.index') }}'">
|
||||
{{ __('Reset') }}
|
||||
</x-secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Documenti -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Nome File
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Tipo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Collegato a
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Dimensione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Data Caricamento
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($documenti as $documento)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $documento->nome_file }}
|
||||
@if($documento->descrizione)
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">{{ $documento->descrizione }}</p>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100">
|
||||
{{ $documento->tipo_documento }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $documento->documentable->denominazione ?? 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $documento->dimensione_leggibile }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $documento->created_at->format('d/m/Y H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.documenti.download', $documento) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Download
|
||||
</a>
|
||||
<a href="{{ route('admin.documenti.show', $documento) }}"
|
||||
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300">
|
||||
Visualizza
|
||||
</a>
|
||||
<form method="POST" action="{{ route('admin.documenti.destroy', $documento) }}"
|
||||
class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo documento?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300">
|
||||
Elimina
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun documento trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $documenti->appends(request()->query())->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
99
resources/views/admin/fornitori/_form.blade.php
Normal file
99
resources/views/admin/fornitori/_form.blade.php
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<div class="space-y-6">
|
||||
<!-- Sezione Informazioni Generali -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Informazioni Generali</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Ragione Sociale -->
|
||||
<div class="md:col-span-2">
|
||||
<x-input-label for="ragione_sociale" :value="__('Ragione Sociale')" />
|
||||
<x-text-input id="ragione_sociale" name="ragione_sociale" type="text" class="mt-1 block w-full"
|
||||
:value="old('ragione_sociale', $fornitore->ragione_sociale ?? '')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('ragione_sociale')" />
|
||||
</div>
|
||||
|
||||
<!-- Partita IVA -->
|
||||
<div>
|
||||
<x-input-label for="partita_iva" :value="__('Partita IVA')" />
|
||||
<x-text-input id="partita_iva" name="partita_iva" type="text" class="mt-1 block w-full"
|
||||
:value="old('partita_iva', $fornitore->partita_iva ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('partita_iva')" />
|
||||
</div>
|
||||
|
||||
<!-- Codice Fiscale -->
|
||||
<div>
|
||||
<x-input-label for="codice_fiscale" :value="__('Codice Fiscale')" />
|
||||
<x-text-input id="codice_fiscale" name="codice_fiscale" type="text" class="mt-1 block w-full"
|
||||
:value="old('codice_fiscale', $fornitore->codice_fiscale ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('codice_fiscale')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Contatti -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Contatti</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full"
|
||||
:value="old('email', $fornitore->email ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('email')" />
|
||||
</div>
|
||||
|
||||
<!-- PEC -->
|
||||
<div>
|
||||
<x-input-label for="pec" :value="__('PEC')" />
|
||||
<x-text-input id="pec" name="pec" type="email" class="mt-1 block w-full"
|
||||
:value="old('pec', $fornitore->pec ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('pec')" />
|
||||
</div>
|
||||
|
||||
<!-- Telefono -->
|
||||
<div>
|
||||
<x-input-label for="telefono" :value="__('Telefono')" />
|
||||
<x-text-input id="telefono" name="telefono" type="text" class="mt-1 block w-full"
|
||||
:value="old('telefono', $fornitore->telefono ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('telefono')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Indirizzo -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Indirizzo</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Indirizzo -->
|
||||
<div class="md:col-span-2">
|
||||
<x-input-label for="indirizzo" :value="__('Indirizzo')" />
|
||||
<x-text-input id="indirizzo" name="indirizzo" type="text" class="mt-1 block w-full"
|
||||
:value="old('indirizzo', $fornitore->indirizzo ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('indirizzo')" />
|
||||
</div>
|
||||
|
||||
<!-- Città -->
|
||||
<div>
|
||||
<x-input-label for="citta" :value="__('Città')" />
|
||||
<x-text-input id="citta" name="citta" type="text" class="mt-1 block w-full"
|
||||
:value="old('citta', $fornitore->citta ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('citta')" />
|
||||
</div>
|
||||
|
||||
<!-- CAP -->
|
||||
<div>
|
||||
<x-input-label for="cap" :value="__('CAP')" />
|
||||
<x-text-input id="cap" name="cap" type="text" class="mt-1 block w-full"
|
||||
:value="old('cap', $fornitore->cap ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('cap')" />
|
||||
</div>
|
||||
|
||||
<!-- Provincia -->
|
||||
<div>
|
||||
<x-input-label for="provincia" :value="__('Provincia')" />
|
||||
<x-text-input id="provincia" name="provincia" type="text" class="mt-1 block w-full" maxlength="2"
|
||||
:value="old('provincia', $fornitore->provincia ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('provincia')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
49
resources/views/admin/fornitori/create.blade.php
Normal file
49
resources/views/admin/fornitori/create.blade.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Nuovo Fornitore') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Crea Nuovo Fornitore</h3>
|
||||
<a href="{{ route('admin.fornitori.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.fornitori.store') }}">
|
||||
@csrf
|
||||
@include('admin.fornitori._form', ['fornitore' => null])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.fornitori.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Crea Fornitore') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
50
resources/views/admin/fornitori/edit.blade.php
Normal file
50
resources/views/admin/fornitori/edit.blade.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Modifica Fornitore') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Modifica: {{ $fornitore->ragione_sociale }}</h3>
|
||||
<a href="{{ route('admin.fornitori.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.fornitori.update', $fornitore) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('admin.fornitori._form', ['fornitore' => $fornitore])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.fornitori.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Aggiorna Fornitore') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
110
resources/views/admin/fornitori/index.blade.php
Normal file
110
resources/views/admin/fornitori/index.blade.php
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Gestione Fornitori') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Lista Fornitori</h3>
|
||||
<a href="{{ route('admin.fornitori.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Fornitore
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Fornitori -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
ID
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Ragione Sociale
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Partita IVA
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Email
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Telefono
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Città
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($fornitori as $fornitore)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $fornitore->id_fornitore }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $fornitore->ragione_sociale }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $fornitore->partita_iva ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $fornitore->email ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $fornitore->telefono ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $fornitore->citta ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.fornitori.show', $fornitore) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
<a href="{{ route('admin.fornitori.edit', $fornitore) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
|
||||
Modifica
|
||||
</a>
|
||||
<form method="POST" action="{{ route('admin.fornitori.destroy', $fornitore) }}"
|
||||
class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo fornitore?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300">
|
||||
Elimina
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun fornitore trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $fornitori->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
118
resources/views/admin/fornitori/show.blade.php
Normal file
118
resources/views/admin/fornitori/show.blade.php
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dettagli Fornitore') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ $fornitore->ragione_sociale }}</h3>
|
||||
<div class="space-x-2">
|
||||
<a href="{{ route('admin.fornitori.edit', $fornitore) }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Modifica
|
||||
</a>
|
||||
<a href="{{ route('admin.fornitori.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni Principali -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Informazioni Generali</h4>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">ID Fornitore:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->id_fornitore }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Ragione Sociale:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->ragione_sociale }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Partita IVA:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->partita_iva ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Codice Fiscale:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->codice_fiscale ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Contatti</h4>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Email:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->email ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">PEC:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->pec ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Telefono:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->telefono ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indirizzo -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Indirizzo</h4>
|
||||
<dl class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Indirizzo:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->indirizzo ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Città:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->citta ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">CAP:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->cap ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Provincia:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->provincia ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni Sistema -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Informazioni Sistema</h4>
|
||||
<dl class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Creato il:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->created_at->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Ultimo aggiornamento:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->updated_at->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
@if($fornitore->old_id)
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Old ID:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $fornitore->old_id }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
144
resources/views/admin/impostazioni/index.blade.php
Normal file
144
resources/views/admin/impostazioni/index.blade.php
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Impostazioni') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Configurazione Sistema</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-2">Gestisci le impostazioni generali dell'applicazione</p>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.impostazioni.store') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<!-- Sezione Applicazione -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Impostazioni Applicazione</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nome Applicazione -->
|
||||
<div>
|
||||
<x-input-label for="app_name" :value="__('Nome Applicazione')" />
|
||||
<x-text-input id="app_name" name="app_name" type="text" class="mt-1 block w-full"
|
||||
:value="old('app_name', config('app.name'))" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('app_name')" />
|
||||
</div>
|
||||
|
||||
<!-- URL Applicazione -->
|
||||
<div>
|
||||
<x-input-label for="app_url" :value="__('URL Applicazione')" />
|
||||
<x-text-input id="app_url" name="app_url" type="url" class="mt-1 block w-full"
|
||||
:value="old('app_url', config('app.url'))" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('app_url')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Branding -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Branding</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Logo Applicazione -->
|
||||
<div>
|
||||
<x-input-label for="app_logo" :value="__('Logo Applicazione')" />
|
||||
<input type="file" id="app_logo" name="app_logo" accept="image/*"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('app_logo')" />
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Formati supportati: JPG, PNG, SVG (max 2MB)</p>
|
||||
</div>
|
||||
|
||||
<!-- Logo Dashboard -->
|
||||
<div>
|
||||
<x-input-label for="dashboard_logo" :value="__('Logo Dashboard')" />
|
||||
<input type="file" id="dashboard_logo" name="dashboard_logo" accept="image/*"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('dashboard_logo')" />
|
||||
</div>
|
||||
|
||||
<!-- Favicon -->
|
||||
<div>
|
||||
<x-input-label for="favicon" :value="__('Favicon')" />
|
||||
<input type="file" id="favicon" name="favicon" accept="image/*"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('favicon')" />
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Formato ICO o PNG 32x32px</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Email -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Configurazione Email</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Email Mittente -->
|
||||
<div>
|
||||
<x-input-label for="mail_from_address" :value="__('Email Mittente')" />
|
||||
<x-text-input id="mail_from_address" name="mail_from_address" type="email" class="mt-1 block w-full"
|
||||
:value="old('mail_from_address', config('mail.from.address'))" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('mail_from_address')" />
|
||||
</div>
|
||||
|
||||
<!-- Nome Mittente -->
|
||||
<div>
|
||||
<x-input-label for="mail_from_name" :value="__('Nome Mittente')" />
|
||||
<x-text-input id="mail_from_name" name="mail_from_name" type="text" class="mt-1 block w-full"
|
||||
:value="old('mail_from_name', config('mail.from.name'))" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('mail_from_name')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Pagamenti -->
|
||||
<div class="bg-purple-50 dark:bg-purple-900/20 p-6 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Configurazione Pagamenti</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Stripe Key -->
|
||||
<div>
|
||||
<x-input-label for="stripe_key" :value="__('Stripe Publishable Key')" />
|
||||
<x-text-input id="stripe_key" name="stripe_key" type="text" class="mt-1 block w-full"
|
||||
:value="old('stripe_key', config('services.stripe.key'))" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('stripe_key')" />
|
||||
</div>
|
||||
|
||||
<!-- PayPal Client ID -->
|
||||
<div>
|
||||
<x-input-label for="paypal_client_id" :value="__('PayPal Client ID')" />
|
||||
<x-text-input id="paypal_client_id" name="paypal_client_id" type="text" class="mt-1 block w-full"
|
||||
:value="old('paypal_client_id', config('services.paypal.client_id'))" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('paypal_client_id')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<x-secondary-button type="button" onclick="window.location.reload()">
|
||||
{{ __('Ripristina') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Salva Impostazioni') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
203
resources/views/admin/preventivi/index.blade.php
Normal file
203
resources/views/admin/preventivi/index.blade.php
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Gestione Preventivi') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Statistiche -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Preventivi Bozza</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['preventivi_bozza'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Preventivi Approvati</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['preventivi_approvati'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M8.433 7.418c.155-.103.346-.196.567-.267v1.698a2.305 2.305 0 01-.567-.267C8.07 8.34 8 8.114 8 8c0-.114.07-.34.433-.582zM11 12.849v-1.698c.22.071.412.164.567.267.364.243.433.468.433.582 0 .114-.07.34-.433.582a2.305 2.305 0 01-.567.267z"></path>
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0v.092a4.535 4.535 0 00-1.676.662C6.602 6.234 6 7.009 6 8c0 .99.602 1.765 1.324 2.246.48.32 1.054.545 1.676.662v1.941c-.391-.127-.68-.317-.843-.504a1 1 0 10-1.51 1.31c.562.649 1.413 1.076 2.353 1.253V15a1 1 0 102 0v-.092a4.535 4.535 0 001.676-.662C13.398 13.766 14 12.991 14 12c0-.99-.602-1.765-1.324-2.246A4.535 4.535 0 0011 9.092V7.151c.391.127.68.317.843.504a1 1 0 101.511-1.31c-.563-.649-1.413-1.076-2.354-1.253V5z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Importo Anno Corrente</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">€ {{ number_format($stats['importo_totale_anno'], 2, ',', '.') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azioni Rapide -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Azioni Rapide</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<a href="{{ route('admin.preventivi.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Preventivo
|
||||
</a>
|
||||
<a href="{{ route('admin.preventivi.pianificazione') }}"
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Pianificazione
|
||||
</a>
|
||||
<a href="{{ route('admin.preventivi.template') }}"
|
||||
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Template
|
||||
</a>
|
||||
<a href="{{ route('admin.preventivi.report') }}"
|
||||
class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Report
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista Preventivi -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Preventivi</h3>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Anno/Tipo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stabile
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Descrizione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Importo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Versione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($preventivi as $preventivo)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<div>
|
||||
<div class="font-medium">{{ $preventivo->anno_gestione }}</div>
|
||||
<div class="text-gray-500 dark:text-gray-400">{{ ucfirst($preventivo->tipo_gestione) }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $preventivo->stabile->denominazione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $preventivo->descrizione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="font-medium">€ {{ number_format($preventivo->importo_totale, 2, ',', '.') }}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($preventivo->stato)
|
||||
@case('bozza') bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100 @break
|
||||
@case('provvisorio') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@case('definitivo') bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100 @break
|
||||
@case('approvato') bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100 @break
|
||||
@case('archiviato') bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100 @break
|
||||
@endswitch">
|
||||
{{ ucfirst($preventivo->stato) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
v{{ $preventivo->versione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.preventivi.show', $preventivo) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
@if($preventivo->stato === 'bozza')
|
||||
<a href="{{ route('admin.preventivi.edit', $preventivo) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
|
||||
Modifica
|
||||
</a>
|
||||
@endif
|
||||
<a href="{{ route('admin.preventivi.storico', $preventivo) }}"
|
||||
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300">
|
||||
Storico
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun preventivo trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $preventivi->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
184
resources/views/admin/preventivi/pianificazione.blade.php
Normal file
184
resources/views/admin/preventivi/pianificazione.blade.php
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Pianificazione Spese e Entrate') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Dashboard Cashflow -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Previsione Cashflow (Prossimi 6 Mesi)</h3>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Mese
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Entrate Previste
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Uscite Previste
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Saldo Previsto
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@foreach($cashflow as $mese)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $mese['mese'] }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-green-600 dark:text-green-400">
|
||||
+€ {{ number_format($mese['entrate'], 2, ',', '.') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-red-600 dark:text-red-400">
|
||||
-€ {{ number_format($mese['uscite'], 2, ',', '.') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium {{ $mese['saldo'] >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400' }}">
|
||||
{{ $mese['saldo'] >= 0 ? '+' : '' }}€ {{ number_format($mese['saldo'], 2, ',', '.') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Spese in Scadenza -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Spese in Scadenza (Prossimi 30 giorni)</h3>
|
||||
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuova Spesa Pianificata
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
@forelse($speseInScadenza as $spesa)
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $spesa->descrizione }}
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ $spesa->stabile->denominazione }}
|
||||
</p>
|
||||
<div class="mt-2 flex items-center space-x-4">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
€ {{ number_format($spesa->importo_previsto, 2, ',', '.') }}
|
||||
</span>
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium
|
||||
@switch($spesa->tipo)
|
||||
@case('ricorrente') bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100 @break
|
||||
@case('straordinaria') bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100 @break
|
||||
@case('manutenzione') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@endswitch">
|
||||
{{ ucfirst($spesa->tipo) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-end space-y-2">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Scadenza: {{ $spesa->data_scadenza_prevista->format('d/m/Y') }}
|
||||
</span>
|
||||
<div class="flex space-x-2">
|
||||
<button class="text-green-600 hover:text-green-900 text-sm">Conferma</button>
|
||||
<button class="text-blue-600 hover:text-blue-900 text-sm">Modifica</button>
|
||||
<button class="text-red-600 hover:text-red-900 text-sm">Annulla</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-center py-8">
|
||||
<p class="text-gray-500 dark:text-gray-400">Nessuna spesa in scadenza nei prossimi 30 giorni</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grafici Cashflow -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Andamento Cashflow</h3>
|
||||
<div class="h-64">
|
||||
<canvas id="cashflowChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Script per grafici -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
const ctx = document.getElementById('cashflowChart').getContext('2d');
|
||||
const cashflowData = @json($cashflow);
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: cashflowData.map(item => item.mese),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Entrate',
|
||||
data: cashflowData.map(item => item.entrate),
|
||||
borderColor: 'rgb(34, 197, 94)',
|
||||
backgroundColor: 'rgba(34, 197, 94, 0.1)',
|
||||
tension: 0.1
|
||||
},
|
||||
{
|
||||
label: 'Uscite',
|
||||
data: cashflowData.map(item => item.uscite),
|
||||
borderColor: 'rgb(239, 68, 68)',
|
||||
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
||||
tension: 0.1
|
||||
},
|
||||
{
|
||||
label: 'Saldo',
|
||||
data: cashflowData.map(item => item.saldo),
|
||||
borderColor: 'rgb(59, 130, 246)',
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||
tension: 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '€ ' + value.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return context.dataset.label + ': € ' + context.parsed.y.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</x-app-layout>
|
||||
171
resources/views/admin/rubrica/index.blade.php
Normal file
171
resources/views/admin/rubrica/index.blade.php
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Rubrica') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Rubrica Contatti</h3>
|
||||
<a href="{{ route('admin.soggetti.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Contatto
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Filtri di Ricerca -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg mb-6">
|
||||
<form method="GET" action="{{ route('admin.rubrica.index') }}">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<!-- Ricerca per Nome -->
|
||||
<div>
|
||||
<x-input-label for="search" :value="__('Cerca per Nome/Cognome')" />
|
||||
<x-text-input id="search" name="search" type="text" class="mt-1 block w-full"
|
||||
:value="request('search')" placeholder="Nome o cognome..." />
|
||||
</div>
|
||||
|
||||
<!-- Filtro per Tipo -->
|
||||
<div>
|
||||
<x-input-label for="tipo_soggetto" :value="__('Tipo Soggetto')" />
|
||||
<select id="tipo_soggetto" name="tipo_soggetto" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Tutti i tipi</option>
|
||||
<option value="Persona Fisica" {{ request('tipo_soggetto') === 'Persona Fisica' ? 'selected' : '' }}>Persona Fisica</option>
|
||||
<option value="Persona Giuridica" {{ request('tipo_soggetto') === 'Persona Giuridica' ? 'selected' : '' }}>Persona Giuridica</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Filtro per Email -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full"
|
||||
:value="request('email')" placeholder="email@esempio.com" />
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-end space-x-2">
|
||||
<x-primary-button type="submit">
|
||||
{{ __('Filtra') }}
|
||||
</x-primary-button>
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.rubrica.index') }}'">
|
||||
{{ __('Reset') }}
|
||||
</x-secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Contatti -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Nome/Ragione Sociale
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Tipo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Email
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Telefono
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Città
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($soggetti as $soggetto)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
@if($soggetto->tipo_soggetto === 'Persona Fisica')
|
||||
{{ $soggetto->nome }} {{ $soggetto->cognome }}
|
||||
@else
|
||||
{{ $soggetto->ragione_sociale }}
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
{{ $soggetto->tipo_soggetto === 'Persona Fisica' ? 'bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100' : 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' }}">
|
||||
{{ $soggetto->tipo_soggetto }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $soggetto->email ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $soggetto->telefono ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $soggetto->citta ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.soggetti.show', $soggetto) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
<a href="{{ route('admin.soggetti.edit', $soggetto) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
|
||||
Modifica
|
||||
</a>
|
||||
@if($soggetto->email)
|
||||
<a href="mailto:{{ $soggetto->email }}"
|
||||
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300">
|
||||
Email
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun contatto trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $soggetti->appends(request()->query())->links() }}
|
||||
</div>
|
||||
|
||||
<!-- Statistiche -->
|
||||
<div class="mt-8 grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
|
||||
<h4 class="text-sm font-medium text-blue-800 dark:text-blue-200">Persone Fisiche</h4>
|
||||
<p class="text-2xl font-bold text-blue-900 dark:text-blue-100">
|
||||
{{ $soggetti->where('tipo_soggetto', 'Persona Fisica')->count() }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-4 rounded-lg">
|
||||
<h4 class="text-sm font-medium text-green-800 dark:text-green-200">Persone Giuridiche</h4>
|
||||
<p class="text-2xl font-bold text-green-900 dark:text-green-100">
|
||||
{{ $soggetti->where('tipo_soggetto', 'Persona Giuridica')->count() }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-purple-50 dark:bg-purple-900/20 p-4 rounded-lg">
|
||||
<h4 class="text-sm font-medium text-purple-800 dark:text-purple-200">Totale Contatti</h4>
|
||||
<p class="text-2xl font-bold text-purple-900 dark:text-purple-100">
|
||||
{{ $soggetti->count() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
90
resources/views/admin/stabili/_form.blade.php
Normal file
90
resources/views/admin/stabili/_form.blade.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<div class="space-y-6">
|
||||
<!-- Sezione Informazioni Generali -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Informazioni Generali</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Denominazione -->
|
||||
<div>
|
||||
<x-input-label for="denominazione" :value="__('Denominazione Stabile')" />
|
||||
<x-text-input id="denominazione" name="denominazione" type="text" class="mt-1 block w-full"
|
||||
:value="old('denominazione', $stabile->denominazione ?? '')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('denominazione')" />
|
||||
</div>
|
||||
|
||||
<!-- Codice Fiscale -->
|
||||
<div>
|
||||
<x-input-label for="codice_fiscale" :value="__('Codice Fiscale')" />
|
||||
<x-text-input id="codice_fiscale" name="codice_fiscale" type="text" class="mt-1 block w-full"
|
||||
:value="old('codice_fiscale', $stabile->codice_fiscale ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('codice_fiscale')" />
|
||||
</div>
|
||||
|
||||
<!-- CF Amministratore -->
|
||||
<div>
|
||||
<x-input-label for="cod_fisc_amministratore" :value="__('CF Amministratore')" />
|
||||
<x-text-input id="cod_fisc_amministratore" name="cod_fisc_amministratore" type="text" class="mt-1 block w-full"
|
||||
:value="old('cod_fisc_amministratore', $stabile->cod_fisc_amministratore ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('cod_fisc_amministratore')" />
|
||||
</div>
|
||||
|
||||
<!-- Stato -->
|
||||
<div>
|
||||
<x-input-label for="stato" :value="__('Stato')" />
|
||||
<select id="stato" name="stato" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="attivo" {{ old('stato', $stabile->stato ?? 'attivo') === 'attivo' ? 'selected' : '' }}>Attivo</option>
|
||||
<option value="inattivo" {{ old('stato', $stabile->stato ?? '') === 'inattivo' ? 'selected' : '' }}>Inattivo</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('stato')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Indirizzo -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Indirizzo</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Indirizzo -->
|
||||
<div class="md:col-span-2">
|
||||
<x-input-label for="indirizzo" :value="__('Indirizzo')" />
|
||||
<x-text-input id="indirizzo" name="indirizzo" type="text" class="mt-1 block w-full"
|
||||
:value="old('indirizzo', $stabile->indirizzo ?? '')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('indirizzo')" />
|
||||
</div>
|
||||
|
||||
<!-- Città -->
|
||||
<div>
|
||||
<x-input-label for="citta" :value="__('Città')" />
|
||||
<x-text-input id="citta" name="citta" type="text" class="mt-1 block w-full"
|
||||
:value="old('citta', $stabile->citta ?? '')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('citta')" />
|
||||
</div>
|
||||
|
||||
<!-- CAP -->
|
||||
<div>
|
||||
<x-input-label for="cap" :value="__('CAP')" />
|
||||
<x-text-input id="cap" name="cap" type="text" class="mt-1 block w-full"
|
||||
:value="old('cap', $stabile->cap ?? '')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('cap')" />
|
||||
</div>
|
||||
|
||||
<!-- Provincia -->
|
||||
<div>
|
||||
<x-input-label for="provincia" :value="__('Provincia')" />
|
||||
<x-text-input id="provincia" name="provincia" type="text" class="mt-1 block w-full" maxlength="2"
|
||||
:value="old('provincia', $stabile->provincia ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('provincia')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Note -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Note</h4>
|
||||
<div>
|
||||
<x-input-label for="note" :value="__('Note')" />
|
||||
<textarea id="note" name="note" rows="4"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">{{ old('note', $stabile->note ?? '') }}</textarea>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('note')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
49
resources/views/admin/stabili/create.blade.php
Normal file
49
resources/views/admin/stabili/create.blade.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Nuovo Stabile') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Crea Nuovo Stabile</h3>
|
||||
<a href="{{ route('admin.stabili.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.stabili.store') }}">
|
||||
@csrf
|
||||
@include('admin.stabili._form', ['stabile' => null])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.stabili.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Crea Stabile') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
50
resources/views/admin/stabili/edit.blade.php
Normal file
50
resources/views/admin/stabili/edit.blade.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Modifica Stabile') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Modifica: {{ $stabile->denominazione }}</h3>
|
||||
<a href="{{ route('admin.stabili.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.stabili.update', $stabile) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('admin.stabili._form', ['stabile' => $stabile])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.stabili.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Aggiorna Stabile') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
113
resources/views/admin/stabili/index.blade.php
Normal file
113
resources/views/admin/stabili/index.blade.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Gestione Stabili') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Lista Stabili</h3>
|
||||
<a href="{{ route('admin.stabili.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Stabile
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Stabili -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
ID
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Denominazione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Codice Fiscale
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Indirizzo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Città
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($stabili as $stabile)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $stabile->id_stabile }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $stabile->denominazione }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $stabile->codice_fiscale ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $stabile->indirizzo }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $stabile->citta }} ({{ $stabile->provincia }})
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
{{ $stabile->stato === 'attivo' ? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' : 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100' }}">
|
||||
{{ ucfirst($stabile->stato) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.stabili.show', $stabile) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
<a href="{{ route('admin.stabili.edit', $stabile) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
|
||||
Modifica
|
||||
</a>
|
||||
<form method="POST" action="{{ route('admin.stabili.destroy', $stabile) }}"
|
||||
class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo stabile?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300">
|
||||
Elimina
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessuno stabile trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $stabili->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
116
resources/views/admin/stabili/show.blade.php
Normal file
116
resources/views/admin/stabili/show.blade.php
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dettagli Stabile') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ $stabile->denominazione }}</h3>
|
||||
<div class="space-x-2">
|
||||
<a href="{{ route('admin.stabili.edit', $stabile) }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Modifica
|
||||
</a>
|
||||
<a href="{{ route('admin.stabili.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni Principali -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Informazioni Generali</h4>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">ID Stabile:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->id_stabile }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Denominazione:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->denominazione }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Codice Fiscale:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->codice_fiscale ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">CF Amministratore:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->cod_fisc_amministratore ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Stato:</dt>
|
||||
<dd class="text-sm">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
{{ $stabile->stato === 'attivo' ? 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100' : 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100' }}">
|
||||
{{ ucfirst($stabile->stato) }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Indirizzo</h4>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Indirizzo:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->indirizzo }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Città:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->citta }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">CAP:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->cap }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Provincia:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->provincia ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Note -->
|
||||
@if($stabile->note)
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Note</h4>
|
||||
<p class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->note }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Informazioni Sistema -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Informazioni Sistema</h4>
|
||||
<dl class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Creato il:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->created_at->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Ultimo aggiornamento:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->updated_at->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
@if($stabile->old_id)
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Old ID:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $stabile->old_id }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
153
resources/views/admin/tickets/_form.blade.php
Normal file
153
resources/views/admin/tickets/_form.blade.php
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<div class="space-y-6">
|
||||
<!-- Sezione Informazioni Principali -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Informazioni Principali</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Titolo -->
|
||||
<div class="md:col-span-2">
|
||||
<x-input-label for="titolo" :value="__('Titolo')" />
|
||||
<x-text-input id="titolo" name="titolo" type="text" class="mt-1 block w-full"
|
||||
:value="old('titolo', $ticket->titolo ?? '')" required />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('titolo')" />
|
||||
</div>
|
||||
|
||||
<!-- Stabile -->
|
||||
<div>
|
||||
<x-input-label for="stabile_id" :value="__('Stabile')" />
|
||||
<select id="stabile_id" name="stabile_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="">Seleziona uno stabile</option>
|
||||
@foreach($stabili ?? [] as $stabile)
|
||||
<option value="{{ $stabile->id_stabile }}" {{ old('stabile_id', $ticket->stabile_id ?? '') == $stabile->id_stabile ? 'selected' : '' }}>
|
||||
{{ $stabile->denominazione }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('stabile_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Categoria -->
|
||||
<div>
|
||||
<x-input-label for="categoria_ticket_id" :value="__('Categoria')" />
|
||||
<select id="categoria_ticket_id" name="categoria_ticket_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona una categoria</option>
|
||||
@foreach($categorieTicket ?? [] as $categoria)
|
||||
<option value="{{ $categoria->id }}" {{ old('categoria_ticket_id', $ticket->categoria_ticket_id ?? '') == $categoria->id ? 'selected' : '' }}>
|
||||
{{ $categoria->nome }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('categoria_ticket_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Stato -->
|
||||
<div>
|
||||
<x-input-label for="stato" :value="__('Stato')" />
|
||||
<select id="stato" name="stato" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="Aperto" {{ old('stato', $ticket->stato ?? 'Aperto') === 'Aperto' ? 'selected' : '' }}>Aperto</option>
|
||||
<option value="Preso in Carico" {{ old('stato', $ticket->stato ?? '') === 'Preso in Carico' ? 'selected' : '' }}>Preso in Carico</option>
|
||||
<option value="In Lavorazione" {{ old('stato', $ticket->stato ?? '') === 'In Lavorazione' ? 'selected' : '' }}>In Lavorazione</option>
|
||||
<option value="In Attesa Approvazione" {{ old('stato', $ticket->stato ?? '') === 'In Attesa Approvazione' ? 'selected' : '' }}>In Attesa Approvazione</option>
|
||||
<option value="In Attesa Ricambi" {{ old('stato', $ticket->stato ?? '') === 'In Attesa Ricambi' ? 'selected' : '' }}>In Attesa Ricambi</option>
|
||||
<option value="Risolto" {{ old('stato', $ticket->stato ?? '') === 'Risolto' ? 'selected' : '' }}>Risolto</option>
|
||||
<option value="Chiuso" {{ old('stato', $ticket->stato ?? '') === 'Chiuso' ? 'selected' : '' }}>Chiuso</option>
|
||||
<option value="Annullato" {{ old('stato', $ticket->stato ?? '') === 'Annullato' ? 'selected' : '' }}>Annullato</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('stato')" />
|
||||
</div>
|
||||
|
||||
<!-- Priorità -->
|
||||
<div>
|
||||
<x-input-label for="priorita" :value="__('Priorità')" />
|
||||
<select id="priorita" name="priorita" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" required>
|
||||
<option value="Bassa" {{ old('priorita', $ticket->priorita ?? 'Media') === 'Bassa' ? 'selected' : '' }}>Bassa</option>
|
||||
<option value="Media" {{ old('priorita', $ticket->priorita ?? 'Media') === 'Media' ? 'selected' : '' }}>Media</option>
|
||||
<option value="Alta" {{ old('priorita', $ticket->priorita ?? '') === 'Alta' ? 'selected' : '' }}>Alta</option>
|
||||
<option value="Urgente" {{ old('priorita', $ticket->priorita ?? '') === 'Urgente' ? 'selected' : '' }}>Urgente</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('priorita')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Descrizione -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Descrizione</h4>
|
||||
<div>
|
||||
<x-input-label for="descrizione" :value="__('Descrizione')" />
|
||||
<textarea id="descrizione" name="descrizione" rows="4"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">{{ old('descrizione', $ticket->descrizione ?? '') }}</textarea>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('descrizione')" />
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<x-input-label for="luogo_intervento" :value="__('Luogo Intervento')" />
|
||||
<x-text-input id="luogo_intervento" name="luogo_intervento" type="text" class="mt-1 block w-full"
|
||||
:value="old('luogo_intervento', $ticket->luogo_intervento ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('luogo_intervento')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Assegnazione -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Assegnazione</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Assegnato a Utente -->
|
||||
<div>
|
||||
<x-input-label for="assegnato_a_user_id" :value="__('Assegnato a Utente')" />
|
||||
<select id="assegnato_a_user_id" name="assegnato_a_user_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Nessun utente</option>
|
||||
@foreach($users ?? [] as $user)
|
||||
<option value="{{ $user->id }}" {{ old('assegnato_a_user_id', $ticket->assegnato_a_user_id ?? '') == $user->id ? 'selected' : '' }}>
|
||||
{{ $user->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('assegnato_a_user_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Assegnato a Fornitore -->
|
||||
<div>
|
||||
<x-input-label for="assegnato_a_fornitore_id" :value="__('Assegnato a Fornitore')" />
|
||||
<select id="assegnato_a_fornitore_id" name="assegnato_a_fornitore_id" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Nessun fornitore</option>
|
||||
@foreach($fornitori ?? [] as $fornitore)
|
||||
<option value="{{ $fornitore->id_fornitore }}" {{ old('assegnato_a_fornitore_id', $ticket->assegnato_a_fornitore_id ?? '') == $fornitore->id_fornitore ? 'selected' : '' }}>
|
||||
{{ $fornitore->ragione_sociale }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('assegnato_a_fornitore_id')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Date -->
|
||||
<div class="bg-purple-50 dark:bg-purple-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Date</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Data Scadenza Prevista -->
|
||||
<div>
|
||||
<x-input-label for="data_scadenza_prevista" :value="__('Data Scadenza Prevista')" />
|
||||
<x-text-input id="data_scadenza_prevista" name="data_scadenza_prevista" type="date" class="mt-1 block w-full"
|
||||
:value="old('data_scadenza_prevista', $ticket->data_scadenza_prevista?->format('Y-m-d') ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('data_scadenza_prevista')" />
|
||||
</div>
|
||||
|
||||
<!-- Data Risoluzione Effettiva -->
|
||||
<div>
|
||||
<x-input-label for="data_risoluzione_effettiva" :value="__('Data Risoluzione Effettiva')" />
|
||||
<x-text-input id="data_risoluzione_effettiva" name="data_risoluzione_effettiva" type="date" class="mt-1 block w-full"
|
||||
:value="old('data_risoluzione_effettiva', $ticket->data_risoluzione_effettiva?->format('Y-m-d') ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('data_risoluzione_effettiva')" />
|
||||
</div>
|
||||
|
||||
<!-- Data Chiusura Effettiva -->
|
||||
<div>
|
||||
<x-input-label for="data_chiusura_effettiva" :value="__('Data Chiusura Effettiva')" />
|
||||
<x-text-input id="data_chiusura_effettiva" name="data_chiusura_effettiva" type="date" class="mt-1 block w-full"
|
||||
:value="old('data_chiusura_effettiva', $ticket->data_chiusura_effettiva?->format('Y-m-d') ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('data_chiusura_effettiva')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
49
resources/views/admin/tickets/create.blade.php
Normal file
49
resources/views/admin/tickets/create.blade.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Nuovo Ticket') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Crea Nuovo Ticket</h3>
|
||||
<a href="{{ route('admin.tickets.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.tickets.store') }}">
|
||||
@csrf
|
||||
@include('admin.tickets._form', ['ticket' => null])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.tickets.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Crea Ticket') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
50
resources/views/admin/tickets/edit.blade.php
Normal file
50
resources/views/admin/tickets/edit.blade.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Modifica Ticket') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Modifica: {{ $ticket->titolo }}</h3>
|
||||
<a href="{{ route('admin.tickets.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.tickets.update', $ticket) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('admin.tickets._form', ['ticket' => $ticket])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.tickets.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Aggiorna Ticket') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
149
resources/views/admin/tickets/index.blade.php
Normal file
149
resources/views/admin/tickets/index.blade.php
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Gestione Tickets') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Lista Tickets</h3>
|
||||
<a href="{{ route('admin.tickets.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Ticket
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Tickets -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
ID
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Titolo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stabile
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Priorità
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Data Apertura
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($tickets as $ticket)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->id }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->titolo }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->stabile->denominazione ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($ticket->stato)
|
||||
@case('Aperto')
|
||||
bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100
|
||||
@break
|
||||
@case('Preso in Carico')
|
||||
bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100
|
||||
@break
|
||||
@case('In Lavorazione')
|
||||
bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100
|
||||
@break
|
||||
@case('Risolto')
|
||||
bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100
|
||||
@break
|
||||
@case('Chiuso')
|
||||
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@break
|
||||
@default
|
||||
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@endswitch">
|
||||
{{ $ticket->stato }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($ticket->priorita)
|
||||
@case('Urgente')
|
||||
bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100
|
||||
@break
|
||||
@case('Alta')
|
||||
bg-orange-100 text-orange-800 dark:bg-orange-800 dark:text-orange-100
|
||||
@break
|
||||
@case('Media')
|
||||
bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100
|
||||
@break
|
||||
@case('Bassa')
|
||||
bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100
|
||||
@break
|
||||
@default
|
||||
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@endswitch">
|
||||
{{ $ticket->priorita }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->data_apertura->format('d/m/Y H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('admin.tickets.show', $ticket) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
<a href="{{ route('admin.tickets.edit', $ticket) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300">
|
||||
Modifica
|
||||
</a>
|
||||
<form method="POST" action="{{ route('admin.tickets.destroy', $ticket) }}"
|
||||
class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo ticket?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300">
|
||||
Elimina
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun ticket trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $tickets->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
173
resources/views/admin/tickets/show.blade.php
Normal file
173
resources/views/admin/tickets/show.blade.php
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dettagli Ticket') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ $ticket->titolo }}</h3>
|
||||
<div class="space-x-2">
|
||||
<a href="{{ route('admin.tickets.edit', $ticket) }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Modifica
|
||||
</a>
|
||||
<a href="{{ route('admin.tickets.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni Principali -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Informazioni Generali</h4>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">ID Ticket:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->id }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Titolo:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->titolo }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Stabile:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->stabile->denominazione ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Categoria:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->categoriaTicket->nome ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Stato e Priorità</h4>
|
||||
<dl class="space-y-2">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Stato:</dt>
|
||||
<dd class="text-sm">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($ticket->stato)
|
||||
@case('Aperto')
|
||||
bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100
|
||||
@break
|
||||
@case('Preso in Carico')
|
||||
bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100
|
||||
@break
|
||||
@case('In Lavorazione')
|
||||
bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100
|
||||
@break
|
||||
@case('Risolto')
|
||||
bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100
|
||||
@break
|
||||
@case('Chiuso')
|
||||
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@break
|
||||
@default
|
||||
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@endswitch">
|
||||
{{ $ticket->stato }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Priorità:</dt>
|
||||
<dd class="text-sm">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($ticket->priorita)
|
||||
@case('Urgente')
|
||||
bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100
|
||||
@break
|
||||
@case('Alta')
|
||||
bg-orange-100 text-orange-800 dark:bg-orange-800 dark:text-orange-100
|
||||
@break
|
||||
@case('Media')
|
||||
bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100
|
||||
@break
|
||||
@case('Bassa')
|
||||
bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100
|
||||
@break
|
||||
@default
|
||||
bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@endswitch">
|
||||
{{ $ticket->priorita }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Aperto da:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->apertoUser->name ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Assegnato a:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->assegnatoUser->name ?? $ticket->assegnatoFornitore->ragione_sociale ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Descrizione -->
|
||||
@if($ticket->descrizione)
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Descrizione</h4>
|
||||
<p class="text-sm text-gray-900 dark:text-gray-100 whitespace-pre-wrap">{{ $ticket->descrizione }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Date e Scadenze -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg mb-6">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Date e Scadenze</h4>
|
||||
<dl class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Data Apertura:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->data_apertura->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
@if($ticket->data_scadenza_prevista)
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Scadenza Prevista:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->data_scadenza_prevista->format('d/m/Y') }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if($ticket->data_risoluzione_effettiva)
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Data Risoluzione:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->data_risoluzione_effettiva->format('d/m/Y') }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if($ticket->data_chiusura_effettiva)
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Data Chiusura:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->data_chiusura_effettiva->format('d/m/Y') }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni Sistema -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">Informazioni Sistema</h4>
|
||||
<dl class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Creato il:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->created_at->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Ultimo aggiornamento:</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100">{{ $ticket->updated_at->format('d/m/Y H:i') }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
105
resources/views/admin/unita_immobiliari/_form.blade.php
Normal file
105
resources/views/admin/unita_immobiliari/_form.blade.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<div class="space-y-6">
|
||||
<!-- Campo nascosto per stabile_id -->
|
||||
<input type="hidden" name="stabile_id" value="{{ $stabile->id_stabile }}">
|
||||
|
||||
<!-- Sezione Identificazione -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Identificazione Unità</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Interno -->
|
||||
<div>
|
||||
<x-input-label for="interno" :value="__('Interno')" />
|
||||
<x-text-input id="interno" name="interno" type="text" class="mt-1 block w-full"
|
||||
:value="old('interno', $unitaImmobiliare->interno ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('interno')" />
|
||||
</div>
|
||||
|
||||
<!-- Scala -->
|
||||
<div>
|
||||
<x-input-label for="scala" :value="__('Scala')" />
|
||||
<x-text-input id="scala" name="scala" type="text" class="mt-1 block w-full"
|
||||
:value="old('scala', $unitaImmobiliare->scala ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('scala')" />
|
||||
</div>
|
||||
|
||||
<!-- Piano -->
|
||||
<div>
|
||||
<x-input-label for="piano" :value="__('Piano')" />
|
||||
<x-text-input id="piano" name="piano" type="text" class="mt-1 block w-full"
|
||||
:value="old('piano', $unitaImmobiliare->piano ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('piano')" />
|
||||
</div>
|
||||
|
||||
<!-- Fabbricato/Palazzina -->
|
||||
<div>
|
||||
<x-input-label for="fabbricato" :value="__('Fabbricato/Palazzina')" />
|
||||
<x-text-input id="fabbricato" name="fabbricato" type="text" class="mt-1 block w-full"
|
||||
:value="old('fabbricato', $unitaImmobiliare->fabbricato ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('fabbricato')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Dati Catastali -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Dati Catastali e Tecnici</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Categoria Catastale -->
|
||||
<div>
|
||||
<x-input-label for="categoria_catastale" :value="__('Categoria Catastale')" />
|
||||
<x-text-input id="categoria_catastale" name="categoria_catastale" type="text" class="mt-1 block w-full"
|
||||
:value="old('categoria_catastale', $unitaImmobiliare->categoria_catastale ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('categoria_catastale')" />
|
||||
</div>
|
||||
|
||||
<!-- Millesimi Proprietà -->
|
||||
<div>
|
||||
<x-input-label for="millesimi_proprieta" :value="__('Millesimi Proprietà')" />
|
||||
<x-text-input id="millesimi_proprieta" name="millesimi_proprieta" type="number" step="0.0001" class="mt-1 block w-full"
|
||||
:value="old('millesimi_proprieta', $unitaImmobiliare->millesimi_proprieta ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('millesimi_proprieta')" />
|
||||
</div>
|
||||
|
||||
<!-- Superficie -->
|
||||
<div>
|
||||
<x-input-label for="superficie" :value="__('Superficie (mq)')" />
|
||||
<x-text-input id="superficie" name="superficie" type="number" step="0.01" class="mt-1 block w-full"
|
||||
:value="old('superficie', $unitaImmobiliare->superficie ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('superficie')" />
|
||||
</div>
|
||||
|
||||
<!-- Vani -->
|
||||
<div>
|
||||
<x-input-label for="vani" :value="__('Vani')" />
|
||||
<x-text-input id="vani" name="vani" type="number" step="0.01" class="mt-1 block w-full"
|
||||
:value="old('vani', $unitaImmobiliare->vani ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('vani')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Indirizzo -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Indirizzo Specifico</h4>
|
||||
<div>
|
||||
<x-input-label for="indirizzo" :value="__('Indirizzo (se diverso dallo stabile)')" />
|
||||
<x-text-input id="indirizzo" name="indirizzo" type="text" class="mt-1 block w-full"
|
||||
:value="old('indirizzo', $unitaImmobiliare->indirizzo ?? '')" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('indirizzo')" />
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Lascia vuoto se l'indirizzo è lo stesso dello stabile: {{ $stabile->indirizzo }}, {{ $stabile->citta }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Note -->
|
||||
<div class="bg-gray-50 dark:bg-gray-700 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Note</h4>
|
||||
<div>
|
||||
<x-input-label for="note" :value="__('Note')" />
|
||||
<textarea id="note" name="note" rows="3"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">{{ old('note', $unitaImmobiliare->note ?? '') }}</textarea>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('note')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
49
resources/views/admin/unita_immobiliari/create.blade.php
Normal file
49
resources/views/admin/unita_immobiliari/create.blade.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Nuova Unità Immobiliare') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Crea Nuova Unità Immobiliare</h3>
|
||||
<a href="{{ route('admin.stabili.show', $stabile) }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna allo Stabile
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.stabili.unitaImmobiliari.store', $stabile) }}">
|
||||
@csrf
|
||||
@include('admin.unita_immobiliari._form', ['unitaImmobiliare' => null, 'stabile' => $stabile])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.stabili.show', $stabile) }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Crea Unità Immobiliare') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
50
resources/views/admin/unita_immobiliari/edit.blade.php
Normal file
50
resources/views/admin/unita_immobiliari/edit.blade.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Modifica Unità Immobiliare') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Modifica Unità: {{ $unitaImmobiliare->interno ?? 'N/A' }}</h3>
|
||||
<a href="{{ route('admin.stabili.show', $unitaImmobiliare->stabile) }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna allo Stabile
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('admin.unitaImmobiliari.update', $unitaImmobiliare) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('admin.unita_immobiliari._form', ['unitaImmobiliare' => $unitaImmobiliare, 'stabile' => $unitaImmobiliare->stabile])
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('admin.stabili.show', $unitaImmobiliare->stabile) }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Aggiorna Unità Immobiliare') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
262
resources/views/condomino/dashboard.blade.php
Normal file
262
resources/views/condomino/dashboard.blade.php
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dashboard Condomino') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Benvenuto -->
|
||||
<div class="bg-gradient-to-r from-blue-500 to-purple-600 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-white">
|
||||
<h3 class="text-2xl font-bold">Benvenuto, {{ Auth::user()->name }}!</h3>
|
||||
<p class="mt-2">Gestisci le tue proprietà e rimani aggiornato su tutto quello che riguarda il tuo condominio.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistiche Principali -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Le Mie Unità</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['unita_possedute'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Ticket Aperti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['ticket_aperti'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Rate Scadute</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['rate_scadute'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a1 1 0 110 2h-3a1 1 0 01-1-1v-6a1 1 0 00-1-1H9a1 1 0 00-1 1v6a1 1 0 01-1 1H4a1 1 0 110-2V4zm3 1h2v2H7V5zm2 4H7v2h2V9zm2-4h2v2h-2V5zm2 4h-2v2h2V9z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Documenti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $stats['documenti_disponibili'] }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azioni Rapide -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Azioni Rapide</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<a href="{{ route('condomino.tickets.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Ticket
|
||||
</a>
|
||||
<a href="{{ route('condomino.documenti.index') }}"
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Documenti
|
||||
</a>
|
||||
<a href="{{ route('condomino.unita.index') }}"
|
||||
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Le Mie Unità
|
||||
</a>
|
||||
<a href="{{ route('condomino.pagamenti.index') }}"
|
||||
class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Pagamenti
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenuto principale in due colonne -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
<!-- I Miei Ticket -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">I Miei Ticket</h3>
|
||||
<a href="{{ route('condomino.tickets.index') }}" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Vedi tutti
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@forelse($ticketRecenti as $ticket)
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
<a href="{{ route('condomino.tickets.show', $ticket) }}" class="hover:text-blue-600">
|
||||
{{ $ticket->titolo }}
|
||||
</a>
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ $ticket->stabile->denominazione }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-end space-y-1">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium
|
||||
@switch($ticket->stato)
|
||||
@case('Aperto') bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100 @break
|
||||
@case('Preso in Carico') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@case('In Lavorazione') bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100 @break
|
||||
@default bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100
|
||||
@endswitch">
|
||||
{{ $ticket->stato }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $ticket->created_at->diffForHumans() }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-gray-500 dark:text-gray-400 text-center py-4">Nessun ticket aperto</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ultimi Documenti -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Ultimi Documenti</h3>
|
||||
<a href="{{ route('condomino.documenti.index') }}" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Vedi tutti
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@forelse($ultimiDocumenti as $documento)
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<h4 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $documento->nome_file }}
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ $documento->tipo_documento }} - {{ $documento->documentable->denominazione ?? 'N/A' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-end space-y-1">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $documento->created_at->diffForHumans() }}
|
||||
</span>
|
||||
<a href="{{ route('condomino.documenti.download', $documento) }}" class="text-blue-600 hover:text-blue-900 text-xs">
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-gray-500 dark:text-gray-400 text-center py-4">Nessun documento disponibile</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Le Mie Unità -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">Le Mie Unità Immobiliari</h3>
|
||||
<a href="{{ route('condomino.unita.index') }}" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Gestisci
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@forelse($unitaImmobiliari as $unita)
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4">
|
||||
<h4 class="font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $unita->stabile->denominazione }}
|
||||
</h4>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{{ $unita->identificazione_completa }}
|
||||
</p>
|
||||
<div class="mt-3 flex justify-between items-center">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
Millesimi: {{ $unita->millesimi_proprieta ?? 'N/A' }}
|
||||
</span>
|
||||
<a href="{{ route('condomino.unita.show', $unita) }}"
|
||||
class="text-blue-600 hover:text-blue-900 text-sm">
|
||||
Dettagli
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-span-full text-center py-8">
|
||||
<p class="text-gray-500 dark:text-gray-400">Nessuna unità immobiliare associata</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Script per grafici (Chart.js) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
// Implementeremo i grafici quando avremo i dati delle rate e pagamenti
|
||||
</script>
|
||||
</x-app-layout>
|
||||
141
resources/views/condomino/tickets/create.blade.php
Normal file
141
resources/views/condomino/tickets/create.blade.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Nuovo Ticket') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Crea Nuovo Ticket</h3>
|
||||
<a href="{{ route('condomino.tickets.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna ai Ticket
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('condomino.tickets.store') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Sezione Informazioni Principali -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Informazioni Principali</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Titolo -->
|
||||
<div class="md:col-span-2">
|
||||
<x-input-label for="titolo" :value="__('Titolo')" />
|
||||
<x-text-input id="titolo" name="titolo" type="text" class="mt-1 block w-full"
|
||||
:value="old('titolo')" required placeholder="Descrivi brevemente il problema..." />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('titolo')" />
|
||||
</div>
|
||||
|
||||
<!-- Unità Immobiliare -->
|
||||
<div>
|
||||
<x-input-label for="unita_immobiliare_id" :value="__('Unità Immobiliare')" />
|
||||
<select id="unita_immobiliare_id" name="unita_immobiliare_id" required
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona unità</option>
|
||||
@foreach($unitaImmobiliari as $unita)
|
||||
<option value="{{ $unita->id_unita }}" {{ old('unita_immobiliare_id') == $unita->id_unita ? 'selected' : '' }}>
|
||||
{{ $unita->stabile->denominazione }} - {{ $unita->identificazione_completa }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('unita_immobiliare_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Categoria -->
|
||||
<div>
|
||||
<x-input-label for="categoria_ticket_id" :value="__('Categoria')" />
|
||||
<select id="categoria_ticket_id" name="categoria_ticket_id"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="">Seleziona categoria</option>
|
||||
@foreach($categorieTicket as $categoria)
|
||||
<option value="{{ $categoria->id }}" {{ old('categoria_ticket_id') == $categoria->id ? 'selected' : '' }}>
|
||||
{{ $categoria->nome }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('categoria_ticket_id')" />
|
||||
</div>
|
||||
|
||||
<!-- Priorità -->
|
||||
<div>
|
||||
<x-input-label for="priorita" :value="__('Priorità')" />
|
||||
<select id="priorita" name="priorita" required
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
|
||||
<option value="Bassa" {{ old('priorita', 'Media') === 'Bassa' ? 'selected' : '' }}>Bassa</option>
|
||||
<option value="Media" {{ old('priorita', 'Media') === 'Media' ? 'selected' : '' }}>Media</option>
|
||||
<option value="Alta" {{ old('priorita') === 'Alta' ? 'selected' : '' }}>Alta</option>
|
||||
<option value="Urgente" {{ old('priorita') === 'Urgente' ? 'selected' : '' }}>Urgente</option>
|
||||
</select>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('priorita')" />
|
||||
</div>
|
||||
|
||||
<!-- Luogo Intervento -->
|
||||
<div>
|
||||
<x-input-label for="luogo_intervento" :value="__('Luogo Intervento')" />
|
||||
<x-text-input id="luogo_intervento" name="luogo_intervento" type="text" class="mt-1 block w-full"
|
||||
:value="old('luogo_intervento')" placeholder="Es. Bagno, Cucina, Parti comuni..." />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('luogo_intervento')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Descrizione -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Descrizione Dettagliata</h4>
|
||||
<div>
|
||||
<x-input-label for="descrizione" :value="__('Descrizione')" />
|
||||
<textarea id="descrizione" name="descrizione" rows="6" required
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm"
|
||||
placeholder="Descrivi dettagliatamente il problema, quando si è verificato, eventuali danni...">{{ old('descrizione') }}</textarea>
|
||||
<x-input-error class="mt-2" :messages="$errors->get('descrizione')" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Allegati -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-lg">
|
||||
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Allegati (Opzionale)</h4>
|
||||
<div>
|
||||
<x-input-label for="allegati" :value="__('Carica File')" />
|
||||
<input type="file" id="allegati" name="allegati[]" multiple accept="image/*,.pdf,.doc,.docx"
|
||||
class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm" />
|
||||
<x-input-error class="mt-2" :messages="$errors->get('allegati.*')" />
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Puoi caricare foto, documenti PDF, Word. Massimo 10MB per file.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end space-x-4 mt-6">
|
||||
<x-secondary-button type="button" onclick="window.location='{{ route('condomino.tickets.index') }}'">
|
||||
{{ __('Annulla') }}
|
||||
</x-secondary-button>
|
||||
<x-primary-button>
|
||||
{{ __('Crea Ticket') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 text-red-600 dark:text-red-400">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
110
resources/views/condomino/tickets/index.blade.php
Normal file
110
resources/views/condomino/tickets/index.blade.php
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('I Miei Ticket') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-200">I Miei Ticket</h3>
|
||||
<a href="{{ route('condomino.tickets.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Ticket
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Tickets -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Titolo
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stabile
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Stato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Priorità
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Data Apertura
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 dark:border-gray-600 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($tickets as $ticket)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->titolo }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->stabile->denominazione ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($ticket->stato)
|
||||
@case('Aperto') bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100 @break
|
||||
@case('Preso in Carico') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@case('In Lavorazione') bg-blue-100 text-blue-800 dark:bg-blue-800 dark:text-blue-100 @break
|
||||
@case('Risolto') bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100 @break
|
||||
@case('Chiuso') bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100 @break
|
||||
@default bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@endswitch">
|
||||
{{ $ticket->stato }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
@switch($ticket->priorita)
|
||||
@case('Urgente') bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100 @break
|
||||
@case('Alta') bg-orange-100 text-orange-800 dark:bg-orange-800 dark:text-orange-100 @break
|
||||
@case('Media') bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100 @break
|
||||
@case('Bassa') bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100 @break
|
||||
@default bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-100
|
||||
@endswitch">
|
||||
{{ $ticket->priorita }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $ticket->data_apertura->format('d/m/Y H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<a href="{{ route('condomino.tickets.show', $ticket) }}"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Visualizza
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Nessun ticket trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $tickets->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
303
resources/views/layouts/navigation.blade.php
Normal file
303
resources/views/layouts/navigation.blade.php
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
<nav x-data="{ open: false }" class="bg-white dark:bg-gray-800 border-b border-gray-100 dark:border-gray-700">
|
||||
<!-- Primary Navigation Menu -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<!-- Logo -->
|
||||
<div class="shrink-0 flex items-center">
|
||||
<a href="{{ route('dashboard') }}">
|
||||
<x-application-logo class="block h-9 w-auto fill-current text-gray-800 dark:text-gray-200" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
|
||||
@role('super-admin')
|
||||
<!-- Super Admin Menu -->
|
||||
<x-nav-link :href="route('superadmin.dashboard')" :active="request()->routeIs('superadmin.dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('superadmin.users.index')" :active="request()->routeIs('superadmin.users.*')">
|
||||
{{ __('Utenti') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('superadmin.amministratori.index')" :active="request()->routeIs('superadmin.amministratori.*')">
|
||||
{{ __('Amministratori') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('superadmin.categorie-ticket.index')" :active="request()->routeIs('superadmin.categorie-ticket.*')">
|
||||
{{ __('Categorie Ticket') }}
|
||||
</x-nav-link>
|
||||
@endrole
|
||||
|
||||
@role('admin|amministratore')
|
||||
<!-- Admin Menu -->
|
||||
<x-nav-link :href="route('admin.dashboard')" :active="request()->routeIs('admin.dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
|
||||
<!-- Dropdown Stabili -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<x-dropdown align="left" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ __('Stabili') }}</div>
|
||||
<div class="ml-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('admin.stabili.index')">
|
||||
{{ __('Elenco Stabili') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.stabili.create')">
|
||||
{{ __('Nuovo Stabile') }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
||||
<x-nav-link :href="route('admin.soggetti.index')" :active="request()->routeIs('admin.soggetti.*')">
|
||||
{{ __('Soggetti') }}
|
||||
</x-nav-link>
|
||||
|
||||
<!-- Dropdown Gestione -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<x-dropdown align="left" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ __('Gestione') }}</div>
|
||||
<div class="ml-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('admin.tickets.index')">
|
||||
{{ __('Ticket') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.fornitori.index')">
|
||||
{{ __('Fornitori') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.documenti.index')">
|
||||
{{ __('Documenti') }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Contabilità -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<x-dropdown align="left" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ __('Contabilità') }}</div>
|
||||
<div class="ml-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('admin.contabilita.index')">
|
||||
{{ __('Dashboard Contabilità') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.contabilita.registrazione')">
|
||||
{{ __('Nuova Registrazione') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.contabilita.movimenti')">
|
||||
{{ __('Movimenti') }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Impostazioni -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<x-dropdown align="left" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ __('Strumenti') }}</div>
|
||||
<div class="ml-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('admin.rubrica.index')">
|
||||
{{ __('Rubrica') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.impostazioni.index')">
|
||||
{{ __('Impostazioni') }}
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link :href="route('admin.api-tokens.index')">
|
||||
{{ __('API Tokens') }}
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
@endrole
|
||||
|
||||
@role('condomino')
|
||||
<!-- Condomino Menu -->
|
||||
<x-nav-link :href="route('condomino.dashboard')" :active="request()->routeIs('condomino.dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('condomino.scadenze')" :active="request()->routeIs('condomino.scadenze')">
|
||||
{{ __('Scadenze') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('condomino.documenti')" :active="request()->routeIs('condomino.documenti')">
|
||||
{{ __('Documenti') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('condomino.guasti')" :active="request()->routeIs('condomino.guasti')">
|
||||
{{ __('Segnalazioni') }}
|
||||
</x-nav-link>
|
||||
@endrole
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Dropdown -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ Auth::user()->name }}</div>
|
||||
|
||||
<div class="ml-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('profile.edit')">
|
||||
{{ __('Profile') }}
|
||||
</x-dropdown-link>
|
||||
|
||||
@impersonating
|
||||
<x-dropdown-link :href="route('impersonate.leave')">
|
||||
{{ __('Torna al tuo account') }}
|
||||
</x-dropdown-link>
|
||||
@endImpersonating
|
||||
|
||||
<!-- Authentication -->
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<x-dropdown-link :href="route('logout')"
|
||||
onclick="event.preventDefault();
|
||||
this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
||||
<!-- Hamburger -->
|
||||
<div class="-mr-2 flex items-center sm:hidden">
|
||||
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Responsive Navigation Menu -->
|
||||
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
|
||||
<div class="pt-2 pb-3 space-y-1">
|
||||
@role('super-admin')
|
||||
<x-responsive-nav-link :href="route('superadmin.dashboard')" :active="request()->routeIs('superadmin.dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('superadmin.users.index')" :active="request()->routeIs('superadmin.users.*')">
|
||||
{{ __('Utenti') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('superadmin.amministratori.index')" :active="request()->routeIs('superadmin.amministratori.*')">
|
||||
{{ __('Amministratori') }}
|
||||
</x-responsive-nav-link>
|
||||
@endrole
|
||||
|
||||
@role('admin|amministratore')
|
||||
<x-responsive-nav-link :href="route('admin.dashboard')" :active="request()->routeIs('admin.dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('admin.stabili.index')" :active="request()->routeIs('admin.stabili.*')">
|
||||
{{ __('Stabili') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('admin.soggetti.index')" :active="request()->routeIs('admin.soggetti.*')">
|
||||
{{ __('Soggetti') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('admin.tickets.index')" :active="request()->routeIs('admin.tickets.*')">
|
||||
{{ __('Ticket') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('admin.fornitori.index')" :active="request()->routeIs('admin.fornitori.*')">
|
||||
{{ __('Fornitori') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('admin.documenti.index')" :active="request()->routeIs('admin.documenti.*')">
|
||||
{{ __('Documenti') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('admin.contabilita.index')" :active="request()->routeIs('admin.contabilita.*')">
|
||||
{{ __('Contabilità') }}
|
||||
</x-responsive-nav-link>
|
||||
@endrole
|
||||
|
||||
@role('condomino')
|
||||
<x-responsive-nav-link :href="route('condomino.dashboard')" :active="request()->routeIs('condomino.dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('condomino.scadenze')" :active="request()->routeIs('condomino.scadenze')">
|
||||
{{ __('Scadenze') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('condomino.documenti')" :active="request()->routeIs('condomino.documenti')">
|
||||
{{ __('Documenti') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('condomino.guasti')" :active="request()->routeIs('condomino.guasti')">
|
||||
{{ __('Segnalazioni') }}
|
||||
</x-responsive-nav-link>
|
||||
@endrole
|
||||
</div>
|
||||
|
||||
<!-- Responsive Settings Options -->
|
||||
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-600">
|
||||
<div class="px-4">
|
||||
<div class="font-medium text-base text-gray-800 dark:text-gray-200">{{ Auth::user()->name }}</div>
|
||||
<div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 space-y-1">
|
||||
<x-responsive-nav-link :href="route('profile.edit')">
|
||||
{{ __('Profile') }}
|
||||
</x-responsive-nav-link>
|
||||
|
||||
@impersonating
|
||||
<x-responsive-nav-link :href="route('impersonate.leave')">
|
||||
{{ __('Torna al tuo account') }}
|
||||
</x-responsive-nav-link>
|
||||
@endImpersonating
|
||||
|
||||
<!-- Authentication -->
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<x-responsive-nav-link :href="route('logout')"
|
||||
onclick="event.preventDefault();
|
||||
this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-responsive-nav-link>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
206
resources/views/superadmin/amministratori/create.blade.php
Normal file
206
resources/views/superadmin/amministratori/create.blade.php
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Crea Nuovo Amministratore</h2>
|
||||
<a href="{{ route('superadmin.amministratori.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.amministratori.store') }}" class="space-y-8">
|
||||
@csrf
|
||||
|
||||
<!-- Sezione Dati Utente -->
|
||||
<div class="bg-gray-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Dati Utente (Account di accesso)</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nome Utente -->
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700">Nome Utente</label>
|
||||
<input type="text" name="name" id="name" value="{{ old('name') }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('name') border-red-500 @enderror">
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email Utente -->
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700">Email Utente</label>
|
||||
<input type="email" name="email" id="email" value="{{ old('email') }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('email') border-red-500 @enderror">
|
||||
@error('email')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" name="password" id="password" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('password') border-red-500 @enderror">
|
||||
@error('password')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Conferma Password -->
|
||||
<div>
|
||||
<label for="password_confirmation" class="block text-sm font-medium text-gray-700">Conferma Password</label>
|
||||
<input type="password" name="password_confirmation" id="password_confirmation" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Dati Amministratore -->
|
||||
<div class="bg-blue-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Dati Personali Amministratore</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nome -->
|
||||
<div>
|
||||
<label for="nome" class="block text-sm font-medium text-gray-700">Nome</label>
|
||||
<input type="text" name="nome" id="nome" value="{{ old('nome') }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('nome') border-red-500 @enderror">
|
||||
@error('nome')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Cognome -->
|
||||
<div>
|
||||
<label for="cognome" class="block text-sm font-medium text-gray-700">Cognome</label>
|
||||
<input type="text" name="cognome" id="cognome" value="{{ old('cognome') }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('cognome') border-red-500 @enderror">
|
||||
@error('cognome')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Dati Studio -->
|
||||
<div class="bg-green-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Dati Studio Professionale</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Denominazione Studio -->
|
||||
<div class="md:col-span-2">
|
||||
<label for="denominazione_studio" class="block text-sm font-medium text-gray-700">Denominazione Studio</label>
|
||||
<input type="text" name="denominazione_studio" id="denominazione_studio" value="{{ old('denominazione_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('denominazione_studio') border-red-500 @enderror">
|
||||
@error('denominazione_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Partita IVA -->
|
||||
<div>
|
||||
<label for="partita_iva" class="block text-sm font-medium text-gray-700">Partita IVA</label>
|
||||
<input type="text" name="partita_iva" id="partita_iva" value="{{ old('partita_iva') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('partita_iva') border-red-500 @enderror">
|
||||
@error('partita_iva')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Codice Fiscale Studio -->
|
||||
<div>
|
||||
<label for="codice_fiscale_studio" class="block text-sm font-medium text-gray-700">Codice Fiscale Studio</label>
|
||||
<input type="text" name="codice_fiscale_studio" id="codice_fiscale_studio" value="{{ old('codice_fiscale_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('codice_fiscale_studio') border-red-500 @enderror">
|
||||
@error('codice_fiscale_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Indirizzo Studio -->
|
||||
<div class="md:col-span-2">
|
||||
<label for="indirizzo_studio" class="block text-sm font-medium text-gray-700">Indirizzo Studio</label>
|
||||
<input type="text" name="indirizzo_studio" id="indirizzo_studio" value="{{ old('indirizzo_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('indirizzo_studio') border-red-500 @enderror">
|
||||
@error('indirizzo_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- CAP Studio -->
|
||||
<div>
|
||||
<label for="cap_studio" class="block text-sm font-medium text-gray-700">CAP Studio</label>
|
||||
<input type="text" name="cap_studio" id="cap_studio" value="{{ old('cap_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('cap_studio') border-red-500 @enderror">
|
||||
@error('cap_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Città Studio -->
|
||||
<div>
|
||||
<label for="citta_studio" class="block text-sm font-medium text-gray-700">Città Studio</label>
|
||||
<input type="text" name="citta_studio" id="citta_studio" value="{{ old('citta_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('citta_studio') border-red-500 @enderror">
|
||||
@error('citta_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Provincia Studio -->
|
||||
<div>
|
||||
<label for="provincia_studio" class="block text-sm font-medium text-gray-700">Provincia Studio</label>
|
||||
<input type="text" name="provincia_studio" id="provincia_studio" value="{{ old('provincia_studio') }}" maxlength="2"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('provincia_studio') border-red-500 @enderror">
|
||||
@error('provincia_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Telefono Studio -->
|
||||
<div>
|
||||
<label for="telefono_studio" class="block text-sm font-medium text-gray-700">Telefono Studio</label>
|
||||
<input type="text" name="telefono_studio" id="telefono_studio" value="{{ old('telefono_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('telefono_studio') border-red-500 @enderror">
|
||||
@error('telefono_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email Studio -->
|
||||
<div>
|
||||
<label for="email_studio" class="block text-sm font-medium text-gray-700">Email Studio</label>
|
||||
<input type="email" name="email_studio" id="email_studio" value="{{ old('email_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('email_studio') border-red-500 @enderror">
|
||||
@error('email_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- PEC Studio -->
|
||||
<div>
|
||||
<label for="pec_studio" class="block text-sm font-medium text-gray-700">PEC Studio</label>
|
||||
<input type="email" name="pec_studio" id="pec_studio" value="{{ old('pec_studio') }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('pec_studio') border-red-500 @enderror">
|
||||
@error('pec_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<a href="{{ route('superadmin.amministratori.index') }}"
|
||||
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Annulla
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Crea Amministratore
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
195
resources/views/superadmin/amministratori/edit.blade.php
Normal file
195
resources/views/superadmin/amministratori/edit.blade.php
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Modifica Amministratore: {{ $amministratore->nome }} {{ $amministratore->cognome }}</h2>
|
||||
<a href="{{ route('superadmin.amministratori.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.amministratori.update', $amministratore) }}" class="space-y-8">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<!-- Sezione Utente Associato -->
|
||||
<div class="bg-gray-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Utente Associato</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Selezione Utente -->
|
||||
<div class="md:col-span-2">
|
||||
<label for="user_id" class="block text-sm font-medium text-gray-700">Utente Associato</label>
|
||||
<select name="user_id" id="user_id" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('user_id') border-red-500 @enderror">
|
||||
@foreach($usersWithoutAdminRole as $user)
|
||||
<option value="{{ $user->id }}"
|
||||
{{ old('user_id', $amministratore->user_id) == $user->id ? 'selected' : '' }}>
|
||||
{{ $user->name }} ({{ $user->email }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('user_id')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 text-sm text-gray-500">Attualmente associato a: {{ $amministratore->user->name }} ({{ $amministratore->user->email }})</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Dati Amministratore -->
|
||||
<div class="bg-blue-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Dati Personali Amministratore</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Nome -->
|
||||
<div>
|
||||
<label for="nome" class="block text-sm font-medium text-gray-700">Nome</label>
|
||||
<input type="text" name="nome" id="nome" value="{{ old('nome', $amministratore->nome) }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('nome') border-red-500 @enderror">
|
||||
@error('nome')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Cognome -->
|
||||
<div>
|
||||
<label for="cognome" class="block text-sm font-medium text-gray-700">Cognome</label>
|
||||
<input type="text" name="cognome" id="cognome" value="{{ old('cognome', $amministratore->cognome) }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('cognome') border-red-500 @enderror">
|
||||
@error('cognome')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sezione Dati Studio -->
|
||||
<div class="bg-green-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Dati Studio Professionale</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Denominazione Studio -->
|
||||
<div class="md:col-span-2">
|
||||
<label for="denominazione_studio" class="block text-sm font-medium text-gray-700">Denominazione Studio</label>
|
||||
<input type="text" name="denominazione_studio" id="denominazione_studio" value="{{ old('denominazione_studio', $amministratore->denominazione_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('denominazione_studio') border-red-500 @enderror">
|
||||
@error('denominazione_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Partita IVA -->
|
||||
<div>
|
||||
<label for="partita_iva" class="block text-sm font-medium text-gray-700">Partita IVA</label>
|
||||
<input type="text" name="partita_iva" id="partita_iva" value="{{ old('partita_iva', $amministratore->partita_iva) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('partita_iva') border-red-500 @enderror">
|
||||
@error('partita_iva')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Codice Fiscale Studio -->
|
||||
<div>
|
||||
<label for="codice_fiscale_studio" class="block text-sm font-medium text-gray-700">Codice Fiscale Studio</label>
|
||||
<input type="text" name="codice_fiscale_studio" id="codice_fiscale_studio" value="{{ old('codice_fiscale_studio', $amministratore->codice_fiscale_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('codice_fiscale_studio') border-red-500 @enderror">
|
||||
@error('codice_fiscale_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Indirizzo Studio -->
|
||||
<div class="md:col-span-2">
|
||||
<label for="indirizzo_studio" class="block text-sm font-medium text-gray-700">Indirizzo Studio</label>
|
||||
<input type="text" name="indirizzo_studio" id="indirizzo_studio" value="{{ old('indirizzo_studio', $amministratore->indirizzo_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('indirizzo_studio') border-red-500 @enderror">
|
||||
@error('indirizzo_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- CAP Studio -->
|
||||
<div>
|
||||
<label for="cap_studio" class="block text-sm font-medium text-gray-700">CAP Studio</label>
|
||||
<input type="text" name="cap_studio" id="cap_studio" value="{{ old('cap_studio', $amministratore->cap_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('cap_studio') border-red-500 @enderror">
|
||||
@error('cap_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Città Studio -->
|
||||
<div>
|
||||
<label for="citta_studio" class="block text-sm font-medium text-gray-700">Città Studio</label>
|
||||
<input type="text" name="citta_studio" id="citta_studio" value="{{ old('citta_studio', $amministratore->citta_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('citta_studio') border-red-500 @enderror">
|
||||
@error('citta_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Provincia Studio -->
|
||||
<div>
|
||||
<label for="provincia_studio" class="block text-sm font-medium text-gray-700">Provincia Studio</label>
|
||||
<input type="text" name="provincia_studio" id="provincia_studio" value="{{ old('provincia_studio', $amministratore->provincia_studio) }}" maxlength="2"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('provincia_studio') border-red-500 @enderror">
|
||||
@error('provincia_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Telefono Studio -->
|
||||
<div>
|
||||
<label for="telefono_studio" class="block text-sm font-medium text-gray-700">Telefono Studio</label>
|
||||
<input type="text" name="telefono_studio" id="telefono_studio" value="{{ old('telefono_studio', $amministratore->telefono_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('telefono_studio') border-red-500 @enderror">
|
||||
@error('telefono_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email Studio -->
|
||||
<div>
|
||||
<label for="email_studio" class="block text-sm font-medium text-gray-700">Email Studio</label>
|
||||
<input type="email" name="email_studio" id="email_studio" value="{{ old('email_studio', $amministratore->email_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('email_studio') border-red-500 @enderror">
|
||||
@error('email_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- PEC Studio -->
|
||||
<div>
|
||||
<label for="pec_studio" class="block text-sm font-medium text-gray-700">PEC Studio</label>
|
||||
<input type="email" name="pec_studio" id="pec_studio" value="{{ old('pec_studio', $amministratore->pec_studio) }}"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('pec_studio') border-red-500 @enderror">
|
||||
@error('pec_studio')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info aggiuntive -->
|
||||
<div class="bg-gray-50 p-4 rounded-md">
|
||||
<h3 class="text-sm font-medium text-gray-700 mb-2">Informazioni Record</h3>
|
||||
<p class="text-sm text-gray-600">Creato il: {{ $amministratore->created_at->format('d/m/Y H:i') }}</p>
|
||||
<p class="text-sm text-gray-600">Ultimo aggiornamento: {{ $amministratore->updated_at->format('d/m/Y H:i') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<a href="{{ route('superadmin.amministratori.index') }}"
|
||||
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Annulla
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Aggiorna Amministratore
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
95
resources/views/superadmin/amministratori/index.blade.php
Normal file
95
resources/views/superadmin/amministratori/index.blade.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Gestione Amministratori</h2>
|
||||
<a href="{{ route('superadmin.amministratori.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Amministratore
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Amministratori -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white border border-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
ID
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Nome e Cognome
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Denominazione Studio
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Utente Associato
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Partita IVA
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Email Studio
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@forelse($amministratori as $amministratore)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $amministratore->id_amministratore }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{{ $amministratore->nome }} {{ $amministratore->cognome }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $amministratore->denominazione_studio ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
<div>
|
||||
<div class="font-medium">{{ $amministratore->user->name }}</div>
|
||||
<div class="text-gray-500">{{ $amministratore->user->email }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $amministratore->partita_iva ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $amministratore->email_studio ?? '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('superadmin.amministratori.edit', $amministratore) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900">Modifica</a>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.amministratori.destroy', $amministratore) }}" class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo amministratore? Verrà eliminato anche l\'utente associato.')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900">Elimina</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500">
|
||||
Nessun amministratore trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $amministratori->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
82
resources/views/superadmin/categorie_ticket/create.blade.php
Normal file
82
resources/views/superadmin/categorie_ticket/create.blade.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Crea Nuova Categoria Ticket</h2>
|
||||
<a href="{{ route('superadmin.categorie-ticket.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.categorie-ticket.store') }}" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
<!-- Sezione Dati Categoria -->
|
||||
<div class="bg-blue-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Informazioni Categoria</h3>
|
||||
<div class="space-y-6">
|
||||
<!-- Nome -->
|
||||
<div>
|
||||
<label for="nome" class="block text-sm font-medium text-gray-700">
|
||||
Nome Categoria <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
name="nome"
|
||||
id="nome"
|
||||
value="{{ old('nome') }}"
|
||||
required
|
||||
maxlength="255"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('nome') border-red-500 @enderror"
|
||||
placeholder="Es. Manutenzione, Amministrativo, Tecnico...">
|
||||
@error('nome')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 text-sm text-gray-500">Il nome deve essere unico nel sistema</p>
|
||||
</div>
|
||||
|
||||
<!-- Descrizione -->
|
||||
<div>
|
||||
<label for="descrizione" class="block text-sm font-medium text-gray-700">
|
||||
Descrizione
|
||||
</label>
|
||||
<textarea name="descrizione"
|
||||
id="descrizione"
|
||||
rows="4"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('descrizione') border-red-500 @enderror"
|
||||
placeholder="Descrizione dettagliata della categoria (opzionale)">{{ old('descrizione') }}</textarea>
|
||||
@error('descrizione')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 text-sm text-gray-500">Fornisci una descrizione per aiutare gli utenti a comprendere quando utilizzare questa categoria</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni aggiuntive -->
|
||||
<div class="bg-gray-50 p-4 rounded-md">
|
||||
<h3 class="text-sm font-medium text-gray-700 mb-2">Informazioni</h3>
|
||||
<ul class="text-sm text-gray-600 space-y-1">
|
||||
<li>• I campi contrassegnati con <span class="text-red-500">*</span> sono obbligatori</li>
|
||||
<li>• Il nome della categoria deve essere unico nel sistema</li>
|
||||
<li>• La descrizione aiuta gli utenti a scegliere la categoria corretta per i loro ticket</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<a href="{{ route('superadmin.categorie-ticket.index') }}"
|
||||
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Annulla
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Crea Categoria
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
107
resources/views/superadmin/categorie_ticket/edit.blade.php
Normal file
107
resources/views/superadmin/categorie_ticket/edit.blade.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Modifica Categoria: {{ $categoriaTicket->nome }}</h2>
|
||||
<a href="{{ route('superadmin.categorie-ticket.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.categorie-ticket.update', $categoriaTicket) }}" class="space-y-6">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<!-- Sezione Dati Categoria -->
|
||||
<div class="bg-blue-50 p-6 rounded-lg">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Informazioni Categoria</h3>
|
||||
<div class="space-y-6">
|
||||
<!-- Nome -->
|
||||
<div>
|
||||
<label for="nome" class="block text-sm font-medium text-gray-700">
|
||||
Nome Categoria <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
name="nome"
|
||||
id="nome"
|
||||
value="{{ old('nome', $categoriaTicket->nome) }}"
|
||||
required
|
||||
maxlength="255"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('nome') border-red-500 @enderror"
|
||||
placeholder="Es. Manutenzione, Amministrativo, Tecnico...">
|
||||
@error('nome')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 text-sm text-gray-500">Il nome deve essere unico nel sistema</p>
|
||||
</div>
|
||||
|
||||
<!-- Descrizione -->
|
||||
<div>
|
||||
<label for="descrizione" class="block text-sm font-medium text-gray-700">
|
||||
Descrizione
|
||||
</label>
|
||||
<textarea name="descrizione"
|
||||
id="descrizione"
|
||||
rows="4"
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('descrizione') border-red-500 @enderror"
|
||||
placeholder="Descrizione dettagliata della categoria (opzionale)">{{ old('descrizione', $categoriaTicket->descrizione) }}</textarea>
|
||||
@error('descrizione')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 text-sm text-gray-500">Fornisci una descrizione per aiutare gli utenti a comprendere quando utilizzare questa categoria</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info aggiuntive -->
|
||||
<div class="bg-gray-50 p-4 rounded-md">
|
||||
<h3 class="text-sm font-medium text-gray-700 mb-2">Informazioni Record</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-gray-600">
|
||||
<div>
|
||||
<span class="font-medium">ID:</span> {{ $categoriaTicket->id }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Creato il:</span> {{ $categoriaTicket->created_at->format('d/m/Y H:i') }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Ultimo aggiornamento:</span> {{ $categoriaTicket->updated_at->format('d/m/Y H:i') }}
|
||||
</div>
|
||||
@if($categoriaTicket->tickets_count ?? 0 > 0)
|
||||
<div>
|
||||
<span class="font-medium">Ticket associati:</span>
|
||||
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-xs">
|
||||
{{ $categoriaTicket->tickets_count }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informazioni aggiuntive -->
|
||||
<div class="bg-yellow-50 p-4 rounded-md">
|
||||
<h3 class="text-sm font-medium text-yellow-800 mb-2">⚠️ Attenzione</h3>
|
||||
<ul class="text-sm text-yellow-700 space-y-1">
|
||||
<li>• I campi contrassegnati con <span class="text-red-500">*</span> sono obbligatori</li>
|
||||
<li>• Il nome della categoria deve essere unico nel sistema</li>
|
||||
<li>• Le modifiche si applicheranno a tutti i ticket esistenti di questa categoria</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<a href="{{ route('superadmin.categorie-ticket.index') }}"
|
||||
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Annulla
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Aggiorna Categoria
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
87
resources/views/superadmin/categorie_ticket/index.blade.php
Normal file
87
resources/views/superadmin/categorie_ticket/index.blade.php
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Gestione Categorie Ticket</h2>
|
||||
<a href="{{ route('superadmin.categorie-ticket.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuova Categoria
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Categorie Ticket -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white border border-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
ID
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Nome
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Descrizione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Data Creazione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@forelse($categorieTicket as $categoria)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $categoria->id }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{{ $categoria->nome }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-900">
|
||||
<div class="max-w-xs truncate" title="{{ $categoria->descrizione }}">
|
||||
{{ $categoria->descrizione ?? '-' }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $categoria->created_at->format('d/m/Y H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('superadmin.categorie-ticket.edit', $categoria) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900">
|
||||
Modifica
|
||||
</a>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.categorie-ticket.destroy', $categoria) }}"
|
||||
class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questa categoria? Questa azione non può essere annullata.')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900">
|
||||
Elimina
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">
|
||||
Nessuna categoria ticket trovata
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $categorieTicket->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
139
resources/views/superadmin/dashboard.blade.php
Normal file
139
resources/views/superadmin/dashboard.blade.php
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<h2 class="text-3xl font-bold text-gray-800">Dashboard Super Admin</h2>
|
||||
<p class="text-gray-600 mt-2">Benvenuto nel pannello di amministrazione del sistema</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistiche -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Totale Utenti -->
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Totale Utenti</dt>
|
||||
<dd class="text-lg font-medium text-gray-900">{{ \App\Models\User::count() }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Totale Amministratori -->
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Amministratori</dt>
|
||||
<dd class="text-lg font-medium text-gray-900">{{ \App\Models\Amministratore::count() }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Utenti per Ruolo -->
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-8 h-8 bg-purple-500 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">Super Admin</dt>
|
||||
<dd class="text-lg font-medium text-gray-900">{{ \App\Models\User::role('super-admin')->count() }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azioni Rapide -->
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Azioni Rapide</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<a href="{{ route('superadmin.users.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Utente
|
||||
</a>
|
||||
<a href="{{ route('superadmin.amministratori.create') }}"
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Nuovo Amministratore
|
||||
</a>
|
||||
<a href="{{ route('superadmin.users.index') }}"
|
||||
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Gestisci Utenti
|
||||
</a>
|
||||
<a href="{{ route('superadmin.amministratori.index') }}"
|
||||
class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded text-center transition duration-200">
|
||||
Gestisci Amministratori
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ultimi Utenti Creati -->
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Ultimi Utenti Creati</h3>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nome</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ruolo</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Data Creazione</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@foreach(\App\Models\User::latest()->take(5)->get() as $user)
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ $user->name }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $user->email }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
@foreach($user->roles as $role)
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 mr-1">
|
||||
{{ $role->name }}
|
||||
</span>
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $user->created_at->format('d/m/Y H:i') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
93
resources/views/superadmin/layouts/app.blade.php
Normal file
93
resources/views/superadmin/layouts/app.blade.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'Laravel') }} - Super Admin</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Scripts -->
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="font-sans antialiased bg-gray-100">
|
||||
<div class="min-h-screen">
|
||||
<!-- Navigation -->
|
||||
<nav class="bg-white shadow-sm border-b border-gray-200">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<!-- Logo -->
|
||||
<div class="shrink-0 flex items-center">
|
||||
<a href="{{ route('superadmin.dashboard') }}" class="text-xl font-bold text-gray-800">
|
||||
Super Admin Panel
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
|
||||
<a href="{{ route('superadmin.dashboard') }}"
|
||||
class="inline-flex items-center px-1 pt-1 border-b-2 {{ request()->routeIs('superadmin.dashboard') ? 'border-indigo-400 text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }} text-sm font-medium">
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="{{ route('superadmin.users.index') }}"
|
||||
class="inline-flex items-center px-1 pt-1 border-b-2 {{ request()->routeIs('superadmin.users.*') ? 'border-indigo-400 text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }} text-sm font-medium">
|
||||
Utenti
|
||||
</a>
|
||||
<a href="{{ route('superadmin.amministratori.index') }}"
|
||||
class="inline-flex items-center px-1 pt-1 border-b-2 {{ request()->routeIs('superadmin.amministratori.*') ? 'border-indigo-400 text-gray-900' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }} text-sm font-medium">
|
||||
Amministratori
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Dropdown -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<div class="ml-3 relative">
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="text-sm text-gray-700">{{ Auth::user()->name }}</span>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="text-sm text-gray-500 hover:text-gray-700">
|
||||
Logout
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Page Content -->
|
||||
<main class="py-6">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<!-- Flash Messages -->
|
||||
@if (session('success'))
|
||||
<div class="mb-4 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('error'))
|
||||
<div class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('status'))
|
||||
<div class="mb-4 bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@yield('content')
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
85
resources/views/superadmin/users/create.blade.php
Normal file
85
resources/views/superadmin/users/create.blade.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Crea Nuovo Utente</h2>
|
||||
<a href="{{ route('superadmin.users.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.users.store') }}" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
<!-- Nome -->
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700">Nome</label>
|
||||
<input type="text" name="name" id="name" value="{{ old('name') }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('name') border-red-500 @enderror">
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
||||
<input type="email" name="email" id="email" value="{{ old('email') }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('email') border-red-500 @enderror">
|
||||
@error('email')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" name="password" id="password" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('password') border-red-500 @enderror">
|
||||
@error('password')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Conferma Password -->
|
||||
<div>
|
||||
<label for="password_confirmation" class="block text-sm font-medium text-gray-700">Conferma Password</label>
|
||||
<input type="password" name="password_confirmation" id="password_confirmation" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500">
|
||||
</div>
|
||||
|
||||
<!-- Ruolo -->
|
||||
<div>
|
||||
<label for="role" class="block text-sm font-medium text-gray-700">Ruolo</label>
|
||||
<select name="role" id="role" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('role') border-red-500 @enderror">
|
||||
<option value="">Seleziona un ruolo</option>
|
||||
@foreach($roles as $role)
|
||||
<option value="{{ $role->name }}" {{ old('role') == $role->name ? 'selected' : '' }}>
|
||||
{{ $role->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('role')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<a href="{{ route('superadmin.users.index') }}"
|
||||
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Annulla
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Crea Utente
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
82
resources/views/superadmin/users/edit.blade.php
Normal file
82
resources/views/superadmin/users/edit.blade.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Modifica Utente: {{ $user->name }}</h2>
|
||||
<a href="{{ route('superadmin.users.index') }}"
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
|
||||
Torna alla Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.users.update', $user) }}" class="space-y-6">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<!-- Nome -->
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-gray-700">Nome</label>
|
||||
<input type="text" name="name" id="name" value="{{ old('name', $user->name) }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('name') border-red-500 @enderror">
|
||||
@error('name')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
|
||||
<input type="email" name="email" id="email" value="{{ old('email', $user->email) }}" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('email') border-red-500 @enderror">
|
||||
@error('email')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Ruolo -->
|
||||
<div>
|
||||
<label for="role" class="block text-sm font-medium text-gray-700">Ruolo</label>
|
||||
<select name="role" id="role" required
|
||||
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 @error('role') border-red-500 @enderror">
|
||||
<option value="">Seleziona un ruolo</option>
|
||||
@foreach($roles as $role)
|
||||
<option value="{{ $role->name }}"
|
||||
{{ old('role', $user->roles->first()?->name) == $role->name ? 'selected' : '' }}>
|
||||
{{ $role->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('role')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Info aggiuntive -->
|
||||
<div class="bg-gray-50 p-4 rounded-md">
|
||||
<h3 class="text-sm font-medium text-gray-700 mb-2">Informazioni Account</h3>
|
||||
<p class="text-sm text-gray-600">Creato il: {{ $user->created_at->format('d/m/Y H:i') }}</p>
|
||||
<p class="text-sm text-gray-600">Ultimo aggiornamento: {{ $user->updated_at->format('d/m/Y H:i') }}</p>
|
||||
@if($user->email_verified_at)
|
||||
<p class="text-sm text-green-600">Email verificata il: {{ $user->email_verified_at->format('d/m/Y H:i') }}</p>
|
||||
@else
|
||||
<p class="text-sm text-red-600">Email non verificata</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti -->
|
||||
<div class="flex items-center justify-end space-x-4">
|
||||
<a href="{{ route('superadmin.users.index') }}"
|
||||
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">
|
||||
Annulla
|
||||
</a>
|
||||
<button type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Aggiorna Utente
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
109
resources/views/superadmin/users/index.blade.php
Normal file
109
resources/views/superadmin/users/index.blade.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
@extends('superadmin.layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 bg-white border-b border-gray-200">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800">Gestione Utenti</h2>
|
||||
<a href="{{ route('superadmin.users.create') }}"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Nuovo Utente
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabella Utenti -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white border border-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
ID
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Nome
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Email
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Ruoli
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Data Creazione
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Azioni
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@forelse($users as $user)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $user->id }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{{ $user->name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $user->email }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
@foreach($user->roles as $role)
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 mr-1">
|
||||
{{ $role->name }}
|
||||
</span>
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ $user->created_at->format('d/m/Y H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ route('superadmin.users.edit', $user) }}"
|
||||
class="text-indigo-600 hover:text-indigo-900">Modifica</a>
|
||||
|
||||
<!-- Form per cambiare ruolo -->
|
||||
<form method="POST" action="{{ route('superadmin.users.updateRole', $user) }}" class="inline">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<select name="role" onchange="this.form.submit()" class="text-sm border-gray-300 rounded">
|
||||
<option value="">Cambia Ruolo</option>
|
||||
@foreach(\Spatie\Permission\Models\Role::all() as $role)
|
||||
<option value="{{ $role->name }}" {{ $user->hasRole($role->name) ? 'selected' : '' }}>
|
||||
{{ $role->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</form>
|
||||
|
||||
@if($user->id !== auth()->id())
|
||||
<a href="{{ route('superadmin.users.impersonate', $user) }}"
|
||||
class="text-green-600 hover:text-green-900">Impersona</a>
|
||||
|
||||
<form method="POST" action="{{ route('superadmin.users.destroy', $user) }}" class="inline"
|
||||
onsubmit="return confirm('Sei sicuro di voler eliminare questo utente?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900">Elimina</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-4 text-center text-gray-500">
|
||||
Nessun utente trovato
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginazione -->
|
||||
<div class="mt-6">
|
||||
{{ $users->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
181
routes/web.php
Normal file
181
routes/web.php
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Admin\DashboardController;
|
||||
use App\Http\Controllers\SuperAdmin\UserController as SuperAdminUserController;
|
||||
use App\Http\Controllers\SuperAdmin\CategoriaTicketController;
|
||||
use App\Http\Controllers\SuperAdmin\AmministratoreController as SuperAdminAmministratoreController;
|
||||
use App\Http\Controllers\Admin\StabileController;
|
||||
use App\Http\Controllers\Admin\SoggettoController;
|
||||
use App\Http\Controllers\Admin\UnitaImmobiliareController;
|
||||
use App\Http\Controllers\Admin\FornitoreController;
|
||||
use App\Http\Controllers\Admin\TicketController;
|
||||
use App\Http\Controllers\Admin\ContabilitaController;
|
||||
use App\Http\Controllers\Admin\DocumentoController;
|
||||
use App\Http\Controllers\Admin\PreventivoController;
|
||||
use App\Http\Controllers\Admin\BilancioController;
|
||||
use App\Http\Controllers\Condomino\DashboardController as CondominoDashboardController;
|
||||
use App\Http\Controllers\Condomino\TicketController as CondominoTicketController;
|
||||
use App\Http\Controllers\Condomino\DocumentoController as CondominoDocumentoController;
|
||||
use App\Http\Controllers\Condomino\UnitaController as CondominoUnitaController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\Admin\ImpostazioniController;
|
||||
use App\Http\Controllers\Admin\ApiTokenController;
|
||||
use App\Http\Controllers\Admin\RubricaController;
|
||||
|
||||
// --- Public Routes ---
|
||||
Route::get('/', function () { return view('welcome'); });
|
||||
|
||||
// --- Authenticated Routes ---
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
|
||||
// Generic Dashboard (redirects to the correct panel based on role)
|
||||
Route::get('/dashboard', function () { return view('dashboard'); })->name('dashboard');
|
||||
|
||||
// Profile Routes
|
||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
||||
|
||||
// --- SUPER-ADMIN PANEL ---
|
||||
Route::middleware(['role:super-admin'])->prefix('superadmin')->name('superadmin.')->group(function () {
|
||||
Route::get('/', function() {
|
||||
return view('superadmin.dashboard');
|
||||
})->name('dashboard');
|
||||
|
||||
// Gestione utenti
|
||||
Route::resource('users', SuperAdminUserController::class)->except(['show']);
|
||||
Route::patch('users/{user}/update-role', [SuperAdminUserController::class, 'updateRole'])->name('users.updateRole');
|
||||
Route::get('users/{user}/impersonate', [SuperAdminUserController::class, 'impersonate'])->name('users.impersonate');
|
||||
|
||||
// Gestione Amministratori
|
||||
Route::resource('amministratori', SuperAdminAmministratoreController::class)
|
||||
->except(['show'])
|
||||
->parameters(['amministratori' => 'amministratore']);
|
||||
|
||||
// Gestione Categorie Ticket
|
||||
Route::resource('categorie-ticket', CategoriaTicketController::class)->except(['show']);
|
||||
|
||||
// Diagnostica
|
||||
Route::get('/diagnostica', function() { return view('superadmin.diagnostica'); })->name('diagnostica');
|
||||
});
|
||||
|
||||
// --- ADMIN / AMMINISTRATORE PANEL ---
|
||||
Route::middleware(['role:admin|amministratore'])->prefix('admin')->name('admin.')->group(function () {
|
||||
// Dashboard dell'amministratore
|
||||
Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
|
||||
|
||||
// Rotte CRUD principali
|
||||
Route::resource('stabili', StabileController::class);
|
||||
Route::resource('stabili.unitaImmobiliari', UnitaImmobiliareController::class)->shallow();
|
||||
Route::resource('unitaImmobiliari', UnitaImmobiliareController::class)->only(['edit', 'update', 'destroy']);
|
||||
Route::resource('soggetti', SoggettoController::class);
|
||||
Route::resource('fornitori', FornitoreController::class);
|
||||
Route::resource('tickets', TicketController::class);
|
||||
|
||||
// Gestione Documenti
|
||||
Route::resource('documenti', DocumentoController::class)->except(['edit', 'update']);
|
||||
Route::get('documenti/{documento}/download', [DocumentoController::class, 'download'])->name('documenti.download');
|
||||
|
||||
// Gestione Preventivi
|
||||
Route::prefix('preventivi')->name('preventivi.')->group(function () {
|
||||
Route::get('/', [PreventivoController::class, 'index'])->name('index');
|
||||
Route::get('/create', [PreventivoController::class, 'create'])->name('create');
|
||||
Route::post('/', [PreventivoController::class, 'store'])->name('store');
|
||||
Route::get('/{preventivo}', [PreventivoController::class, 'show'])->name('show');
|
||||
Route::get('/{preventivo}/edit', [PreventivoController::class, 'edit'])->name('edit');
|
||||
Route::put('/{preventivo}', [PreventivoController::class, 'update'])->name('update');
|
||||
Route::post('/{preventivo}/approva', [PreventivoController::class, 'approva'])->name('approva');
|
||||
Route::post('/{preventivo}/genera-rate', [PreventivoController::class, 'generaRate'])->name('genera-rate');
|
||||
Route::get('/{preventivo}/storico', [PreventivoController::class, 'storicoModifiche'])->name('storico');
|
||||
Route::get('/pianificazione/dashboard', [PreventivoController::class, 'pianificazione'])->name('pianificazione');
|
||||
});
|
||||
|
||||
// Gestione Bilanci e Consuntivi
|
||||
Route::prefix('bilanci')->name('bilanci.')->group(function () {
|
||||
Route::get('/', [BilancioController::class, 'index'])->name('index');
|
||||
Route::get('/create', [BilancioController::class, 'create'])->name('create');
|
||||
Route::post('/', [BilancioController::class, 'store'])->name('store');
|
||||
Route::get('/{bilancio}', [BilancioController::class, 'show'])->name('show');
|
||||
Route::get('/{bilancio}/edit', [BilancioController::class, 'edit'])->name('edit');
|
||||
Route::put('/{bilancio}', [BilancioController::class, 'update'])->name('update');
|
||||
Route::post('/{bilancio}/calcola-conguagli', [BilancioController::class, 'calcolaConguagli'])->name('calcola-conguagli');
|
||||
Route::post('/{bilancio}/genera-rate-conguaglio', [BilancioController::class, 'generaRateConguaglio'])->name('genera-rate-conguaglio');
|
||||
Route::post('/{bilancio}/quadratura', [BilancioController::class, 'quadratura'])->name('quadratura');
|
||||
Route::post('/{bilancio}/chiusura-esercizio', [BilancioController::class, 'chiusuraEsercizio'])->name('chiusura-esercizio');
|
||||
Route::get('/{bilancio}/storico', [BilancioController::class, 'storicoModifiche'])->name('storico');
|
||||
Route::get('/quadrature/dashboard', [BilancioController::class, 'quadratureDashboard'])->name('quadrature');
|
||||
Route::get('/conguagli/dashboard', [BilancioController::class, 'conguagliDashboard'])->name('conguagli');
|
||||
Route::get('/rimborsi/dashboard', [BilancioController::class, 'rimborsiDashboard'])->name('rimborsi');
|
||||
Route::get('/automazioni/dashboard', [BilancioController::class, 'automazioniDashboard'])->name('automazioni');
|
||||
});
|
||||
|
||||
// Contabilità
|
||||
Route::prefix('contabilita')->name('contabilita.')->group(function () {
|
||||
Route::get('/', [ContabilitaController::class, 'index'])->name('index');
|
||||
Route::get('/movimenti', [ContabilitaController::class, 'movimenti'])->name('movimenti');
|
||||
Route::get('/registrazione', [ContabilitaController::class, 'registrazione'])->name('registrazione');
|
||||
Route::post('/registrazione', [ContabilitaController::class, 'storeRegistrazione'])->name('store-registrazione');
|
||||
Route::get('/import-xml', [ContabilitaController::class, 'importXml'])->name('import-xml');
|
||||
Route::post('/import-xml', [ContabilitaController::class, 'importXml'])->name('import-xml.store');
|
||||
});
|
||||
|
||||
// Impostazioni e API Tokens
|
||||
Route::get('impostazioni', [ImpostazioniController::class, 'index'])->name('impostazioni.index');
|
||||
Route::post('impostazioni', [ImpostazioniController::class, 'store'])->name('impostazioni.store');
|
||||
Route::get('api-tokens', [ApiTokenController::class, 'index'])->name('api-tokens.index');
|
||||
Route::post('api-tokens', [ApiTokenController::class, 'store'])->name('api-tokens.store');
|
||||
Route::delete('api-tokens/{token_id}', [ApiTokenController::class, 'destroy'])->name('api-tokens.destroy');
|
||||
|
||||
// Rubrica
|
||||
Route::get('rubrica', [RubricaController::class, 'index'])->name('rubrica.index');
|
||||
});
|
||||
|
||||
// --- CONDOMINO PANEL ---
|
||||
Route::middleware(['role:condomino'])->prefix('condomino')->name('condomino.')->group(function () {
|
||||
// Dashboard
|
||||
Route::get('/', [CondominoDashboardController::class, 'index'])->name('dashboard');
|
||||
|
||||
// Tickets
|
||||
Route::resource('tickets', CondominoTicketController::class)->only(['index', 'create', 'store', 'show']);
|
||||
|
||||
// Documenti
|
||||
Route::get('/documenti', [CondominoDocumentoController::class, 'index'])->name('documenti.index');
|
||||
Route::get('/documenti/{documento}/download', [CondominoDocumentoController::class, 'download'])->name('documenti.download');
|
||||
|
||||
// Unità Immobiliari
|
||||
Route::get('/unita', [CondominoUnitaController::class, 'index'])->name('unita.index');
|
||||
Route::get('/unita/{unitaImmobiliare}', [CondominoUnitaController::class, 'show'])->name('unita.show');
|
||||
Route::post('/unita/{unitaImmobiliare}/richiesta-modifica', [CondominoUnitaController::class, 'richiestaModifica'])->name('unita.richiesta-modifica');
|
||||
|
||||
// Pagamenti (placeholder)
|
||||
Route::view('/pagamenti', 'condomino.pagamenti.index')->name('pagamenti.index');
|
||||
|
||||
// Altre viste placeholder
|
||||
Route::view('/scadenze', 'condomino.scadenze')->name('scadenze');
|
||||
Route::view('/comunicazioni', 'condomino.comunicazioni')->name('comunicazioni');
|
||||
Route::view('/avvisi', 'condomino.avvisi')->name('avvisi');
|
||||
Route::view('/guasti', 'condomino.guasti')->name('guasti');
|
||||
Route::view('/contabilita', 'condomino.contabilita')->name('contabilita');
|
||||
Route::view('/fornitori', 'condomino.fornitori')->name('fornitori');
|
||||
Route::view('/bacheca', 'condomino.bacheca')->name('bacheca');
|
||||
Route::view('/sondaggi', 'condomino.sondaggi')->name('sondaggi');
|
||||
});
|
||||
|
||||
// --- DEBUG ROUTE FOR PERMISSIONS ---
|
||||
Route::get('/test-permissions', function() {
|
||||
$user = Auth::user();
|
||||
echo "<h1>Diagnostica Permessi per: " . $user->name . "</h1>";
|
||||
echo "<h2>Ruoli Assegnati:</h2>";
|
||||
echo "<ul>";
|
||||
foreach ($user->getRoleNames() as $role) {
|
||||
echo "<li>" . $role . "</li>";
|
||||
}
|
||||
echo "</ul>";
|
||||
});
|
||||
});
|
||||
|
||||
// --- PUBLIC ROUTE TO LEAVE IMPERSONATION ---
|
||||
Route::get('impersonate/leave', [\Lab404\Impersonate\Controllers\ImpersonateController::class, 'leave'])->name('impersonate.leave');
|
||||
|
||||
// --- AUTHENTICATION ROUTES ---
|
||||
require __DIR__.'/auth.php';
|
||||
Loading…
Reference in New Issue
Block a user