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

761 lines
30 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Modules\Contabilita\Services;
use App\Models\FornitoreStabileImpostazione;
use App\Models\GestioneContabile;
use App\Models\User;
use App\Models\VoceSpesa;
use App\Modules\Contabilita\Models\FatturaFornitore;
use App\Modules\Contabilita\Models\Movimento;
use App\Modules\Contabilita\Models\PianoConti;
use App\Modules\Contabilita\Models\Registrazione;
use App\Services\FattureElettroniche\FatturaElettronicaXmlParser;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use RuntimeException;
class FatturaFornitorePrimaNotaService
{
private function resolveContoCostoFromVoce(VoceSpesa $voce, int $stabileId): ?int
{
$codes = [];
foreach (['sottoconto_pd', 'conto_pd', 'codice', 'legacy_codice'] as $field) {
$val = trim((string) ($voce->{$field} ?? ''));
if ($val !== '') {
$codes[] = $val;
}
}
foreach ($codes as $code) {
$candidate = $this->normalizeVoceContoCode($code);
$conto = PianoConti::query()->where('codice', $candidate)->first();
if (! $conto && $candidate !== $code) {
$conto = PianoConti::query()->where('codice', $code)->first();
}
if ($conto) {
return (int) $conto->id;
}
if ($candidate !== '') {
$createdId = $this->createContoFromVoce($candidate, trim((string) ($voce->descrizione ?? 'Voce spesa ' . $voce->id)), 'COSTO');
if ($createdId > 0) {
return $createdId;
}
}
}
if (Schema::hasTable('piano_conti')) {
foreach ($codes as $code) {
$legacyParts = $this->parseLegacyContoCode($code);
if (! $legacyParts) {
continue;
}
$legacy = DB::table('piano_conti')
->where('mastro', $legacyParts['mastro'])
->where('conto', $legacyParts['conto'])
->where('sottoconto', $legacyParts['sottoconto'])
->where('quarto_livello', $legacyParts['quarto'])
->first();
if (! $legacy) {
continue;
}
$codice = $legacyParts['mastro'] . '.' . $legacyParts['conto'] . '.' . $legacyParts['sottoconto'] . '.' . $legacyParts['quarto'];
$descr = trim((string) ($legacy->denominazione ?? $voce->descrizione ?? ''));
if ($descr === '') {
$descr = 'Voce spesa ' . $voce->id;
}
$existing = PianoConti::query()->where('codice', $codice)->first();
if ($existing) {
return (int) $existing->id;
}
try {
$created = PianoConti::query()->create([
'codice' => $codice,
'descrizione' => $descr,
'tipo' => 'Costo',
'is_master' => false,
'parent_id' => null,
]);
if ($created) {
return (int) $created->id;
}
} catch (\Throwable) {
// ignore
}
}
}
return null;
}
private function normalizeVoceContoCode(string $code): string
{
$code = trim($code);
if ($code === '') {
return '';
}
$parts = preg_split('/\s*[-]\s*/u', $code, 2);
$base = trim((string) ($parts[0] ?? $code));
$base = preg_replace('/\s+/', '', $base) ?? $base;
$base = preg_replace('/[^A-Za-z0-9\.]/', '', $base) ?? $base;
return $base;
}
private function createContoFromVoce(string $codice, string $descrizione, string $tipoConto): int
{
$codice = trim($codice);
if ($codice === '') {
return 0;
}
if (! Schema::hasTable('contabilita_piano_conti')) {
return 0;
}
$existingId = DB::table('contabilita_piano_conti')->where('codice', $codice)->value('id');
if (is_numeric($existingId)) {
return (int) $existingId;
}
$descrizione = trim($descrizione);
if ($descrizione === '') {
$descrizione = 'Voce spesa';
}
try {
$id = DB::table('contabilita_piano_conti')->insertGetId([
'codice' => $codice,
'denominazione' => $descrizione,
'descrizione' => $descrizione,
'tipo_conto' => $tipoConto,
'attivo' => 1,
'livello' => 3,
'codice_padre' => null,
'saldo_iniziale' => 0.0,
'created_at' => now(),
'updated_at' => now(),
]);
return is_numeric($id) ? (int) $id : 0;
} catch (\Throwable) {
return 0;
}
}
/**
* @return array{mastro: string, conto: string, sottoconto: string, quarto: string}|null
*/
private function parseLegacyContoCode(string $code): ?array
{
$code = trim($code);
if ($code === '') {
return null;
}
if (str_contains($code, '.')) {
$parts = array_values(array_filter(explode('.', $code), fn($v) => $v !== ''));
} else {
$parts = [$code];
}
$mastro = $parts[0] ?? '';
$conto = $parts[1] ?? '';
$sottoconto = $parts[2] ?? '';
$quarto = $parts[3] ?? '';
if ($mastro === '') {
return null;
}
$mastro = str_pad(substr($mastro, 0, 4), 4, '0', STR_PAD_LEFT);
$conto = $conto !== '' ? str_pad(substr($conto, 0, 8), 8, '0', STR_PAD_LEFT) : '00000000';
$sottoconto = $sottoconto !== '' ? str_pad(substr($sottoconto, 0, 5), 5, '0', STR_PAD_LEFT) : '00000';
$quarto = $quarto !== '' ? str_pad(substr($quarto, 0, 5), 5, '0', STR_PAD_LEFT) : '00000';
return [
'mastro' => $mastro,
'conto' => $conto,
'sottoconto' => $sottoconto,
'quarto' => $quarto,
];
}
private function buildMovimentoPayload(
int $registrazioneId,
int $stabileId,
int $contoId,
?PianoConti $conto,
string $tipo,
float $importo,
string $descrizione,
?int $voceSpesaId = null
): array {
$payload = [
'registrazione_id' => $registrazioneId,
'conto_id' => $contoId,
'tipo' => $tipo,
'importo' => $importo,
];
if (Schema::hasColumn('contabilita_movimenti', 'entry_id')) {
$payload['entry_id'] = $registrazioneId;
}
if (Schema::hasColumn('contabilita_movimenti', 'voce_spesa_id') && $voceSpesaId && $voceSpesaId > 0) {
$payload['voce_spesa_id'] = $voceSpesaId;
}
$code = trim((string) ($conto?->codice ?? ''));
if ($code === '') {
$code = (string) $contoId;
}
$normalized = preg_replace('/[^A-Za-z0-9]/', '', strtoupper($code)) ?: (string) $contoId;
$masterCode = str_pad(substr($normalized, 0, 3), 3, '0', STR_PAD_LEFT);
$accountCode = str_pad(substr($normalized, 0, 8), 8, '0', STR_PAD_LEFT);
$subAccountCode = str_pad(substr($normalized, -5), 5, '0', STR_PAD_LEFT);
if (Schema::hasColumn('contabilita_movimenti', 'chart_account_id')) {
$chartAccountId = $this->resolveLegacyChartAccountId(
$stabileId,
$contoId,
$conto,
$masterCode,
$accountCode,
$subAccountCode,
);
if ($chartAccountId > 0) {
$payload['chart_account_id'] = $chartAccountId;
}
}
if (Schema::hasColumn('contabilita_movimenti', 'master_code')) {
$payload['master_code'] = $masterCode;
}
if (Schema::hasColumn('contabilita_movimenti', 'account_code')) {
$payload['account_code'] = $accountCode;
}
if (Schema::hasColumn('contabilita_movimenti', 'subaccount_code')) {
$payload['subaccount_code'] = $subAccountCode;
}
if (Schema::hasColumn('contabilita_movimenti', 'debit_amount')) {
$payload['debit_amount'] = $tipo === 'dare' ? $importo : 0.0;
}
if (Schema::hasColumn('contabilita_movimenti', 'credit_amount')) {
$payload['credit_amount'] = $tipo === 'avere' ? $importo : 0.0;
}
if (Schema::hasColumn('contabilita_movimenti', 'movement_description')) {
$payload['movement_description'] = $descrizione !== '' ? $descrizione : null;
}
if (Schema::hasColumn('contabilita_movimenti', 'sequence')) {
$payload['sequence'] = 1;
}
return $payload;
}
/**
* @param array<int> $voceIds
*/
private function syncConsuntivoVociSpesaDaMovimenti(int $stabileId, int $gestioneId, array $voceIds): void
{
if ($stabileId <= 0 || $gestioneId <= 0 || $voceIds === []) {
return;
}
if (! Schema::hasColumn('voci_spesa', 'importo_consuntivo')) {
return;
}
if (! Schema::hasColumn('contabilita_movimenti', 'voce_spesa_id')) {
return;
}
$voceIds = array_values(array_unique(array_filter(array_map('intval', $voceIds), fn(int $id) => $id > 0)));
if ($voceIds === []) {
return;
}
$totali = DB::table('contabilita_movimenti as m')
->join('contabilita_registrazioni as r', 'r.id', '=', 'm.registrazione_id')
->where('r.stabile_id', $stabileId)
->where('r.gestione_id', $gestioneId)
->where('m.tipo', 'dare')
->whereIn('m.voce_spesa_id', $voceIds)
->selectRaw('m.voce_spesa_id, SUM(COALESCE(m.importo, 0)) as totale')
->groupBy('m.voce_spesa_id')
->pluck('totale', 'm.voce_spesa_id')
->all();
foreach ($voceIds as $voceId) {
$totale = round((float) ($totali[$voceId] ?? 0), 2);
VoceSpesa::query()
->whereKey($voceId)
->where('stabile_id', $stabileId)
->where('gestione_contabile_id', $gestioneId)
->update(['importo_consuntivo' => $totale]);
}
}
private function resolveLegacyChartAccountId(
int $stabileId,
int $contoId,
?PianoConti $conto,
string $masterCode,
string $accountCode,
string $subAccountCode
): int {
if (! Schema::hasTable('piano_conti')) {
return 0;
}
$mastro = str_pad(substr($masterCode, 0, 4), 4, '0', STR_PAD_LEFT);
$contoCode = str_pad(substr($accountCode, 0, 8), 8, '0', STR_PAD_LEFT);
$sotto = str_pad(substr($subAccountCode, -5), 5, '0', STR_PAD_LEFT);
$quarto = '00000';
$query = DB::table('piano_conti')
->where('mastro', $mastro)
->where('conto', $contoCode)
->where('sottoconto', $sotto);
if ($stabileId > 0) {
$query->where(function ($q) use ($stabileId) {
$q->whereNull('stabile_id')->orWhere('stabile_id', $stabileId);
});
}
$existingId = $query->value('id');
if (is_numeric($existingId)) {
return (int) $existingId;
}
$descr = trim((string) ($conto?->descrizione ?? $conto?->description ?? ''));
if ($descr === '') {
$descr = 'Conto ' . $mastro . '.' . $contoCode . '.' . $sotto;
}
$tipo = strtoupper(trim((string) ($conto?->tipo ?? '')));
$tipoEnum = match ($tipo) {
'ATTIVITÀ', 'ATTIVITA', 'ATTIVO' => 'ATTIVO',
'PASSIVITÀ', 'PASSIVITA', 'PASSIVO' => 'PASSIVO',
'RICAVO' => 'RICAVO',
'PATRIMONIO NETTO', 'PATRIMONIO' => 'PATRIMONIO',
default => 'COSTO',
};
$tipologia = match ($tipoEnum) {
'RICAVO' => 'RIC',
'ATTIVO' => 'ATT',
'PASSIVO' => 'PAS',
'PATRIMONIO' => 'PAT',
default => 'SPE',
};
$id = DB::table('piano_conti')->insertGetId([
'stabile_id' => $stabileId > 0 ? $stabileId : null,
'mastro' => $mastro,
'conto' => $contoCode,
'sottoconto' => $sotto,
'quarto_livello' => $quarto,
'denominazione' => $descr,
'tipo' => $tipoEnum,
'tipologia' => $tipologia,
'attivo' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
return is_numeric($id) ? (int) $id : 0;
}
public function generaRegistrazioneDaFattura(User $user, FatturaFornitore $fattura): Registrazione
{
if (! Schema::hasTable('contabilita_registrazioni') || ! Schema::hasTable('contabilita_movimenti')) {
throw new RuntimeException('Tabelle prima nota mancanti.');
}
if (! empty($fattura->registrazione_id)) {
$existing = Registrazione::query()->find((int) $fattura->registrazione_id);
if ($existing) {
return $existing;
}
}
$stabileId = (int) ($fattura->stabile_id ?? 0);
if ($stabileId <= 0) {
throw new RuntimeException('Fattura senza stabile_id.');
}
$gestioneId = (int) ($fattura->gestione_id ?? 0);
if ($gestioneId <= 0) {
throw new RuntimeException('Seleziona una gestione per questa fattura.');
}
if (Schema::hasTable('contabilita_chiusure_gestione')) {
$chiusa = app(ChiusuraGestioneService::class)->isGestioneChiusa($gestioneId);
if ($chiusa) {
throw new RuntimeException('Gestione chiusa: non è possibile generare nuove registrazioni.');
}
}
$fattura->loadMissing(['righe']);
if ($fattura->righe->count() <= 0) {
throw new RuntimeException('Inserisci almeno una riga di fattura (riparto costi) prima di generare la prima nota.');
}
$imponibile = round((float) ($fattura->imponibile ?? 0), 2);
$iva = round((float) ($fattura->iva ?? 0), 2);
$totale = round((float) ($fattura->totale ?? 0), 2);
$ritenuta = round((float) ($fattura->ritenuta_importo ?? 0), 2);
$ritenutaAliquota = (float) ($fattura->ritenuta_aliquota ?? 0);
$ritenutaCodice = trim((string) ($fattura->ritenuta_previdenziale_codice ?? ''));
$ritenutaTributo = trim((string) ($fattura->ritenuta_codice_tributo ?? ''));
if (abs($totale) < 0.005) {
throw new RuntimeException('Totale fattura non valido.');
}
$netto = round($totale - $ritenuta, 2);
if ($ritenuta > 0) {
$needsUpdate = false;
if (round((float) ($fattura->ritenuta_importo ?? 0), 2) <= 0 && $ritenuta > 0) {
$fattura->ritenuta_importo = $ritenuta;
$needsUpdate = true;
}
if ((float) ($fattura->ritenuta_aliquota ?? 0) <= 0 && $ritenutaAliquota > 0) {
$fattura->ritenuta_aliquota = $ritenutaAliquota;
$needsUpdate = true;
}
if (trim((string) ($fattura->ritenuta_previdenziale_codice ?? '')) === '' && $ritenutaCodice !== '') {
$fattura->ritenuta_previdenziale_codice = $ritenutaCodice;
$needsUpdate = true;
}
if (trim((string) ($fattura->ritenuta_codice_tributo ?? '')) === '' && $ritenutaTributo !== '') {
$fattura->ritenuta_codice_tributo = $ritenutaTributo;
$needsUpdate = true;
}
if ($needsUpdate) {
$fattura->save();
}
}
$fattura->loadMissing(['fornitore', 'fatturaElettronica']);
if ($ritenuta <= 0 && $fattura->fatturaElettronica && is_string($fattura->fatturaElettronica->xml_content ?? null)) {
$xml = trim((string) $fattura->fatturaElettronica->xml_content);
if ($xml !== '') {
try {
$parsed = app(FatturaElettronicaXmlParser::class)->parse($xml);
$rit = is_array($parsed['ritenuta'] ?? null) ? $parsed['ritenuta'] : null;
if (is_array($rit)) {
if ($ritenuta <= 0 && is_numeric($rit['importo'] ?? null)) {
$ritenuta = round((float) $rit['importo'], 2);
}
if ($ritenutaAliquota <= 0 && is_numeric($rit['aliquota'] ?? null)) {
$ritenutaAliquota = (float) $rit['aliquota'];
}
if ($ritenutaCodice === '' && is_string($rit['tipo'] ?? null)) {
$ritenutaCodice = trim((string) $rit['tipo']);
}
if ($ritenutaTributo === '' && is_string($rit['causale'] ?? null)) {
$ritenutaTributo = trim((string) $rit['causale']);
}
$needsUpdate = false;
if ($ritenuta > 0 && round((float) ($fattura->ritenuta_importo ?? 0), 2) <= 0) {
$fattura->ritenuta_importo = $ritenuta;
$needsUpdate = true;
}
if ($ritenutaAliquota > 0 && (float) ($fattura->ritenuta_aliquota ?? 0) <= 0) {
$fattura->ritenuta_aliquota = $ritenutaAliquota;
$needsUpdate = true;
}
if ($ritenutaCodice !== '' && trim((string) ($fattura->ritenuta_previdenziale_codice ?? '')) === '') {
$fattura->ritenuta_previdenziale_codice = $ritenutaCodice;
$needsUpdate = true;
}
if ($ritenutaTributo !== '' && trim((string) ($fattura->ritenuta_codice_tributo ?? '')) === '') {
$fattura->ritenuta_codice_tributo = $ritenutaTributo;
$needsUpdate = true;
}
if ($needsUpdate) {
$fattura->save();
}
}
} catch (\Throwable) {
// ignore
}
}
}
$fornitoreId = (int) ($fattura->fornitore_id ?? 0);
$fornitoreLabel = is_object($fattura->fornitore)
? (is_string($fattura->fornitore->ragione_sociale ?? null) ? trim((string) $fattura->fornitore->ragione_sociale) : null)
: null;
$fornitoreCode = is_object($fattura->fornitore) && is_string($fattura->fornitore->codice_univoco ?? null)
? trim((string) $fattura->fornitore->codice_univoco)
: null;
$contoDebitiFornitori = ($fornitoreId > 0)
? app(ContiSoggettiService::class)->resolveContoDebitoFornitore($stabileId, $fornitoreId, $fornitoreLabel, $fornitoreCode)
: app(ChiusuraGestioneService::class)->resolveOrCreateConto(
(string) config('contabilita.conti_speciali.debiti_fornitori_codice', '2000'),
(string) config('contabilita.conti_speciali.debiti_fornitori_descrizione', 'Debiti verso fornitori'),
(string) config('contabilita.conti_speciali.debiti_fornitori_tipo', 'Passività'),
);
$contoIvaCredito = null;
if ($iva > 0) {
$contoIvaCredito = app(ChiusuraGestioneService::class)->resolveOrCreateConto(
(string) config('contabilita.conti_speciali.iva_credito_codice', '1210'),
(string) config('contabilita.conti_speciali.iva_credito_descrizione', 'IVA a credito'),
(string) config('contabilita.conti_speciali.iva_credito_tipo', 'Attività'),
);
}
$contoRitenute = null;
if ($ritenuta > 0) {
$contoRitenute = $this->resolveContoRitenuteOperate($stabileId);
}
$data = $fattura->data_registrazione?->toDateString()
?: $fattura->data_documento?->toDateString()
?: now()->toDateString();
$numero = is_string($fattura->numero_documento) ? trim((string) $fattura->numero_documento) : '';
$descr = is_string($fattura->descrizione) ? trim((string) $fattura->descrizione) : '';
if ($descr === '') {
$descr = 'Fattura fornitore' . ($numero !== '' ? (' ' . $numero) : '');
}
return DB::transaction(function () use ($user, $fattura, $stabileId, $gestioneId, $data, $descr, $iva, $ritenuta, $netto, $contoDebitiFornitori, $contoIvaCredito, $contoRitenute, $fornitoreId) {
$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,
'data_registrazione' => $data,
];
if (Schema::hasColumn('contabilita_registrazioni', 'created_by')) {
$regPayload['created_by'] = (int) $user->id;
}
if (Schema::hasColumn('contabilita_registrazioni', 'updated_by')) {
$regPayload['updated_by'] = (int) $user->id;
}
if (Schema::hasColumn('contabilita_registrazioni', 'user_id')) {
$regPayload['user_id'] = (int) $user->id;
}
if (Schema::hasColumn('contabilita_registrazioni', 'gestione_id')) {
$regPayload['gestione_id'] = Registrazione::resolveGestioneIdOrDefault($gestioneId, $stabileId, $data);
}
if (Schema::hasColumn('contabilita_registrazioni', 'entry_date')) {
$regPayload['entry_date'] = $data;
}
if (Schema::hasColumn('contabilita_registrazioni', 'description')) {
$regPayload['description'] = $descr;
} elseif (Schema::hasColumn('contabilita_registrazioni', 'descrizione')) {
$regPayload['descrizione'] = $descr;
} elseif (Schema::hasColumn('contabilita_registrazioni', 'denominazione')) {
$regPayload['denominazione'] = $descr;
}
if (Schema::hasColumn('contabilita_registrazioni', 'document_type')) {
$regPayload['document_type'] = $fattura->causale_contabile ?: 'Fattura ricevuta';
}
if (Schema::hasColumn('contabilita_registrazioni', 'document_number')) {
$numeroDoc = is_string($fattura->numero_documento) ? trim((string) $fattura->numero_documento) : '';
if ($numeroDoc !== '') {
$regPayload['document_number'] = $numeroDoc;
}
}
if (Schema::hasColumn('contabilita_registrazioni', 'external_reference')) {
$regPayload['external_reference'] = 'FATTURA_FORNITORE:' . (int) $fattura->id;
}
if (Schema::hasColumn('contabilita_registrazioni', 'note')) {
$regPayload['note'] = $descr;
}
/** @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();
}
$voceIdsMovimenti = [];
foreach ($fattura->righe as $r) {
$imp = round((float) ($r->imponibile_euro ?? 0), 2);
if (abs($imp) < 0.005) {
continue;
}
$contoCostoId = (int) ($r->conto_costo_id ?? 0);
$voceId = (int) ($r->voce_spesa_id ?? 0);
if ($voceId > 0 && Schema::hasTable('voci_spesa') && Schema::hasTable('contabilita_piano_conti')) {
$voce = VoceSpesa::query()->find($voceId);
if ($voce) {
$contoCostoId = $this->resolveContoCostoFromVoce($voce, $stabileId) ?? 0;
}
}
if ($contoCostoId <= 0 && $voceId <= 0 && Schema::hasTable('fornitore_stabile_impostazioni')) {
$preset = FornitoreStabileImpostazione::query()
->where('stabile_id', $stabileId)
->where('fornitore_id', $fornitoreId)
->first();
$presetVoceId = (int) ($preset?->voce_spesa_default_id ?? 0);
if ($presetVoceId > 0 && Schema::hasTable('voci_spesa') && Schema::hasTable('contabilita_piano_conti')) {
$presetVoce = VoceSpesa::query()->find($presetVoceId);
if ($presetVoce) {
$contoCostoId = $this->resolveContoCostoFromVoce($presetVoce, $stabileId) ?? $contoCostoId;
}
}
}
if ($voceId <= 0) {
throw new RuntimeException('Riga fattura senza voce spesa. Seleziona la voce spesa per ogni riga.');
}
if ($contoCostoId <= 0) {
throw new RuntimeException('Voce spesa senza conto contabile collegato.');
}
$rowDescr = trim((string) ($r->descrizione ?? ''));
$contoObj = PianoConti::query()->find($contoCostoId);
$movPayload = $this->buildMovimentoPayload(
(int) $reg->id,
$stabileId,
$contoCostoId,
$contoObj,
'dare',
$imp,
$rowDescr !== '' ? $rowDescr : $descr,
$voceId > 0 ? $voceId : null,
);
Movimento::query()->create($movPayload);
if ($voceId > 0) {
$voceIdsMovimenti[] = $voceId;
}
}
if ($contoIvaCredito && $iva > 0) {
$movPayload = $this->buildMovimentoPayload(
(int) $reg->id,
$stabileId,
(int) $contoIvaCredito->id,
$contoIvaCredito,
'dare',
$iva,
$descr,
);
Movimento::query()->create($movPayload);
}
if ($contoRitenute && $ritenuta > 0) {
$movPayload = $this->buildMovimentoPayload(
(int) $reg->id,
$stabileId,
(int) $contoRitenute->id,
$contoRitenute,
'avere',
$ritenuta,
$descr,
);
Movimento::query()->create($movPayload);
}
$movPayload = $this->buildMovimentoPayload(
(int) $reg->id,
$stabileId,
(int) $contoDebitiFornitori->id,
$contoDebitiFornitori,
'avere',
$netto,
$descr,
);
Movimento::query()->create($movPayload);
$reg->assertBilanciata();
$fattura->registrazione_id = (int) $reg->id;
$fattura->stato = 'contabilizzato';
$fattura->save();
$this->syncConsuntivoVociSpesaDaMovimenti(
$stabileId,
$gestioneId,
$voceIdsMovimenti
);
return $reg;
});
}
private function resolveContoRitenuteOperate(int $stabileId): PianoConti
{
$voce = null;
if (Schema::hasTable('voci_spesa')) {
$voce = VoceSpesa::query()
->where(function ($q) {
$q->where('codice', 'RAO')
->orWhere('legacy_codice', 'RAO')
->orWhere('descrizione', 'like', '%Ritenute%');
})
->orderByDesc('id')
->first();
}
$codice = '2010';
$descrizione = 'Erario c/ritenute da versare';
$tipo = 'Passività';
if ($voce) {
$candidate = trim((string) ($voce->codice ?? $voce->legacy_codice ?? ''));
if ($candidate !== '') {
$codice = $candidate;
}
$descVoce = trim((string) ($voce->descrizione ?? ''));
if ($descVoce !== '') {
$descrizione = $descVoce;
}
}
return app(ChiusuraGestioneService::class)->resolveOrCreateConto(
(string) config('contabilita.conti_speciali.ritenute_da_versare_codice', $codice),
(string) config('contabilita.conti_speciali.ritenute_da_versare_descrizione', $descrizione),
(string) config('contabilita.conti_speciali.ritenute_da_versare_tipo', $tipo),
);
}
}