netgescon-day0/resources/views/modules/contabilita/create.blade.php

842 lines
32 KiB
PHP

@extends('admin.layouts.netgescon')
@php
$moduleTitle = 'Nuova Registrazione Contabile';
$moduleIcon = 'fas fa-plus-circle';
$moduleBreadcrumb = 'Moduli > Contabilità > Nuova Registrazione';
@endphp
@section('title', $moduleTitle)
@section('content')
@extends('admin.layouts.netgescon')
@section('title', 'Nuova Registrazione Contabile')
@section('content')
<div class="content-wrapper">
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">Nuova Registrazione Contabile</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('admin.contabilita.index') }}">Contabilità</a></li>
<li class="breadcrumb-item active">Nuova Registrazione</li>
</ol>
</div>
</div>
</div>
</div>
<!-- Sub-menu tabs come nella pagina stabili -->
<div class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card card-primary card-tabs">
<div class="card-header p-0 pt-1">
<ul class="nav nav-tabs" id="custom-tabs-one-tab" role="tablist">
<li class="nav-item">
<a class="nav-link"
href="{{ route('admin.contabilita.index') }}"
role="tab">
<i class="fas fa-list"></i> Registrazioni
</a>
</li>
<li class="nav-item">
<a class="nav-link active"
href="{{ route('admin.contabilita.create') }}"
role="tab">
<i class="fas fa-plus"></i> Nuova Registrazione
</a>
</li>
<li class="nav-item">
<a class="nav-link"
href="#bilanci"
role="tab"
onclick="alert('Funzione in sviluppo')">
<i class="fas fa-balance-scale"></i> Bilanci
</a>
</li>
<li class="nav-item">
<a class="nav-link"
href="#reports"
role="tab"
onclick="alert('Funzione in sviluppo')">
<i class="fas fa-chart-bar"></i> Reports
</a>
</li>
<li class="nav-item">
<a class="nav-link"
href="#piano-conti"
role="tab"
onclick="alert('Funzione in sviluppo')">
<i class="fas fa-list-alt"></i> Piano dei Conti
</a>
</li>
<li class="nav-item">
<a class="nav-link"
href="#config"
role="tab"
onclick="alert('Funzione in sviluppo')">
<i class="fas fa-cog"></i> Configurazione
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<form id="contabilita-form" method="POST" action="{{ route('admin.contabilita.store') }}">
@csrf
<!-- Dati Generali - Design System NetGescon -->
<div class="netgescon-card mb-4">
<div class="netgescon-card-header">
<div class="netgescon-card-title">
<i class="fas fa-file-invoice me-2"></i>Dati Generali della Registrazione
</div>
</div>
<div class="netgescon-card-body">
<div class="row g-4">
<div class="col-md-6">
<label for="entry_date" class="netgescon-form-label required">
<i class="fas fa-calendar me-1 text-primary"></i>
Data Registrazione
</label>
<input type="date"
class="netgescon-form-control @error('entry_date') is-invalid @enderror"
id="entry_date"
name="entry_date"
value="{{ old('entry_date', date('Y-m-d')) }}"
required>
@error('entry_date')
<div class="netgescon-invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="gestione_id" class="netgescon-form-label required">
<i class="fas fa-building me-1 text-primary"></i>
Gestione
</label>
<select class="netgescon-form-select @error('gestione_id') is-invalid @enderror"
id="gestione_id"
name="gestione_id"
required>
<option value="">Seleziona una gestione</option>
@foreach($gestioni as $gestione)
<option value="{{ $gestione->id_gestione }}"
{{ old('gestione_id') == $gestione->id_gestione ? 'selected' : '' }}>
{{ $gestione->anno_gestione }} - {{ $gestione->tipo_gestione }}
</option>
@endforeach
</select>
@error('gestione_id')
<div class="netgescon-invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="document_type" class="netgescon-form-label">
<i class="fas fa-tag me-1 text-primary"></i>
Tipo Documento
</label>
<select class="netgescon-form-select @error('document_type') is-invalid @enderror"
id="document_type"
name="document_type">
<option value="REG" {{ old('document_type', 'REG') == 'REG' ? 'selected' : '' }}>Registrazione Generale</option>
<option value="FAT" {{ old('document_type') == 'FAT' ? 'selected' : '' }}>Fattura</option>
<option value="RIC" {{ old('document_type') == 'RIC' ? 'selected' : '' }}>Ricevuta</option>
<option value="BON" {{ old('document_type') == 'BON' ? 'selected' : '' }}>Bonifico</option>
<option value="GIR" {{ old('document_type') == 'GIR' ? 'selected' : '' }}>Giroconto</option>
</select>
@error('document_type')
<div class="netgescon-invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="document_number" class="netgescon-form-label">
<i class="fas fa-hashtag me-1 text-primary"></i>
Numero Documento
</label>
<input type="text"
class="netgescon-form-control @error('document_number') is-invalid @enderror"
id="document_number"
name="document_number"
value="{{ old('document_number') }}"
placeholder="Numero del documento">
@error('document_number')
<div class="netgescon-invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-12">
<label for="description" class="netgescon-form-label required">
<i class="fas fa-comment me-1 text-primary"></i>
Descrizione
</label>
<textarea class="netgescon-form-control @error('description') is-invalid @enderror"
id="description"
name="description"
rows="3"
required
placeholder="Descrizione dettagliata della registrazione contabile">{{ old('description') }}</textarea>
@error('description')
<div class="netgescon-invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
</div>
<!-- Movimenti Contabili - Design System NetGescon -->
<div class="netgescon-card mb-4">
<div class="netgescon-card-header">
<div class="netgescon-card-title">
<i class="fas fa-exchange-alt me-2"></i>Movimenti Contabili (Partita Doppia)
</div>
<div class="netgescon-card-subtitle">
Il saldo DARE deve essere uguale al saldo AVERE
</div>
</div>
<div class="netgescon-card-body">
<!-- Riepilogo Saldi -->
<div class="netgescon-balance-summary mb-4">
<div class="row g-3">
<div class="col-md-4">
<div class="netgescon-balance-card netgescon-balance-dare">
<div class="netgescon-balance-label">
<i class="fas fa-arrow-up me-1"></i>
Totale DARE
</div>
<div class="netgescon-balance-amount" id="total-dare"> 0,00</div>
</div>
</div>
<div class="col-md-4">
<div class="netgescon-balance-card netgescon-balance-avere">
<div class="netgescon-balance-label">
<i class="fas fa-arrow-down me-1"></i>
Totale AVERE
</div>
<div class="netgescon-balance-amount" id="total-avere"> 0,00</div>
</div>
</div>
<div class="col-md-4">
<div class="netgescon-balance-card netgescon-balance-difference" id="balance-status">
<div class="netgescon-balance-label">
<i class="fas fa-balance-scale me-1"></i>
Differenza
</div>
<div class="netgescon-balance-amount" id="balance-difference"> 0,00</div>
</div>
</div>
</div>
</div>
<!-- Movimenti -->
<div id="movements-container">
<!-- Movimento 1 -->
<div class="netgescon-movement-row" data-movement="1">
<div class="netgescon-movement-header">
<span class="netgescon-movement-number">Movimento #1</span>
<button type="button" class="netgescon-btn-remove" onclick="removeMovement(1)" style="display: none;">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="row g-3">
<div class="col-md-6">
<label class="netgescon-form-label required">
<i class="fas fa-list me-1 text-primary"></i>
Conto
</label>
<select class="netgescon-form-select piano-conti-select"
name="movements[1][conto_id]"
required>
<option value="">Seleziona un conto</option>
@foreach($pianoConti as $conto)
<option value="{{ $conto->id }}">
{{ $conto->mastro }}.{{ $conto->conto }}.{{ $conto->sottoconto }} - {{ $conto->descrizione }}
</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<label class="netgescon-form-label">
<i class="fas fa-arrow-up me-1 text-success"></i>
DARE
</label>
<input type="number"
class="netgescon-form-control dare-amount"
name="movements[1][dare]"
step="0.01"
min="0"
placeholder="0,00"
onchange="calculateBalance()">
</div>
<div class="col-md-2">
<label class="netgescon-form-label">
<i class="fas fa-arrow-down me-1 text-danger"></i>
AVERE
</label>
<input type="number"
class="netgescon-form-control avere-amount"
name="movements[1][avere]"
step="0.01"
min="0"
placeholder="0,00"
onchange="calculateBalance()">
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="button"
class="netgescon-btn netgescon-btn-outline w-100"
onclick="clearMovement(1)">
<i class="fas fa-eraser"></i>
</button>
</div>
</div>
</div>
<!-- Movimento 2 -->
<div class="netgescon-movement-row" data-movement="2">
<div class="netgescon-movement-header">
<span class="netgescon-movement-number">Movimento #2</span>
<button type="button" class="netgescon-btn-remove" onclick="removeMovement(2)" style="display: none;">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="row g-3">
<div class="col-md-6">
<label class="netgescon-form-label required">
<i class="fas fa-list me-1 text-primary"></i>
Conto
</label>
<select class="netgescon-form-select piano-conti-select"
name="movements[2][conto_id]"
required>
<option value="">Seleziona un conto</option>
@foreach($pianoConti as $conto)
<option value="{{ $conto->id }}">
{{ $conto->mastro }}.{{ $conto->conto }}.{{ $conto->sottoconto }} - {{ $conto->descrizione }}
</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<label class="netgescon-form-label">
<i class="fas fa-arrow-up me-1 text-success"></i>
DARE
</label>
<input type="number"
class="netgescon-form-control dare-amount"
name="movements[2][dare]"
step="0.01"
min="0"
placeholder="0,00"
onchange="calculateBalance()">
</div>
<div class="col-md-2">
<label class="netgescon-form-label">
<i class="fas fa-arrow-down me-1 text-danger"></i>
AVERE
</label>
<input type="number"
class="netgescon-form-control avere-amount"
name="movements[2][avere]"
step="0.01"
min="0"
placeholder="0,00"
onchange="calculateBalance()">
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="button"
class="netgescon-btn netgescon-btn-outline w-100"
onclick="clearMovement(2)">
<i class="fas fa-eraser"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Aggiungi Movimento -->
<div class="text-center mt-4">
<button type="button"
class="netgescon-btn netgescon-btn-outline"
onclick="addMovement()">
<i class="fas fa-plus me-2"></i>
Aggiungi Movimento
</button>
</div>
</div>
</div>
<!-- Azioni Form -->
<div class="netgescon-card">
<div class="netgescon-card-body">
<div class="row g-3">
<div class="col-md-6">
<div class="netgescon-form-check">
<input type="checkbox"
class="netgescon-form-check-input"
id="status_confirmed"
name="status"
value="confirmed"
{{ old('status') == 'confirmed' ? 'checked' : '' }}>
<label class="netgescon-form-check-label" for="status_confirmed">
<i class="fas fa-check-circle me-1 text-success"></i>
Conferma immediatamente la registrazione
</label>
</div>
<small class="netgescon-form-text">
Se non spuntato, la registrazione sarà salvata come bozza
</small>
</div>
<div class="col-md-6 text-end">
<div class="btn-group">
<button type="submit"
class="netgescon-btn netgescon-btn-success netgescon-btn-lg"
id="submit-btn"
disabled>
<i class="fas fa-save me-2"></i>
Salva Registrazione
</button>
<a href="{{ route('admin.contabilita.index') }}"
class="netgescon-btn netgescon-btn-outline">
<i class="fas fa-times me-1"></i>
Annulla
</a>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
@endsection
@push('styles')
<style>
/* Design System NetGescon - Contabilità Create Form */
/* Required Field Indicator */
.required::after {
content: " *";
color: var(--bs-danger);
font-weight: bold;
}
/* Balance Summary */
.netgescon-balance-summary {
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
border-radius: 12px;
padding: 1.5rem;
border: 2px solid #e2e8f0;
}
.netgescon-balance-card {
background: white;
border-radius: 10px;
padding: 1.25rem;
text-align: center;
border: 2px solid;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.netgescon-balance-dare {
border-color: var(--bs-success);
}
.netgescon-balance-avere {
border-color: var(--bs-danger);
}
.netgescon-balance-difference {
border-color: var(--bs-warning);
}
.netgescon-balance-difference.balanced {
border-color: var(--bs-success);
background: rgba(22, 163, 74, 0.05);
}
.netgescon-balance-difference.unbalanced {
border-color: var(--bs-danger);
background: rgba(239, 68, 68, 0.05);
}
.netgescon-balance-label {
font-size: 0.875rem;
font-weight: 600;
color: var(--bs-secondary);
margin-bottom: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.netgescon-balance-amount {
font-size: 1.5rem;
font-weight: 700;
color: var(--bs-dark);
}
/* Movement Rows */
.netgescon-movement-row {
background: white;
border: 2px solid #e2e8f0;
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1rem;
position: relative;
transition: all 0.3s ease;
}
.netgescon-movement-row:hover {
border-color: var(--bs-primary);
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.1);
}
.netgescon-movement-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid #e2e8f0;
}
.netgescon-movement-number {
font-weight: 600;
color: var(--bs-primary);
font-size: 1.125rem;
}
.netgescon-btn-remove {
background: rgba(239, 68, 68, 0.1);
color: var(--bs-danger);
border: none;
border-radius: 8px;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.netgescon-btn-remove:hover {
background: var(--bs-danger);
color: white;
transform: scale(1.1);
}
/* Form Validation */
.netgescon-invalid-feedback {
display: block;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875rem;
color: var(--bs-danger);
font-weight: 500;
}
.is-invalid {
border-color: var(--bs-danger) !important;
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important;
}
/* Form Check */
.netgescon-form-check {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem;
background: #f8fafc;
border-radius: 8px;
border: 2px solid #e2e8f0;
transition: all 0.3s ease;
}
.netgescon-form-check:hover {
border-color: var(--bs-primary);
}
.netgescon-form-check-input {
width: 20px;
height: 20px;
border: 2px solid #e2e8f0;
border-radius: 4px;
transition: all 0.3s ease;
}
.netgescon-form-check-input:checked {
background-color: var(--bs-success);
border-color: var(--bs-success);
}
.netgescon-form-check-label {
margin: 0;
font-weight: 500;
color: var(--bs-dark);
cursor: pointer;
display: flex;
align-items: center;
}
.netgescon-form-text {
color: var(--bs-secondary);
font-style: italic;
margin-top: 0.25rem;
}
/* Success Button */
.netgescon-btn-success {
background: linear-gradient(135deg, var(--bs-success) 0%, #16a34a 100%);
color: white;
border: none;
}
.netgescon-btn-success:hover {
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(22, 163, 74, 0.3);
color: white;
}
.netgescon-btn-success:disabled {
background: #9ca3af;
color: white;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* Large Button */
.netgescon-btn-lg {
padding: 1rem 2rem;
font-size: 1.125rem;
font-weight: 600;
}
/* Animations */
@keyframes balanceUpdate {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.balance-updated {
animation: balanceUpdate 0.3s ease;
}
/* Responsive Design */
@media (max-width: 768px) {
.netgescon-balance-summary .row {
gap: 1rem;
}
.netgescon-movement-row {
padding: 1rem;
}
.netgescon-btn-lg {
padding: 0.75rem 1.5rem;
font-size: 1rem;
}
}
</style>
@endpush
@push('scripts')
<script>
let movementCounter = 2;
function calculateBalance() {
let totalDare = 0;
let totalAvere = 0;
// Calcola tutti i movimenti DARE
document.querySelectorAll('.dare-amount').forEach(input => {
const value = parseFloat(input.value) || 0;
totalDare += value;
});
// Calcola tutti i movimenti AVERE
document.querySelectorAll('.avere-amount').forEach(input => {
const value = parseFloat(input.value) || 0;
totalAvere += value;
});
// Aggiorna i totali
document.getElementById('total-dare').textContent = '€ ' + totalDare.toLocaleString('it-IT', {minimumFractionDigits: 2});
document.getElementById('total-avere').textContent = '€ ' + totalAvere.toLocaleString('it-IT', {minimumFractionDigits: 2});
// Calcola la differenza
const difference = Math.abs(totalDare - totalAvere);
document.getElementById('balance-difference').textContent = '€ ' + difference.toLocaleString('it-IT', {minimumFractionDigits: 2});
// Aggiorna lo stato del bilanciamento
const balanceStatus = document.getElementById('balance-status');
const submitBtn = document.getElementById('submit-btn');
if (difference === 0 && totalDare > 0 && totalAvere > 0) {
balanceStatus.className = 'netgescon-balance-card netgescon-balance-difference balanced';
submitBtn.disabled = false;
} else {
balanceStatus.className = 'netgescon-balance-card netgescon-balance-difference unbalanced';
submitBtn.disabled = true;
}
// Animazione di aggiornamento
balanceStatus.classList.add('balance-updated');
setTimeout(() => {
balanceStatus.classList.remove('balance-updated');
}, 300);
}
function addMovement() {
movementCounter++;
const container = document.getElementById('movements-container');
const movementHtml = `
<div class="netgescon-movement-row" data-movement="${movementCounter}">
<div class="netgescon-movement-header">
<span class="netgescon-movement-number">Movimento #${movementCounter}</span>
<button type="button" class="netgescon-btn-remove" onclick="removeMovement(${movementCounter})">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="row g-3">
<div class="col-md-6">
<label class="netgescon-form-label required">
<i class="fas fa-list me-1 text-primary"></i>
Conto
</label>
<select class="netgescon-form-select piano-conti-select"
name="movements[${movementCounter}][conto_id]"
required>
<option value="">Seleziona un conto</option>
@foreach($pianoConti as $conto)
<option value="{{ $conto->id }}">
{{ $conto->mastro }}.{{ $conto->conto }}.{{ $conto->sottoconto }} - {{ $conto->descrizione }}
</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<label class="netgescon-form-label">
<i class="fas fa-arrow-up me-1 text-success"></i>
DARE
</label>
<input type="number"
class="netgescon-form-control dare-amount"
name="movements[${movementCounter}][dare]"
step="0.01"
min="0"
placeholder="0,00"
onchange="calculateBalance()">
</div>
<div class="col-md-2">
<label class="netgescon-form-label">
<i class="fas fa-arrow-down me-1 text-danger"></i>
AVERE
</label>
<input type="number"
class="netgescon-form-control avere-amount"
name="movements[${movementCounter}][avere]"
step="0.01"
min="0"
placeholder="0,00"
onchange="calculateBalance()">
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="button"
class="netgescon-btn netgescon-btn-outline w-100"
onclick="clearMovement(${movementCounter})">
<i class="fas fa-eraser"></i>
</button>
</div>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', movementHtml);
updateRemoveButtons();
}
function removeMovement(movementId) {
const movement = document.querySelector(`[data-movement="${movementId}"]`);
if (movement) {
movement.remove();
calculateBalance();
updateRemoveButtons();
}
}
function clearMovement(movementId) {
const movement = document.querySelector(`[data-movement="${movementId}"]`);
if (movement) {
movement.querySelector('.piano-conti-select').selectedIndex = 0;
movement.querySelector('.dare-amount').value = '';
movement.querySelector('.avere-amount').value = '';
calculateBalance();
}
}
function updateRemoveButtons() {
const movements = document.querySelectorAll('.netgescon-movement-row');
movements.forEach((movement, index) => {
const removeBtn = movement.querySelector('.netgescon-btn-remove');
if (movements.length > 2) {
removeBtn.style.display = 'flex';
} else {
removeBtn.style.display = 'none';
}
});
}
function clearForm() {
if (confirm('Sei sicuro di voler pulire tutti i campi del form?')) {
document.getElementById('contabilita-form').reset();
calculateBalance();
}
}
// Validazione DARE/AVERE - un solo campo per movimento può essere valorizzato
document.addEventListener('change', function(e) {
if (e.target.classList.contains('dare-amount')) {
const movementRow = e.target.closest('.netgescon-movement-row');
const avereInput = movementRow.querySelector('.avere-amount');
if (e.target.value && parseFloat(e.target.value) > 0) {
avereInput.value = '';
}
calculateBalance();
}
if (e.target.classList.contains('avere-amount')) {
const movementRow = e.target.closest('.netgescon-movement-row');
const dareInput = movementRow.querySelector('.dare-amount');
if (e.target.value && parseFloat(e.target.value) > 0) {
dareInput.value = '';
}
calculateBalance();
}
});
// Inizializza la pagina
document.addEventListener('DOMContentLoaded', function() {
calculateBalance();
updateRemoveButtons();
});
</script>
@endpush