📋 AGGIUNTE PRINCIPALI: - Sistema contabile partita doppia con gestioni multiple - Documentazione implementazione completa - Models Laravel: GestioneContabile, MovimentoPartitaDoppia - Controller ContabilitaAvanzataController - Migration sistema contabile completo - Scripts automazione e trasferimento - Manuali utente e checklist implementazione 📊 FILES PRINCIPALI: - docs/10-IMPLEMENTAZIONE-CONTABILITA-PARTITA-DOPPIA-GESTIONI.md - SPECIFICHE-SISTEMA-CONTABILE-COMPLETO.md - netgescon-laravel/database/migrations/2025_07_20_100000_create_complete_accounting_system.php - netgescon-laravel/app/Models/GestioneContabile.php ✅ CHECKPOINT SICURO PER ROLLBACK
241 lines
6.5 KiB
PHP
241 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
|
|
/**
|
|
* Movimento Contabile con Partita Doppia
|
|
* Ogni movimento ha righe di dare e avere bilanciate
|
|
*/
|
|
class MovimentoPartitaDoppia extends Model
|
|
{
|
|
use HasFactory, SoftDeletes;
|
|
|
|
protected $table = 'movimenti_partita_doppia';
|
|
|
|
protected $fillable = [
|
|
'codice_movimento',
|
|
'stabile_id',
|
|
'gestione_contabile_id',
|
|
'esercizio_contabile_id',
|
|
'data_movimento',
|
|
'data_registrazione',
|
|
'descrizione',
|
|
'causale_dettagliata',
|
|
'note_interne',
|
|
'tipo_documento',
|
|
'numero_documento',
|
|
'data_documento',
|
|
'fornitore_id',
|
|
'documento_id',
|
|
'numero_protocollo',
|
|
'progressivo_anno',
|
|
'stato_movimento',
|
|
'tipologia_registrazione',
|
|
'importo_lordo',
|
|
'importo_iva',
|
|
'importo_ritenute',
|
|
'importo_netto',
|
|
'dettagli_fiscali',
|
|
'ripartito',
|
|
'ripartizione_millesimale',
|
|
'tabella_millesimale_utilizzata',
|
|
'creato_da',
|
|
'verificato_da',
|
|
'confermato_da',
|
|
'data_verifica',
|
|
'data_conferma',
|
|
];
|
|
|
|
protected $casts = [
|
|
'data_movimento' => 'date',
|
|
'data_registrazione' => 'date',
|
|
'data_documento' => 'date',
|
|
'data_verifica' => 'datetime',
|
|
'data_conferma' => 'datetime',
|
|
'importo_lordo' => 'decimal:2',
|
|
'importo_iva' => 'decimal:2',
|
|
'importo_ritenute' => 'decimal:2',
|
|
'importo_netto' => 'decimal:2',
|
|
'dettagli_fiscali' => 'json',
|
|
'ripartito' => 'boolean',
|
|
'ripartizione_millesimale' => 'json',
|
|
];
|
|
|
|
protected static function boot()
|
|
{
|
|
parent::boot();
|
|
|
|
static::creating(function ($model) {
|
|
if (!$model->codice_movimento) {
|
|
$model->codice_movimento = static::generateCodiceMovimento();
|
|
}
|
|
|
|
if (!$model->progressivo_anno) {
|
|
$model->progressivo_anno = static::getNextProgressivo($model->stabile_id);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Genera codice movimento univoco
|
|
*/
|
|
public static function generateCodiceMovimento(): string
|
|
{
|
|
do {
|
|
$codice = 'MOV' . sprintf('%09d', rand(100000000, 999999999));
|
|
} while (static::where('codice_movimento', $codice)->exists());
|
|
|
|
return $codice;
|
|
}
|
|
|
|
/**
|
|
* Ottiene il prossimo progressivo per l'anno
|
|
*/
|
|
public static function getNextProgressivo($stabile_id): int
|
|
{
|
|
$anno = date('Y');
|
|
$ultimo = static::where('stabile_id', $stabile_id)
|
|
->whereYear('data_registrazione', $anno)
|
|
->max('progressivo_anno');
|
|
|
|
return ($ultimo ?? 0) + 1;
|
|
}
|
|
|
|
/**
|
|
* Relazioni
|
|
*/
|
|
public function stabile(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Stabile::class);
|
|
}
|
|
|
|
public function gestioneContabile(): BelongsTo
|
|
{
|
|
return $this->belongsTo(GestioneContabile::class, 'gestione_contabile_id');
|
|
}
|
|
|
|
public function esercizioContabile(): BelongsTo
|
|
{
|
|
return $this->belongsTo(EsercizioContabile::class, 'esercizio_contabile_id');
|
|
}
|
|
|
|
public function fornitore(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Fornitore::class);
|
|
}
|
|
|
|
public function righeContabili(): HasMany
|
|
{
|
|
return $this->hasMany(RigaContabile::class, 'movimento_id');
|
|
}
|
|
|
|
public function creatoBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'creato_da');
|
|
}
|
|
|
|
public function verificatoBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'verificato_da');
|
|
}
|
|
|
|
public function confermatoBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'confermato_da');
|
|
}
|
|
|
|
/**
|
|
* Scopes
|
|
*/
|
|
public function scopeConfermati($query)
|
|
{
|
|
return $query->where('stato_movimento', 'confermato');
|
|
}
|
|
|
|
public function scopeByGestione($query, $gestione_id)
|
|
{
|
|
return $query->where('gestione_contabile_id', $gestione_id);
|
|
}
|
|
|
|
public function scopeByPeriodo($query, $data_inizio, $data_fine)
|
|
{
|
|
return $query->whereBetween('data_movimento', [$data_inizio, $data_fine]);
|
|
}
|
|
|
|
/**
|
|
* Metodi di business logic
|
|
*/
|
|
public function verificaQuadratura(): bool
|
|
{
|
|
$totaleDare = $this->righeContabili()->where('dare_avere', 'dare')->sum('importo');
|
|
$totaleAvere = $this->righeContabili()->where('dare_avere', 'avere')->sum('importo');
|
|
|
|
return abs($totaleDare - $totaleAvere) < 0.01; // Tolleranza centesimi
|
|
}
|
|
|
|
public function confermaMovimento($user_id): bool
|
|
{
|
|
if (!$this->verificaQuadratura()) {
|
|
return false;
|
|
}
|
|
|
|
$this->update([
|
|
'stato_movimento' => 'confermato',
|
|
'confermato_da' => $user_id,
|
|
'data_conferma' => now(),
|
|
]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function ripartisciSuMillesimi($tabella_id): void
|
|
{
|
|
// Implementare logica di ripartizione automatica
|
|
$tabella = TabellaMillesimale::find($tabella_id);
|
|
$dettagli = $tabella->dettagli;
|
|
|
|
$ripartizione = [];
|
|
foreach ($dettagli as $dettaglio) {
|
|
$quota = ($this->importo_netto * $dettaglio->millesimi) / 1000;
|
|
$ripartizione[$dettaglio->unita_immobiliare_id] = [
|
|
'millesimi' => $dettaglio->millesimi,
|
|
'importo' => $quota,
|
|
];
|
|
}
|
|
|
|
$this->update([
|
|
'ripartito' => true,
|
|
'ripartizione_millesimale' => $ripartizione,
|
|
'tabella_millesimale_utilizzata' => $tabella_id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Crea automaticamente le righe contabili standard
|
|
*/
|
|
public function creaRigheStandard($conto_dare, $conto_avere): void
|
|
{
|
|
// Riga in DARE
|
|
$this->righeContabili()->create([
|
|
'codice_conto' => $conto_dare,
|
|
'descrizione_riga' => $this->descrizione,
|
|
'dare_avere' => 'dare',
|
|
'importo' => $this->importo_netto,
|
|
]);
|
|
|
|
// Riga in AVERE
|
|
$this->righeContabili()->create([
|
|
'codice_conto' => $conto_avere,
|
|
'descrizione_riga' => $this->descrizione,
|
|
'dare_avere' => 'avere',
|
|
'importo' => $this->importo_netto,
|
|
]);
|
|
}
|
|
}
|