1240 lines
50 KiB
PHP
Executable File
1240 lines
50 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Filament\Pages\Contabilita;
|
|
|
|
use App\Models\GestioneContabile;
|
|
use App\Models\User;
|
|
use App\Filament\Pages\Contabilita\PrimaNotaDettaglio;
|
|
use App\Filament\Pages\Contabilita\PrimaNotaModifica;
|
|
use App\Modules\Contabilita\Models\ChiusuraGestione;
|
|
use App\Modules\Contabilita\Models\Movimento;
|
|
use App\Modules\Contabilita\Models\PianoConti;
|
|
use App\Modules\Contabilita\Models\Registrazione;
|
|
use App\Modules\Contabilita\Services\ChiusuraGestioneService;
|
|
use App\Support\AnnoGestioneContext;
|
|
use App\Support\GestioneContext;
|
|
use App\Support\StabileContext;
|
|
use BackedEnum;
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\DatePicker;
|
|
use Filament\Forms\Components\Repeater;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Forms\Components\Textarea;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tables\Concerns\InteractsWithTable;
|
|
use Filament\Tables\Contracts\HasTable;
|
|
use Filament\Tables\Filters\SelectFilter;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use UnitEnum;
|
|
|
|
class PrimaNotaArchivio extends Page implements HasTable
|
|
{
|
|
use InteractsWithTable;
|
|
|
|
protected static ?string $navigationLabel = 'Prima nota';
|
|
|
|
protected static ?string $title = 'Prima nota';
|
|
|
|
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-book-open';
|
|
|
|
protected static UnitEnum|string|null $navigationGroup = 'Contabilità';
|
|
|
|
protected static ?int $navigationSort = 10;
|
|
|
|
protected static ?string $slug = 'contabilita/prima-nota';
|
|
|
|
protected string $view = 'filament.pages.contabilita.prima-nota-archivio';
|
|
|
|
/** @var array<int, array{causale: string, fornitore: string, totale: float, ritenuta: float}> */
|
|
private array $fatturaCache = [];
|
|
|
|
/** @var array<int, array{dare: float, avere: float}> */
|
|
private array $movimentiTotaliCache = [];
|
|
|
|
/** @var array<string, mixed>|null */
|
|
private ?array $bilancioCache = null;
|
|
|
|
public string $archivioScope = 'all';
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
if ($user->hasAnyRole(['super-admin', 'admin'])) {
|
|
return true;
|
|
}
|
|
|
|
return $user->can('contabilita.prima-nota');
|
|
}
|
|
|
|
public function mount(): void
|
|
{
|
|
$scope = strtolower((string) request()->query('archivio', 'all'));
|
|
$this->archivioScope = in_array($scope, ['all', 'gescon', 'netgescon'], true) ? $scope : 'all';
|
|
$this->mountInteractsWithTable();
|
|
}
|
|
|
|
public function getActiveStabile(): bool
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) StabileContext::resolveActiveStabileId($user);
|
|
}
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
Action::make('sync_legacy_operazioni')
|
|
->label('Aggiorna da legacy')
|
|
->icon('heroicon-o-arrow-path')
|
|
->form([
|
|
Select::make('tipo')
|
|
->label('Gestione')
|
|
->options([
|
|
'all' => 'Tutte',
|
|
'ordinaria' => 'Ordinaria',
|
|
'straordinaria' => 'Straordinaria',
|
|
'riscaldamento' => 'Riscaldamento',
|
|
])
|
|
->default('all')
|
|
->required(),
|
|
TextInput::make('anno')
|
|
->label('Anno (opzionale)')
|
|
->numeric()
|
|
->minValue(2000)
|
|
->maxValue(2100)
|
|
->nullable(),
|
|
\Filament\Forms\Components\Toggle::make('dry')
|
|
->label('Solo simulazione (dry-run)')
|
|
->default(false),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
Notification::make()->title('Utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
Notification::make()->title('Seleziona uno stabile')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$params = [
|
|
'--stabile-id' => (string) $stabileId,
|
|
'--tipo' => (string) ($data['tipo'] ?? 'all'),
|
|
];
|
|
|
|
if (! empty($data['anno']) && is_numeric($data['anno'])) {
|
|
$params['--year'] = (string) ((int) $data['anno']);
|
|
}
|
|
|
|
if (! empty($data['dry'])) {
|
|
$params['--dry'] = true;
|
|
}
|
|
|
|
$code = Artisan::call('gescon:sync-operazioni-prima-nota', $params);
|
|
$output = trim(Artisan::output());
|
|
|
|
if ($code === 0) {
|
|
Notification::make()
|
|
->title('Sync legacy completato')
|
|
->body($output !== '' ? $output : 'Operazione completata con successo.')
|
|
->success()
|
|
->send();
|
|
$this->resetTable();
|
|
return;
|
|
}
|
|
|
|
Notification::make()
|
|
->title('Errore sync legacy')
|
|
->body($output !== '' ? $output : 'Comando terminato con errore.')
|
|
->danger()
|
|
->send();
|
|
}),
|
|
|
|
Action::make('archivio_chiusure')
|
|
->label('Archivio chiusure')
|
|
->icon('heroicon-o-archive-box')
|
|
->visible(fn () => Schema::hasTable('contabilita_chiusure_gestione'))
|
|
->url(fn (): string => ChiusureGestioneArchivio::getUrl()),
|
|
|
|
Action::make('chiudi_gestione')
|
|
->label('Chiudi gestione')
|
|
->icon('heroicon-o-lock-closed')
|
|
->visible(fn () => Schema::hasTable('gestioni_contabili')
|
|
&& Schema::hasTable('contabilita_registrazioni')
|
|
&& Schema::hasColumn('contabilita_registrazioni', 'gestione_id')
|
|
&& Schema::hasTable('contabilita_chiusure_gestione'))
|
|
->form([
|
|
Select::make('gestione_id')
|
|
->label('Gestione da chiudere')
|
|
->options(fn () => $this->getGestioniOptions())
|
|
->searchable()
|
|
->required(),
|
|
DatePicker::make('data_chiusura')
|
|
->label('Data chiusura')
|
|
->required()
|
|
->default(fn () => now()->toDateString()),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
Notification::make()->title('Utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
Notification::make()->title('Seleziona uno stabile')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$gestione = GestioneContabile::query()->where('stabile_id', $stabileId)->find((int) $data['gestione_id']);
|
|
if (! $gestione) {
|
|
Notification::make()->title('Gestione non trovata')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$service = app(ChiusuraGestioneService::class);
|
|
$service->chiudiGestione($user, $stabileId, $gestione, (string) $data['data_chiusura']);
|
|
Notification::make()->title('Gestione chiusa')->success()->send();
|
|
$this->resetTable();
|
|
} catch (\Throwable $e) {
|
|
Notification::make()->title('Errore chiusura')->body($e->getMessage())->danger()->send();
|
|
}
|
|
}),
|
|
|
|
Action::make('nuova_registrazione')
|
|
->label('Nuova registrazione')
|
|
->icon('heroicon-o-plus')
|
|
->form([
|
|
Select::make('gestione_id')
|
|
->label('Gestione')
|
|
->options(fn () => $this->getGestioniOptions())
|
|
->searchable()
|
|
->visible(fn () => Schema::hasTable('contabilita_registrazioni') && Schema::hasColumn('contabilita_registrazioni', 'gestione_id'))
|
|
->default(function (): ?int {
|
|
if (! Schema::hasTable('gestioni_contabili')) {
|
|
return null;
|
|
}
|
|
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return null;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
return null;
|
|
}
|
|
|
|
$anno = AnnoGestioneContext::resolveActiveAnno($user);
|
|
$tipo = GestioneContext::resolveActiveGestione($user);
|
|
|
|
$match = GestioneContabile::query()
|
|
->where('stabile_id', $stabileId)
|
|
->where('anno_gestione', $anno)
|
|
->where('tipo_gestione', $tipo)
|
|
->orderBy('numero_straordinaria')
|
|
->first();
|
|
|
|
return $match?->id;
|
|
}),
|
|
|
|
DatePicker::make('data_registrazione')
|
|
->label('Data')
|
|
->required()
|
|
->default(now()->toDateString()),
|
|
|
|
Textarea::make('descrizione')
|
|
->label('Descrizione')
|
|
->rows(2)
|
|
->required(),
|
|
|
|
Repeater::make('movimenti')
|
|
->label('Movimenti (Dare/Avere)')
|
|
->minItems(2)
|
|
->schema([
|
|
Select::make('conto_id')
|
|
->label('Conto')
|
|
->options(fn () => PianoConti::query()
|
|
->orderBy('codice')
|
|
->get()
|
|
->mapWithKeys(fn (PianoConti $c) => [(string) $c->id => $c->codice . ' - ' . $c->descrizione])
|
|
->all())
|
|
->searchable()
|
|
->required(),
|
|
|
|
Select::make('tipo')
|
|
->label('Tipo')
|
|
->options(['dare' => 'Dare', 'avere' => 'Avere'])
|
|
->required(),
|
|
|
|
TextInput::make('importo')
|
|
->label('Importo')
|
|
->required(),
|
|
])
|
|
->columns(3)
|
|
->required(),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
Notification::make()->title('Utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
Notification::make()->title('Seleziona uno stabile')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$movimenti = $data['movimenti'] ?? [];
|
|
if (! is_array($movimenti) || count($movimenti) < 2) {
|
|
Notification::make()->title('Inserisci almeno 2 movimenti')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$totDare = 0.0;
|
|
$totAvere = 0.0;
|
|
|
|
foreach ($movimenti as $m) {
|
|
$tipo = (string) ($m['tipo'] ?? '');
|
|
$importo = (float) str_replace(',', '.', str_replace('.', '', (string) ($m['importo'] ?? '0')));
|
|
|
|
if ($tipo === 'dare') {
|
|
$totDare += $importo;
|
|
} elseif ($tipo === 'avere') {
|
|
$totAvere += $importo;
|
|
}
|
|
}
|
|
|
|
if (round($totDare, 2) !== round($totAvere, 2)) {
|
|
Notification::make()
|
|
->title('Registrazione non bilanciata')
|
|
->body('Totale Dare e Totale Avere devono coincidere.')
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
DB::transaction(function () use ($user, $stabileId, $data, $movimenti): void {
|
|
$regPayload = [
|
|
'stabile_id' => $stabileId,
|
|
'gestione_id' => (Schema::hasColumn('contabilita_registrazioni', 'gestione_id')
|
|
? (isset($data['gestione_id'])
|
|
? Registrazione::resolveGestioneIdOrDefault((int) $data['gestione_id'], $stabileId, (string) ($data['data_registrazione'] ?? ''))
|
|
: null)
|
|
: null),
|
|
'data_registrazione' => $data['data_registrazione'],
|
|
'description' => $data['descrizione'],
|
|
'created_by' => $user->id,
|
|
'updated_by' => $user->id,
|
|
];
|
|
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'user_id')) {
|
|
$regPayload['user_id'] = (int) $user->id;
|
|
}
|
|
|
|
/** @var Registrazione $reg */
|
|
$reg = Registrazione::query()->create($regPayload);
|
|
|
|
foreach ($movimenti as $m) {
|
|
$contoId = (int) ($m['conto_id'] ?? 0);
|
|
$tipo = (string) ($m['tipo'] ?? '');
|
|
$imp = (float) str_replace(',', '.', str_replace('.', '', (string) ($m['importo'] ?? '0')));
|
|
if ($contoId <= 0 || ! in_array($tipo, ['dare', 'avere'], true) || $imp <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$conto = PianoConti::query()->find($contoId);
|
|
$payload = $this->buildMovimentoPayload((int) $reg->id, $stabileId, $contoId, $conto, $tipo, $imp, (string) $data['descrizione']);
|
|
Movimento::query()->create($payload);
|
|
}
|
|
|
|
$reg->assertBilanciata();
|
|
});
|
|
|
|
Notification::make()->title('Registrazione creata')->success()->send();
|
|
$this->resetTable();
|
|
}),
|
|
|
|
Action::make('bilancio_apertura_manual')
|
|
->label('Bilancio di apertura')
|
|
->icon('heroicon-o-scale')
|
|
->visible(fn () => Schema::hasTable('contabilita_registrazioni')
|
|
&& Schema::hasTable('contabilita_movimenti')
|
|
&& Schema::hasTable('gestioni_contabili')
|
|
&& Schema::hasColumn('contabilita_registrazioni', 'gestione_id'))
|
|
->form([
|
|
Select::make('gestione_id')
|
|
->label('Gestione')
|
|
->options(fn () => $this->getGestioniOptions())
|
|
->searchable()
|
|
->required(),
|
|
|
|
DatePicker::make('data_registrazione')
|
|
->label('Data')
|
|
->required()
|
|
->default(fn () => now()->toDateString()),
|
|
|
|
Textarea::make('descrizione')
|
|
->label('Descrizione')
|
|
->default('Bilancio di apertura (manuale)')
|
|
->rows(2)
|
|
->required(),
|
|
|
|
Repeater::make('movimenti')
|
|
->label('Righe (Dare / Avere)')
|
|
->minItems(2)
|
|
->schema([
|
|
Select::make('conto_id')
|
|
->label('Conto')
|
|
->options(fn () => PianoConti::query()
|
|
->orderBy('codice')
|
|
->get()
|
|
->mapWithKeys(fn (PianoConti $c) => [(string) $c->id => $c->codice . ' - ' . $c->descrizione])
|
|
->all())
|
|
->searchable()
|
|
->required(),
|
|
Select::make('tipo')
|
|
->label('Tipo')
|
|
->options(['dare' => 'Dare', 'avere' => 'Avere'])
|
|
->required(),
|
|
TextInput::make('importo')
|
|
->label('Importo €')
|
|
->numeric()
|
|
->required(),
|
|
])
|
|
->columns(3)
|
|
->required(),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
Notification::make()->title('Utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
Notification::make()->title('Seleziona uno stabile')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$gestioneId = (int) ($data['gestione_id'] ?? 0);
|
|
if ($gestioneId <= 0) {
|
|
Notification::make()->title('Seleziona una gestione')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
if (Schema::hasTable('contabilita_chiusure_gestione')) {
|
|
$chiusa = app(ChiusuraGestioneService::class)->isGestioneChiusa($gestioneId);
|
|
if ($chiusa) {
|
|
Notification::make()->title('Gestione chiusa')->body('Non è possibile inserire movimenti.')->danger()->send();
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
DB::transaction(function () use ($user, $stabileId, $gestioneId, $data): void {
|
|
$regPayload = [
|
|
'stabile_id' => $stabileId,
|
|
'gestione_id' => Registrazione::resolveGestioneIdOrDefault($gestioneId, $stabileId, (string) ($data['data_registrazione'] ?? '')),
|
|
'data_registrazione' => (string) $data['data_registrazione'],
|
|
'description' => (string) $data['descrizione'],
|
|
'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);
|
|
|
|
$righe = is_array($data['movimenti'] ?? null) ? $data['movimenti'] : [];
|
|
foreach ($righe as $r) {
|
|
$contoId = (int) ($r['conto_id'] ?? 0);
|
|
$tipo = (string) ($r['tipo'] ?? '');
|
|
$imp = round((float) ($r['importo'] ?? 0), 2);
|
|
if ($contoId <= 0 || ! in_array($tipo, ['dare', 'avere'], true) || $imp <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$conto = PianoConti::query()->find($contoId);
|
|
$payload = $this->buildMovimentoPayload((int) $reg->id, $stabileId, $contoId, $conto, $tipo, $imp, (string) $data['descrizione']);
|
|
Movimento::query()->create($payload);
|
|
}
|
|
|
|
$reg->assertBilanciata();
|
|
});
|
|
|
|
Notification::make()->title('Bilancio di apertura salvato')->success()->send();
|
|
$this->resetTable();
|
|
} catch (\Throwable $e) {
|
|
Notification::make()->title('Errore')->body($e->getMessage())->danger()->send();
|
|
}
|
|
}),
|
|
|
|
Action::make('modifica_registrazione')
|
|
->label('Modifica registrazione')
|
|
->icon('heroicon-o-pencil-square')
|
|
->form([
|
|
TextInput::make('id')
|
|
->label('ID registrazione')
|
|
->required()
|
|
->numeric(),
|
|
Select::make('gestione_id')
|
|
->label('Gestione')
|
|
->options(fn () => $this->getGestioniOptions())
|
|
->searchable()
|
|
->nullable()
|
|
->visible(fn () => Schema::hasColumn('contabilita_registrazioni', 'gestione_id')),
|
|
DatePicker::make('data_registrazione')
|
|
->label('Data')
|
|
->required(),
|
|
Textarea::make('descrizione')
|
|
->label('Descrizione')
|
|
->rows(2)
|
|
->required(),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
Notification::make()->title('Utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
Notification::make()->title('Seleziona uno stabile')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$id = (int) ($data['id'] ?? 0);
|
|
if ($id <= 0) {
|
|
Notification::make()->title('ID non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
/** @var Registrazione|null $reg */
|
|
$reg = Registrazione::query()->where('stabile_id', $stabileId)->find($id);
|
|
if (! $reg) {
|
|
Notification::make()->title('Registrazione non trovata')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$payload = [
|
|
'data_registrazione' => (string) $data['data_registrazione'],
|
|
'description' => (string) $data['descrizione'],
|
|
];
|
|
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'gestione_id')) {
|
|
$payload['gestione_id'] = ! empty($data['gestione_id'])
|
|
? Registrazione::resolveGestioneIdOrDefault((int) $data['gestione_id'], $stabileId, (string) $data['data_registrazione'])
|
|
: null;
|
|
}
|
|
|
|
$reg->fill($payload);
|
|
$reg->save();
|
|
|
|
Notification::make()->title('Registrazione aggiornata')->success()->send();
|
|
$this->resetTable();
|
|
}),
|
|
|
|
Action::make('elimina_registrazione')
|
|
->label('Elimina registrazione')
|
|
->icon('heroicon-o-trash')
|
|
->requiresConfirmation()
|
|
->form([
|
|
TextInput::make('id')
|
|
->label('ID registrazione')
|
|
->required()
|
|
->numeric(),
|
|
])
|
|
->action(function (array $data): void {
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
Notification::make()->title('Utente non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
Notification::make()->title('Seleziona uno stabile')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
$id = (int) ($data['id'] ?? 0);
|
|
if ($id <= 0) {
|
|
Notification::make()->title('ID non valido')->danger()->send();
|
|
return;
|
|
}
|
|
|
|
DB::transaction(function () use ($id, $stabileId): void {
|
|
$reg = Registrazione::query()->where('stabile_id', $stabileId)->find($id);
|
|
if (! $reg) {
|
|
return;
|
|
}
|
|
|
|
if (Schema::hasTable('contabilita_movimenti')) {
|
|
DB::table('contabilita_movimenti')->where('registrazione_id', $id)->delete();
|
|
}
|
|
|
|
$reg->delete();
|
|
});
|
|
|
|
Notification::make()->title('Registrazione eliminata')->success()->send();
|
|
$this->resetTable();
|
|
}),
|
|
];
|
|
}
|
|
|
|
protected function getTableQuery(): Builder
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return Registrazione::query()->whereRaw('1 = 0');
|
|
}
|
|
|
|
$activeStabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $activeStabileId) {
|
|
return Registrazione::query()->whereRaw('1 = 0');
|
|
}
|
|
|
|
$query = Registrazione::query()
|
|
->where('stabile_id', $activeStabileId)
|
|
->orderByDesc('data_registrazione')
|
|
->orderByDesc('id');
|
|
|
|
if ($this->archivioScope === 'gescon') {
|
|
$this->applyGesconSourceCondition($query);
|
|
} elseif ($this->archivioScope === 'netgescon') {
|
|
$query->where(function ($q): void {
|
|
$q->whereNull('external_reference')
|
|
->orWhere(function ($sub): void {
|
|
$sub->where('external_reference', 'not like', 'GESCON:%')
|
|
->where('external_reference', 'not like', 'LEGACY:%')
|
|
->where('external_reference', 'not like', 'IMPORT_GESCON:%')
|
|
->where('external_reference', 'not like', 'OPERAZIONE_LEGACY:%');
|
|
});
|
|
})->where(function ($q): void {
|
|
$q->whereNull('document_type')
|
|
->orWhereNotIn(DB::raw('UPPER(document_type)'), ['GESCON', 'LEGACY', 'LEGACY_GESCON']);
|
|
});
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
public function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->striped()
|
|
->filters([
|
|
SelectFilter::make('gestione_id')
|
|
->label('Gestione')
|
|
->options(fn () => $this->getGestioniOptions())
|
|
->visible(fn () => Schema::hasColumn('contabilita_registrazioni', 'gestione_id')),
|
|
])
|
|
->columns([
|
|
TextColumn::make('protocol')
|
|
->label('Prot. unico')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$protocol = trim((string) ($record->protocol ?? ''));
|
|
return $protocol !== '' ? $protocol : ('PN-' . (int) $record->id);
|
|
})
|
|
->searchable()
|
|
->toggleable(),
|
|
|
|
TextColumn::make('protocollo_completo')
|
|
->label('Prot. gestione')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$value = trim((string) ($record->protocollo_completo ?? ''));
|
|
if ($value !== '') {
|
|
return $value;
|
|
}
|
|
$prefix = trim((string) ($record->protocollo_prefix ?? ''));
|
|
$num = (int) ($record->protocollo_numero ?? 0);
|
|
return $prefix !== '' && $num > 0 ? ($prefix . '-' . $num) : '—';
|
|
})
|
|
->toggleable(),
|
|
|
|
TextColumn::make('protocollo_prefix')
|
|
->label('Prot. tipo')
|
|
->badge()
|
|
->color('gray')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$prefix = trim((string) ($record->protocollo_prefix ?? ''));
|
|
return $prefix !== '' ? $prefix : '—';
|
|
})
|
|
->toggleable(),
|
|
|
|
TextColumn::make('source')
|
|
->label('Archivio')
|
|
->badge()
|
|
->color(fn (string $state): string => $state === 'GESCON' ? 'warning' : 'success')
|
|
->getStateUsing(fn (Registrazione $record): string => $this->isGesconRecord($record) ? 'GESCON' : 'NetGescon')
|
|
->toggleable(),
|
|
|
|
TextColumn::make('data_registrazione')
|
|
->label('Data')
|
|
->date('d/m/Y')
|
|
->sortable(),
|
|
|
|
TextColumn::make('gestioneContabile.denominazione')
|
|
->label('Gestione')
|
|
->toggleable()
|
|
->visible(fn () => Schema::hasColumn('contabilita_registrazioni', 'gestione_id'))
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
return $this->getGestioneLabel($record);
|
|
}),
|
|
|
|
TextColumn::make('tipo_movimento')
|
|
->label('Tipo movimento')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$info = $this->getFatturaSummary($record);
|
|
$causale = $info['causale'] !== '' ? $info['causale'] : 'Operazione';
|
|
$fornitore = $info['fornitore'] !== '' ? (' · ' . $info['fornitore']) : '';
|
|
return $causale . $fornitore;
|
|
})
|
|
->wrap(),
|
|
|
|
TextColumn::make('description')
|
|
->label('Descrizione')
|
|
->wrap()
|
|
->searchable()
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$descr = trim((string) ($record->description ?? ''));
|
|
if ($descr === '') {
|
|
$descr = trim((string) ($record->descrizione ?? ''));
|
|
}
|
|
if ($descr === '') {
|
|
$descr = trim((string) ($record->notes ?? ''));
|
|
}
|
|
return $descr !== '' ? $descr : '—';
|
|
}),
|
|
|
|
TextColumn::make('totale_operazione')
|
|
->label('Totale')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$info = $this->getFatturaSummary($record);
|
|
$tot = (float) ($info['totale'] ?? 0);
|
|
if ($tot <= 0) {
|
|
$totali = $this->getMovimentiTotals($record);
|
|
$tot = max($totali['dare'], $totali['avere']);
|
|
}
|
|
return '€ ' . number_format($tot, 2, ',', '.');
|
|
})
|
|
->alignRight(),
|
|
|
|
TextColumn::make('ritenuta_importo')
|
|
->label('RA')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$info = $this->getFatturaSummary($record);
|
|
$rit = (float) ($info['ritenuta'] ?? 0);
|
|
return $rit > 0 ? ('€ ' . number_format($rit, 2, ',', '.')) : '—';
|
|
})
|
|
->alignRight(),
|
|
|
|
TextColumn::make('registro_rda')
|
|
->label('Registro RDA')
|
|
->getStateUsing(function (Registrazione $record): string {
|
|
$info = $this->getFatturaSummary($record);
|
|
return ((float) ($info['ritenuta'] ?? 0)) > 0 ? 'Apri' : '—';
|
|
})
|
|
->url(function (Registrazione $record): ?string {
|
|
$info = $this->getFatturaSummary($record);
|
|
if (((float) ($info['ritenuta'] ?? 0)) <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return \App\Filament\Pages\Contabilita\RegistroRitenuteAccontoArchivio::getUrl(panel: 'admin-filament');
|
|
})
|
|
->openUrlInNewTab(),
|
|
|
|
TextColumn::make('dettaglio')
|
|
->label('Dettaglio')
|
|
->getStateUsing(fn (): string => 'Apri')
|
|
->url(fn (Registrazione $record): string => PrimaNotaDettaglio::getUrl(['record' => $record->id], panel: 'admin-filament'))
|
|
->openUrlInNewTab(),
|
|
|
|
TextColumn::make('modifica')
|
|
->label('Modifica')
|
|
->getStateUsing(fn (): string => 'Modifica')
|
|
->url(fn (Registrazione $record): string => PrimaNotaModifica::getUrl(['record' => $record->id], panel: 'admin-filament'))
|
|
->openUrlInNewTab(),
|
|
|
|
TextColumn::make('id')
|
|
->label('ID')
|
|
->toggleable(isToggledHiddenByDefault: true),
|
|
]);
|
|
}
|
|
|
|
private function applyGesconSourceCondition(Builder $query): void
|
|
{
|
|
$query->where(function ($q): void {
|
|
$q->where('external_reference', 'like', 'GESCON:%')
|
|
->orWhere('external_reference', 'like', 'LEGACY:%')
|
|
->orWhere('external_reference', 'like', 'IMPORT_GESCON:%')
|
|
->orWhere('external_reference', 'like', 'OPERAZIONE_LEGACY:%')
|
|
->orWhereIn(DB::raw('UPPER(document_type)'), ['GESCON', 'LEGACY', 'LEGACY_GESCON']);
|
|
});
|
|
}
|
|
|
|
private function isGesconRecord(Registrazione $record): bool
|
|
{
|
|
$ref = strtoupper(trim((string) ($record->external_reference ?? '')));
|
|
if (
|
|
str_starts_with($ref, 'GESCON:')
|
|
|| str_starts_with($ref, 'LEGACY:')
|
|
|| str_starts_with($ref, 'IMPORT_GESCON:')
|
|
|| str_starts_with($ref, 'OPERAZIONE_LEGACY:')
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
$docType = strtoupper(trim((string) ($record->document_type ?? '')));
|
|
if (in_array($docType, ['GESCON', 'LEGACY', 'LEGACY_GESCON'], true)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return array<string,string>
|
|
*/
|
|
private function getGestioniOptions(): array
|
|
{
|
|
if (! Schema::hasTable('gestioni_contabili')) {
|
|
return [];
|
|
}
|
|
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return [];
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId) {
|
|
return [];
|
|
}
|
|
|
|
return GestioneContabile::query()
|
|
->where('stabile_id', $stabileId)
|
|
->orderByDesc('anno_gestione')
|
|
->orderBy('tipo_gestione')
|
|
->orderBy('numero_straordinaria')
|
|
->get()
|
|
->mapWithKeys(fn (GestioneContabile $g) => [
|
|
(string) $g->id => $g->denominazione
|
|
. ' (' . $g->tipo_gestione . ' ' . $g->anno_gestione
|
|
. ($g->numero_straordinaria ? ' #' . $g->numero_straordinaria : '')
|
|
. ')',
|
|
])
|
|
->all();
|
|
}
|
|
|
|
private function getGestioneLabel(Registrazione $record): string
|
|
{
|
|
$gestioneId = (int) ($record->gestione_id ?? 0);
|
|
if ($gestioneId <= 0) {
|
|
return '—';
|
|
}
|
|
|
|
if (Schema::hasTable('gestioni_contabili')) {
|
|
$g = GestioneContabile::query()
|
|
->select(['id', 'denominazione', 'anno_gestione', 'tipo_gestione', 'protocollo_prefix'])
|
|
->find($gestioneId);
|
|
if ($g) {
|
|
$label = trim((string) ($g->denominazione ?? ''));
|
|
if ($label === '') {
|
|
$label = (string) $g->anno_gestione . ' - ' . (string) $g->tipo_gestione;
|
|
}
|
|
$prefix = trim((string) ($g->protocollo_prefix ?? ''));
|
|
if ($prefix !== '') {
|
|
$label = $prefix . ' — ' . $label;
|
|
}
|
|
return $label;
|
|
}
|
|
}
|
|
|
|
if (Schema::hasTable('gestioni')) {
|
|
$g = DB::table('gestioni')
|
|
->select(['id_gestione', 'anno_gestione', 'tipo_gestione'])
|
|
->where('id_gestione', $gestioneId)
|
|
->first();
|
|
if ($g) {
|
|
return (string) ($g->anno_gestione ?? '') . ' - ' . (string) ($g->tipo_gestione ?? '');
|
|
}
|
|
}
|
|
|
|
return '—';
|
|
}
|
|
|
|
/**
|
|
* @return array{causale: string, fornitore: string, totale: float, ritenuta: float}
|
|
*/
|
|
private function getFatturaSummary(Registrazione $record): array
|
|
{
|
|
$id = (int) $record->id;
|
|
if (isset($this->fatturaCache[$id])) {
|
|
return $this->fatturaCache[$id];
|
|
}
|
|
|
|
$summary = [
|
|
'causale' => '',
|
|
'fornitore' => '',
|
|
'totale' => 0.0,
|
|
'ritenuta' => 0.0,
|
|
];
|
|
|
|
if (Schema::hasTable('contabilita_fatture_fornitori')) {
|
|
$select = [
|
|
'f.causale_contabile',
|
|
'f.totale',
|
|
'f.ritenuta_importo',
|
|
'fo.ragione_sociale',
|
|
];
|
|
if (Schema::hasColumn('fornitori', 'denominazione')) {
|
|
$select[] = 'fo.denominazione';
|
|
}
|
|
|
|
$row = DB::table('contabilita_fatture_fornitori as f')
|
|
->leftJoin('fornitori as fo', 'fo.id', '=', 'f.fornitore_id')
|
|
->where('f.registrazione_id', $id)
|
|
->select($select)
|
|
->first();
|
|
|
|
if ($row) {
|
|
$causale = trim((string) ($row->causale_contabile ?? ''));
|
|
$fornitore = trim((string) ($row->ragione_sociale ?? ''));
|
|
if ($fornitore === '' && Schema::hasColumn('fornitori', 'denominazione')) {
|
|
$fornitore = trim((string) ($row->denominazione ?? ''));
|
|
}
|
|
$summary = [
|
|
'causale' => $causale !== '' ? $causale : 'Fattura ricevuta',
|
|
'fornitore' => $fornitore,
|
|
'totale' => (float) ($row->totale ?? 0),
|
|
'ritenuta' => (float) ($row->ritenuta_importo ?? 0),
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($summary['causale'] === '') {
|
|
$docType = trim((string) ($record->document_type ?? ''));
|
|
if ($docType !== '') {
|
|
$summary['causale'] = $docType;
|
|
}
|
|
}
|
|
|
|
$this->fatturaCache[$id] = $summary;
|
|
return $summary;
|
|
}
|
|
|
|
/**
|
|
* @return array{dare: float, avere: float}
|
|
*/
|
|
private function getMovimentiTotals(Registrazione $record): array
|
|
{
|
|
$id = (int) $record->id;
|
|
if (isset($this->movimentiTotaliCache[$id])) {
|
|
return $this->movimentiTotaliCache[$id];
|
|
}
|
|
|
|
$totali = ['dare' => 0.0, 'avere' => 0.0];
|
|
if (! Schema::hasTable('contabilita_movimenti')) {
|
|
return $totali;
|
|
}
|
|
|
|
$rows = DB::table('contabilita_movimenti')
|
|
->select('tipo', DB::raw('SUM(importo) as totale'))
|
|
->where('registrazione_id', $id)
|
|
->groupBy('tipo')
|
|
->get();
|
|
|
|
foreach ($rows as $row) {
|
|
$tipo = (string) ($row->tipo ?? '');
|
|
$val = (float) ($row->totale ?? 0);
|
|
if ($tipo === 'dare') {
|
|
$totali['dare'] = $val;
|
|
} elseif ($tipo === 'avere') {
|
|
$totali['avere'] = $val;
|
|
}
|
|
}
|
|
|
|
$this->movimentiTotaliCache[$id] = $totali;
|
|
return $totali;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function getBilancioData(): array
|
|
{
|
|
if ($this->bilancioCache !== null) {
|
|
return $this->bilancioCache;
|
|
}
|
|
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return $this->bilancioCache = [
|
|
'totale_dare' => 0.0,
|
|
'totale_avere' => 0.0,
|
|
'sezioni' => [],
|
|
];
|
|
}
|
|
|
|
$stabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $stabileId || ! Schema::hasTable('contabilita_movimenti') || ! Schema::hasTable('contabilita_registrazioni')) {
|
|
return $this->bilancioCache = [
|
|
'totale_dare' => 0.0,
|
|
'totale_avere' => 0.0,
|
|
'sezioni' => [],
|
|
];
|
|
}
|
|
|
|
$select = [
|
|
'm.conto_id',
|
|
'c.codice',
|
|
'c.denominazione',
|
|
'c.descrizione',
|
|
'c.tipo_conto',
|
|
DB::raw("SUM(CASE WHEN m.tipo = 'dare' THEN m.importo ELSE 0 END) as dare"),
|
|
DB::raw("SUM(CASE WHEN m.tipo = 'avere' THEN m.importo ELSE 0 END) as avere"),
|
|
];
|
|
|
|
$query = DB::table('contabilita_movimenti as m')
|
|
->join('contabilita_registrazioni as r', 'r.id', '=', 'm.registrazione_id')
|
|
->leftJoin('contabilita_piano_conti as c', 'c.id', '=', 'm.conto_id')
|
|
->where('r.stabile_id', $stabileId);
|
|
|
|
if (Schema::hasColumn('contabilita_registrazioni', 'deleted_at')) {
|
|
$query->whereNull('r.deleted_at');
|
|
}
|
|
|
|
$rows = $query
|
|
->select($select)
|
|
->groupBy('m.conto_id', 'c.codice', 'c.denominazione', 'c.descrizione', 'c.tipo_conto')
|
|
->get();
|
|
|
|
$totaleDare = 0.0;
|
|
$totaleAvere = 0.0;
|
|
|
|
$finanziari = [];
|
|
$variazioni = [];
|
|
$economici = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$dare = (float) ($row->dare ?? 0);
|
|
$avere = (float) ($row->avere ?? 0);
|
|
if (abs($dare) < 0.005 && abs($avere) < 0.005) {
|
|
continue;
|
|
}
|
|
|
|
$totaleDare += $dare;
|
|
$totaleAvere += $avere;
|
|
|
|
$codice = trim((string) ($row->codice ?? ''));
|
|
$desc = trim((string) ($row->denominazione ?? $row->descrizione ?? ''));
|
|
$label = $codice !== '' ? $codice : ('Conto #' . (int) ($row->conto_id ?? 0));
|
|
if ($desc !== '') {
|
|
$label .= ' - ' . $desc;
|
|
}
|
|
|
|
$item = [
|
|
'conto_id' => (int) ($row->conto_id ?? 0),
|
|
'label' => $label,
|
|
'dare' => $dare,
|
|
'avere' => $avere,
|
|
'saldo' => $dare - $avere,
|
|
];
|
|
|
|
$tipoConto = strtoupper(trim((string) ($row->tipo_conto ?? '')));
|
|
|
|
if (in_array($tipoConto, ['ATTIVO', 'PASSIVO'], true)) {
|
|
$finanziari[] = $item;
|
|
} elseif (in_array($tipoConto, ['RICAVO', 'COSTO'], true)) {
|
|
$economici[] = $item;
|
|
} else {
|
|
$variazioni[] = $item;
|
|
}
|
|
}
|
|
|
|
$this->bilancioCache = [
|
|
'totale_dare' => $totaleDare,
|
|
'totale_avere' => $totaleAvere,
|
|
'sezioni' => [
|
|
[
|
|
'titolo' => 'Conti finanziari (crediti/debiti)',
|
|
'righe' => $finanziari,
|
|
],
|
|
[
|
|
'titolo' => 'Variazioni finanziarie (entrate/uscite)',
|
|
'righe' => $variazioni,
|
|
],
|
|
[
|
|
'titolo' => 'Conti economici (costi/ricavi)',
|
|
'righe' => $economici,
|
|
],
|
|
],
|
|
];
|
|
|
|
return $this->bilancioCache;
|
|
}
|
|
|
|
private function buildMovimentoPayload(
|
|
int $registrazioneId,
|
|
int $stabileId,
|
|
int $contoId,
|
|
?PianoConti $conto,
|
|
string $tipo,
|
|
float $importo,
|
|
string $descrizione
|
|
): array {
|
|
$payload = [
|
|
'registrazione_id' => $registrazioneId,
|
|
'conto_id' => $contoId,
|
|
'tipo' => $tipo,
|
|
'importo' => $importo,
|
|
];
|
|
|
|
if (Schema::hasColumn('contabilita_movimenti', 'entry_id')) {
|
|
$payload['entry_id'] = $registrazioneId;
|
|
}
|
|
|
|
$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;
|
|
}
|
|
|
|
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)
|
|
->where('quarto_livello', $quarto);
|
|
|
|
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?->denominazione ?? ''));
|
|
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;
|
|
}
|
|
}
|