openstamanager/modules/interventi/modutil.php

408 lines
16 KiB
PHP
Executable File

<?php
/*
* OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
* Copyright (C) DevCode s.r.l.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
include_once __DIR__.'/../../core.php';
use Modules\Anagrafiche\Anagrafica;
use Modules\Articoli\Articolo as ArticoloOriginale;
use Modules\Emails\Mail;
use Modules\Emails\Template;
use Modules\Fatture\Components\Descrizione;
use Modules\Fatture\Components\Riga;
use Modules\Fatture\Fattura;
use Modules\Interventi\Components\Riga as RigaIntervento;
use Modules\Interventi\Components\Sessione;
use Modules\Interventi\Intervento;
use Modules\Iva\Aliquota;
use Util\Generator;
use Util\Ini;
/*
* Recupera il totale delle ore spese per un intervento.
*
* @param int $id_intervento
*
* @deprecated
*/
if (!function_exists('get_ore_intervento')) {
function get_ore_intervento($id_intervento)
{
$intervento = Intervento::find($id_intervento);
return $intervento->ore_totali;
}
}
/*
* Funzione per collegare gli articoli, usati in un intervento, ai rispettivi impianti.
*
* @param int $id_intervento
* @param int $id_impianto
* @param int $id_articolo
* @param int $qta
*
* @deprecated 2.4.25
*/
if (!function_exists('link_componente_to_articolo')) {
function link_componente_to_articolo($id_intervento, $id_impianto, $id_articolo, $qta)
{
if (empty($id_impianto) || empty($id_intervento)) {
return;
}
$dbo = database();
$intervento = Intervento::find($id_intervento);
// Data di inizio dell'intervento (data_richiesta in caso di assenza di sessioni)
$data = $intervento->inizio;
// Se l'articolo aggiunto è collegato a un componente, aggiungo il componente all'impianto selezionato
$componente_articolo = $dbo->fetchOne('SELECT `componente_filename`, `contenuto` FROM `mg_articoli` WHERE `id` = '.prepare($id_articolo));
if (!empty($componente_articolo) && !empty($componente_articolo['componente_filename'])) {
$contenuto_ini = Ini::read($componente_articolo['contenuto']);
$nome_componente = Ini::getValue($contenuto_ini, 'Nome');
$dati = [
'idimpianto' => $id_impianto,
'idintervento' => $id_intervento,
'nome' => $nome_componente,
'data' => $data,
'filename' => $componente_articolo['componente_filename'],
'contenuto' => $componente_articolo['contenuto'],
];
// Inserisco il componente tante volte quante la quantità degli articoli inseriti
for ($q = 0; $q < $qta; ++$q) {
$dbo->insert('my_impianto_componenti', $dati);
}
}
}
}
if (!function_exists('add_tecnico')) {
function add_tecnico($id_intervento, $idtecnico, $inizio, $fine, $idcontratto = null)
{
$intervento = Intervento::find($id_intervento);
$anagrafica = Anagrafica::find($idtecnico);
$sessione = Sessione::build($intervento, $anagrafica, $inizio, $fine);
// Notifica nuovo intervento al tecnico
if (setting('Notifica al tecnico l\'aggiunta della sessione nell\'attività')) {
if (!empty($anagrafica['email'])) {
$template = Template::where('name', 'Notifica intervento')->first();
if (!empty($template)) {
$mail = Mail::build(auth()->getUser(), $template, $id_intervento);
$mail->addReceiver($anagrafica['email']);
$mail->save();
flash()->info(tr('Notifica al tecnico aggiunta correttamente.'));
}
}
}
// Inserisco le righe aggiuntive previste dal tipo di intervento
$righe_aggiuntive = database()->fetchArray('SELECT * FROM in_righe_tipiinterventi WHERE id_tipointervento='.prepare($sessione->idtipointervento));
foreach ($righe_aggiuntive as $riga_aggiuntiva) {
$riga = RigaIntervento::build($intervento);
$riga->descrizione = $riga_aggiuntiva['descrizione'];
$riga->um = $riga_aggiuntiva['um'];
$riga->costo_unitario = $riga_aggiuntiva['prezzo_acquisto'];
$riga->setPrezzoUnitario($riga_aggiuntiva['prezzo_vendita'], $riga_aggiuntiva['idiva']);
$riga->qta = $riga_aggiuntiva['qta'];
$riga->save();
}
//Trigger aggiornamento intervento
$intervento->updated_at = date('Y-m-d H:i:s');
$intervento->save();
return true;
}
}
/*
* Calcola le ore presenti tra due date.
*
* @param string $orario_inizio
* @param string $orario_fine
*
* @return float
*
* @deprecated
*/
if (!function_exists('calcola_ore_intervento')) {
function calcola_ore_intervento($orario_inizio, $orario_fine)
{
$inizio = new DateTime($orario_inizio);
$diff = $inizio->diff(new DateTime($orario_fine));
$ore = $diff->i / 60 + $diff->h + ($diff->days * 24);
return $ore;
}
}
if (!function_exists('aggiungi_intervento_in_fattura')) {
function aggiungi_intervento_in_fattura($id_intervento, $id_fattura, $descrizione, $id_iva, $id_conto, $id_rivalsa_inps = false, $id_ritenuta_acconto = false, $calcolo_ritenuta_acconto = false)
{
$dbo = database();
$id_rivalsa_inps = $id_rivalsa_inps !== false ? $id_rivalsa_inps : setting('Cassa previdenziale predefinita');
$id_ritenuta_acconto = $id_ritenuta_acconto !== false ? $id_ritenuta_acconto : setting("Ritenuta d'acconto predefinita");
$calcolo_ritenuta_acconto = $calcolo_ritenuta_acconto !== false ? $calcolo_ritenuta_acconto : setting("Metodologia calcolo ritenuta d'acconto predefinito");
$fattura = Fattura::find($id_fattura);
$intervento = Intervento::find($id_intervento);
$codice = $intervento->codice;
// Riga di descrizione
$riga = Descrizione::build($fattura);
$riga->descrizione = $descrizione;
$riga->idintervento = $id_intervento;
$riga->save();
// Ore di lavoro raggruppate per costo orario
$sessioni = $intervento->sessioni;
if (empty($sessioni)) {
flash()->warning(tr("L'attività _NUM_ non ha sessioni di lavoro!", [
'_NUM_' => $codice,
]));
} else {
$decimals = setting('Cifre decimali per quantità');
$ore_di_lavoro = $sessioni->groupBy(fn ($item, $key) => $item['prezzo_orario'].'|'.$item['sconto_unitario'].'|'.$item['tipo_sconto']);
foreach ($ore_di_lavoro as $gruppo) {
$date = [];
$sessione = $gruppo->first();
$riga = Riga::build($fattura);
foreach ($gruppo as $sessione) {
$dateValue = date('d/m/Y', strtotime($sessione->orario_fine));
if (!in_array($dateValue, $date)) {
$date[] = $dateValue;
}
}
$riga->descrizione = tr("Ore di lavoro dell'attività _NUM_ del _DATE_", [
'_NUM_' => $codice,
'_DATE_' => implode(', ', $date),
]);
$riga->idintervento = $id_intervento;
$riga->um = 'ore';
$riga->id_iva = $id_iva;
$riga->idconto = $id_conto;
$riga->calcolo_ritenuta_acconto = $calcolo_ritenuta_acconto;
$riga->id_ritenuta_acconto = $id_ritenuta_acconto;
$riga->id_rivalsa_inps = $id_rivalsa_inps;
$riga->prezzo_unitario = $sessione->prezzo_orario;
$riga->costo_unitario = $sessione->prezzo_ore_unitario_tecnico;
// Calcolo lo sconto unitario della sessione in base all'impostazione sui prezzi ivati
$iva = Aliquota::find($sessione->id_iva);
if ($sessione->tipo_sconto == 'UNT' && setting('Utilizza prezzi di vendita comprensivi di IVA')) {
$sconto_unitario = $sessione->sconto_unitario + (($sessione->sconto_unitario * $iva->percentuale) / 100);
} else {
$sconto_unitario = $sessione->sconto_unitario;
}
$riga->setSconto($sconto_unitario, $sessione->tipo_sconto);
$qta_gruppo = $gruppo->sum('ore');
$riga->qta = round($qta_gruppo, $decimals);
// Riferimento al documento di origine
$riga->original_document_type = $intervento::class;
$riga->original_document_id = $intervento->id;
$riga->save();
}
// Diritti di chiamata raggruppati per costo
$diritti_chiamata = $sessioni->where('prezzo_diritto_chiamata', '>', 0)->groupBy(fn ($item, $key) => $item['prezzo_diritto_chiamata']);
foreach ($diritti_chiamata as $gruppo) {
$date = [];
$diritto_chiamata = $gruppo->first();
$riga = Riga::build($fattura);
foreach ($gruppo as $sessione) {
$dateValue = date('d/m/Y', strtotime($sessione->orario_fine));
if (!in_array($dateValue, $date)) {
$date[] = $dateValue;
}
}
$riga->descrizione = tr("Diritto di chiamata dell'attività _NUM_ del _DATE_", [
'_NUM_' => $codice,
'_DATE_' => implode(', ', $date),
]);
$riga->idintervento = $id_intervento;
// $riga->um = 'ore';
$riga->id_iva = $id_iva;
$riga->idconto = $id_conto;
$riga->calcolo_ritenuta_acconto = $calcolo_ritenuta_acconto;
$riga->id_ritenuta_acconto = $id_ritenuta_acconto;
$riga->id_rivalsa_inps = $id_rivalsa_inps;
$riga->prezzo_unitario = $diritto_chiamata->prezzo_diritto_chiamata;
$riga->costo_unitario = $sessione->prezzo_dirittochiamata_tecnico;
$riga->qta = $gruppo->count();
// Riferimento al documento di origine
$riga->original_document_type = $intervento::class;
$riga->original_document_id = $intervento->id;
$riga->save();
}
// Viaggi raggruppati per costo
$viaggi = $sessioni->where('prezzo_km_unitario', '>', 0)->groupBy(fn ($item, $key) => $item['prezzo_km_unitario'].'|'.$item['scontokm_unitario'].'|'.$item['tipo_scontokm']);
foreach ($viaggi as $gruppo) {
$date = [];
$qta_trasferta = $gruppo->sum('km');
if ($qta_trasferta == 0) {
continue;
}
foreach ($gruppo as $sessione) {
$dateValue = date('d/m/Y', strtotime($sessione->orario_fine));
if (!in_array($dateValue, $date)) {
$date[] = $dateValue;
}
}
$viaggio = $gruppo->first();
$riga = Riga::build($fattura);
$riga->descrizione = tr("Trasferta dell'attività _NUM_ del _DATE_", [
'_NUM_' => $codice,
'_DATE_' => implode(', ', $date),
]);
$riga->idintervento = $id_intervento;
$riga->um = 'km';
$riga->id_iva = $id_iva;
$riga->idconto = $id_conto;
$riga->calcolo_ritenuta_acconto = $calcolo_ritenuta_acconto;
$riga->id_ritenuta_acconto = $id_ritenuta_acconto;
$riga->id_rivalsa_inps = $id_rivalsa_inps;
$riga->prezzo_unitario = $viaggio->prezzo_km_unitario;
$riga->costo_unitario = $sessione->prezzo_km_unitario_tecnico;
$riga->setSconto($viaggio->scontokm_unitario, $viaggio->tipo_scontokm);
// Riferimento al documento di origine
$riga->original_document_type = $intervento::class;
$riga->original_document_id = $intervento->id;
$riga->qta = $qta_trasferta;
$riga->save();
}
}
// Articoli, righe, sconti e descrizioni collegati all'intervento
$righe = $intervento->getRighe();
foreach ($righe as $riga) {
$qta = $riga->qta;
$copia = $riga->copiaIn($fattura, $qta);
$copia->id_conto = $id_conto;
$copia->calcolo_ritenuta_acconto = $calcolo_ritenuta_acconto;
$copia->id_ritenuta_acconto = $id_ritenuta_acconto;
$copia->id_rivalsa_inps = $id_rivalsa_inps;
// Aggiornamento seriali dalla riga dell'ordine
if ($copia->isArticolo()) {
$copia->serials = $riga->serials;
$articolo = ArticoloOriginale::find($copia->idarticolo);
$copia->id_conto = ($articolo->idconto_vendita ?: $id_conto);
}
$copia->save();
}
// Ricalcolo inps, ritenuta e bollo
ricalcola_costiagg_fattura($id_fattura);
// Metto l'intervento in stato "Fatturato"
$dbo->query("UPDATE `in_interventi` SET `idstatointervento`=(SELECT `id` FROM `in_statiintervento` WHERE `codice`='FAT') WHERE `id`=".prepare($id_intervento));
}
}
/*
* Verifica che il numero_esterno della fattura indicata sia correttamente impostato, a partire dai valori delle fatture ai giorni precedenti.
* Restituisce il numero_esterno mancante in caso di numero errato.
*
* @return bool|string
*/
if (!function_exists('verifica_numero_intervento')) {
function verifica_numero_intervento(Intervento $intervento, $id_segment)
{
if (empty($intervento->codice)) {
return null;
}
$data = $intervento->data_richiesta;
$documenti = Intervento::whereDate('data_richiesta', '=', $data->format('Y-m-d'))
->get();
// Recupero maschera per questo segmento
$maschera = Generator::getMaschera($id_segment);
if ((!str_contains($maschera, 'YYYY')) or (!str_contains($maschera, 'yy'))) {
$ultimo = Generator::getPreviousFrom($maschera, 'in_interventi', 'codice', [
'DATE(data_richiesta) < '.prepare($data->format('Y-m-d')),
'YEAR(data_richiesta) = '.prepare($data->format('Y')),
], $data);
} else {
$ultimo = Generator::getPreviousFrom($maschera, 'in_interventi', 'codice', [
'DATE(data_richiesta) < '.prepare($data->format('Y-m-d')),
]);
}
do {
$numero = Generator::generate($maschera, $ultimo, 1, Generator::dateToPattern($data), $data);
$filtered = $documenti->reject(fn ($item, $key) => $item->codice == $numero);
if ($documenti->count() == $filtered->count()) {
return $numero;
}
$documenti = $filtered;
$ultimo = $numero;
} while ($numero != $intervento->codice);
return null;
}
}