netgescon-master/netgescon-laravel/app/Models/MovimentoPartitaDoppia.php
Michele Windows e68ee85a18 🚀 CHECKPOINT STABILE - Sistema Contabile Avanzato
📋 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
2025-07-26 15:11:19 +02:00

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,
]);
}
}