netgescon-day0/app/Modules/Contabilita/Services/MovimentoBancaPrimaNotaService.php

155 lines
5.7 KiB
PHP

<?php
namespace App\Modules\Contabilita\Services;
use App\Models\User;
use App\Models\GestioneContabile;
use App\Modules\Contabilita\Models\Movimento;
use App\Modules\Contabilita\Models\MovimentoBanca;
use App\Modules\Contabilita\Models\RegolaPrimaNota;
use App\Modules\Contabilita\Models\Registrazione;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use RuntimeException;
class MovimentoBancaPrimaNotaService
{
/**
* Genera una registrazione in prima nota per un movimento bancario.
* - Se importo > 0: usa (dare=conto_dare_id, avere=conto_avere_id)
* - Se importo < 0: inverte automaticamente dare/avere (perché banca esce)
*/
public function generaRegistrazioneDaMovimento(User $user, MovimentoBanca $movimento): Registrazione
{
if (! Schema::hasTable('contabilita_registrazioni') || ! Schema::hasTable('contabilita_movimenti')) {
throw new RuntimeException('Tabelle prima nota mancanti.');
}
if ($movimento->registrazione_id) {
$existing = Registrazione::query()->find((int) $movimento->registrazione_id);
if ($existing) {
return $existing;
}
}
$stabileId = (int) ($movimento->stabile_id ?? 0);
if ($stabileId <= 0) {
throw new RuntimeException('Movimento banca senza stabile_id.');
}
$gestioneId = (int) ($movimento->gestione_id ?? 0);
if ($gestioneId <= 0) {
throw new RuntimeException('Seleziona una gestione per questo movimento banca.');
}
if (Schema::hasTable('contabilita_chiusure_gestione')) {
$chiusa = app(ChiusuraGestioneService::class)->isGestioneChiusa($gestioneId);
if ($chiusa) {
throw new RuntimeException('Gestione chiusa: non è possibile generare nuove registrazioni.');
}
}
$causale = is_string($movimento->causale) ? trim((string) $movimento->causale) : '';
$chiave = $causale !== '' ? $causale : 'default';
$regola = RegolaPrimaNota::query()
->where('stabile_id', $stabileId)
->where('origine', 'banca')
->where('chiave', $chiave)
->where('attiva', true)
->first();
if (! $regola) {
$regola = RegolaPrimaNota::query()
->where('stabile_id', $stabileId)
->where('origine', 'banca')
->where('chiave', 'default')
->where('attiva', true)
->first();
}
if (! $regola || ! $regola->conto_dare_id || ! $regola->conto_avere_id) {
throw new RuntimeException('Regola prima nota banca non configurata (origine=banca, chiave=' . $chiave . ' o default).');
}
$importo = (float) ($movimento->importo ?? 0);
if (abs($importo) < 0.005) {
throw new RuntimeException('Importo movimento banca non valido.');
}
$amount = round(abs($importo), 2);
$descr = is_string($movimento->descrizione) ? trim((string) $movimento->descrizione) : '';
if ($descr === '') {
$descr = 'Movimento banca';
}
$data = $movimento->data ? $movimento->data->toDateString() : now()->toDateString();
return DB::transaction(function () use ($user, $movimento, $stabileId, $gestioneId, $regola, $data, $descr, $amount, $importo) {
$swap = $importo < 0;
$contoDare = (int) ($swap ? $regola->conto_avere_id : $regola->conto_dare_id);
$contoAvere = (int) ($swap ? $regola->conto_dare_id : $regola->conto_avere_id);
$prot = null;
if (
Schema::hasColumn('contabilita_registrazioni', 'protocollo_completo')
&& Schema::hasTable('gestioni_contabili')
) {
$g = GestioneContabile::query()->lockForUpdate()->find($gestioneId);
if ($g) {
$prot = $g->getNextProtocollo();
}
}
$regPayload = [
'stabile_id' => $stabileId,
'gestione_id' => (Schema::hasColumn('contabilita_registrazioni', 'gestione_id')
? Registrazione::resolveGestioneIdOrDefault($gestioneId, $stabileId, $data)
: null),
'data_registrazione' => $data,
'descrizione' => $descr,
'created_by' => (int) $user->id,
'updated_by' => (int) $user->id,
];
if (Schema::hasColumn('contabilita_registrazioni', 'user_id')) {
$regPayload['user_id'] = (int) $user->id;
}
/** @var Registrazione $reg */
$reg = Registrazione::query()->create($regPayload);
if (is_array($prot) && ! empty($prot['completo'])) {
$reg->fill([
'protocollo_prefix' => $prot['prefix'] ?? null,
'protocollo_numero' => $prot['numero'] ?? null,
'protocollo_completo' => $prot['completo'] ?? null,
]);
$reg->save();
}
Movimento::query()->create([
'registrazione_id' => (int) $reg->id,
'conto_id' => $contoDare,
'tipo' => 'dare',
'importo' => $amount,
]);
Movimento::query()->create([
'registrazione_id' => (int) $reg->id,
'conto_id' => $contoAvere,
'tipo' => 'avere',
'importo' => $amount,
]);
$reg->assertBilanciata();
$movimento->registrazione_id = (int) $reg->id;
$movimento->save();
return $reg;
});
}
}