Non puoi selezionare più di 25 argomenti
Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
2335 righe
76 KiB
2335 righe
76 KiB
<?php |
|
/** |
|
* Gestore delle API ANAC NPA |
|
*/ |
|
require_once "api.trait.php"; |
|
|
|
trait NPAAPI |
|
{ |
|
use API; |
|
|
|
/** |
|
* Retrieves endpoint settings for the given card |
|
* |
|
* @param mixed $card |
|
* @return string|null |
|
*/ |
|
public function getCardEndpoint(?String $card = null): ?string |
|
{ |
|
|
|
if (empty($card)) { |
|
$card = $this->scheda["id_scheda"]; |
|
} |
|
foreach ($this->api_config as $key => $config) { |
|
|
|
if (!empty($config["schede"]) && in_array($card, $config["schede"])) { |
|
|
|
return $key; |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
} |
|
|
|
public function cardCanDoRequest(?array &$reason = null) |
|
{ |
|
$method = null; |
|
if (!empty($this->scheda["codice"])) { |
|
$apiEndpoint = $this->getCardEndpoint(); |
|
if ($this->checkIfCardNeedsXMLDocument() && ($this->scheda["stato"] == 10 || ($this->scheda["stato"] > 10 && $this->scheda["stato"] <= 40 && $this->scheda["errore"] == "SI"))) { |
|
$method = "create"; |
|
} elseif (($this->scheda["stato"] == 20 || $this->scheda["stato"] == 40) && $this->scheda["errore"] == "NO") { |
|
$method = "confirm"; |
|
} elseif (($this->scheda["stato"] == 50 || $this->scheda["stato"] == 90) && $this->scheda["errore"] == "NO" && self::hasToBePublished($this->scheda)) { |
|
$method = "publish"; |
|
$apiEndpoint = "pubblicazioneAvvisi"; |
|
} |
|
} |
|
|
|
if ($method === null) $apiEndpoint = null; |
|
return $this->canDoRequest($reason, $apiEndpoint, $method); |
|
} |
|
|
|
/** |
|
* Risincronizza la scheda con i server ANAC |
|
* |
|
* @return boolean |
|
*/ |
|
public function resyncScheda(?string &$message = null): bool { |
|
$message = ""; |
|
$readData = $this->read(); |
|
$scheda = $readData->_scheda->body ?? null; |
|
$uuid = $readData->_scheda->_idScheda ?? null; |
|
|
|
$message = "Impossibile trovare i dati della scheda"; |
|
if(empty($scheda) || empty($uuid)) return false; |
|
|
|
$scheda = json_decode(json_encode($scheda), true); |
|
|
|
// Niente da aggiornare |
|
if(empty($scheda["anacForm"])) return true; |
|
|
|
$schedaAttuale = $this->getCardData(); |
|
$schedaAttuale["anacForm"] = $scheda["anacForm"]; |
|
|
|
$this->updateScheda(["dati" => json_encode($schedaAttuale, JSON_PRETTY_PRINT), "uuid" => $uuid]); |
|
|
|
return true; |
|
} |
|
|
|
/** |
|
* Associa al form attuale gli XML necessari per la richiesta |
|
* |
|
* @param array $data La richiesta attuale |
|
* @param string|null $reason In caso di fallimento viene popolato con il messaggio d'errore |
|
* @return boolean True se va con successo |
|
*/ |
|
function attachXMLDocumentToRequestBody(array &$data, ?string &$reason = null): bool |
|
{ |
|
|
|
$this->checkIfCardNeedsXMLDocument(); |
|
|
|
if ($this->check["eform"]["required"]) { |
|
|
|
$data["eform"] = null; |
|
if (!empty($this->check["eform"]["ted"])) { |
|
$guue = $this->check["eform"]["ted"]; |
|
|
|
if (!$guue->isReadyForAnac()) { |
|
$reason = "La scheda n°{$guue->info["codice"]} deve essere in stato 'Pronto per ANAC' prima di essere trasmessa."; |
|
return false; |
|
} |
|
$data["eform"] = base64_encode($guue->toXML()); |
|
} |
|
} |
|
|
|
if ($this->check["espd"]["required"]) { |
|
|
|
$data["espd"] = null; |
|
if (!empty($this->check["espd"]["xml"])) { |
|
|
|
$data["espd"] = base64_encode($this->check["espd"]["xml"]); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/** |
|
* Rretrieves the codes associated with cost centers |
|
* |
|
* @param String $identifier |
|
* |
|
* @return Array |
|
*/ |
|
public static function getCodiciCentriDiCostoFromANAC_Cache(String $identifier): ?array |
|
{ |
|
global $pdo; |
|
// Nota: su necessità questo metodo può diventare statico |
|
$identifier = trim(strtoupper($identifier)); |
|
$centri = $pdo->go( |
|
"SELECT data FROM b_centri_costo_ws_cache WHERE cf = :cf AND created_at > DATE_SUB(NOW(), INTERVAL 1 DAY)", |
|
["cf" => $identifier] |
|
)->fetch(PDO::FETCH_ASSOC); |
|
|
|
|
|
if (!is_array($centri) || empty($centri)) { |
|
return null; |
|
} |
|
|
|
return json_decode($centri["data"], TRUE); |
|
} |
|
/** |
|
* Rretrieves the codes associated with cost centers |
|
* |
|
* @param String $identifier |
|
* |
|
* @return Array |
|
*/ |
|
public function getCodiciCentriDiCostoFromAnac(String $identifier): ?array |
|
{ |
|
$cachedResponse = $this->getCodiciCentriDiCostoFromANAC_Cache($identifier); |
|
if (!empty($cachedResponse)) return $cachedResponse; |
|
|
|
$apiConfig = $this->api_config["ausaController"]; |
|
|
|
$claims = $this->getAnacJwsCustomClaims( |
|
$apiConfig["endpoint"], |
|
$this->getUUID(), |
|
"", |
|
DelegheRoles::UNAUTHORIZED |
|
); |
|
|
|
$response = $this->apiCall("ausaController", "getBy", ["codiceFiscale" => $identifier], $claims); |
|
if (is_json($response)) { |
|
|
|
$response = json_decode($response); |
|
|
|
if ($response->status == "OK" && !empty($response->items)) { |
|
|
|
$centri = []; |
|
foreach ($response->items as $item) { |
|
if (!empty($item->scheda->stazioneAppaltante->centriDiCosto)) { |
|
|
|
foreach ($item->scheda->stazioneAppaltante->centriDiCosto as $center) { |
|
if(!empty($center->idCentroDiCosto)) { |
|
$centri[$center->idCentroDiCosto] = $center->denominazioneCentroDiCosto ?? "Centro di costo"; |
|
} |
|
} |
|
} |
|
} |
|
if(!empty($centri)) { |
|
global $pdo; |
|
$pdo->go( |
|
"INSERT INTO b_centri_costo_ws_cache (cf, data) VALUES (:cf, :data) ON DUPLICATE KEY UPDATE data = :data, created_at = NOW()", |
|
[ |
|
":cf" => $identifier, |
|
":data" => json_encode($centri), |
|
] |
|
); |
|
} |
|
return $centri; |
|
} |
|
} |
|
|
|
|
|
return []; |
|
} |
|
/** |
|
* Verifica l'esito di una modifica |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return bool|null |
|
*/ |
|
public function checkModify(?string &$message = null): ?bool |
|
{ |
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
// Istanziamo l'NPA rettifica |
|
$npa_originale = self::init(null, $this->scheda["codice_modifica"]); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di rettifica in attesa |
|
$modifica = $this->getLastOperation(null, "MODIFICA_AVVISO", NULL); |
|
|
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
if(empty($modifica)) { |
|
$modifica = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "MODIFICA_AVVISO", |
|
]; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione", "esito", "risultato"]; |
|
} else { |
|
$salva->operazione = "UPDATE"; |
|
$salva->expect = ["codice", "esito", "risultato"]; |
|
} |
|
|
|
$operazione = "AV_MOD"; |
|
|
|
// Andiamo a chiedere al server lo stato della conferma |
|
$result = $npa_originale->resultNew($operazione); |
|
|
|
if (!empty($result)) { |
|
if ($result->status == 200) { |
|
// Esito non trovato |
|
if (empty($result->listaEsiti)) { |
|
$modifica["risultato"] = serialize([]); |
|
$modifica["esito"] = "KO"; |
|
$salva->oggetto = $modifica; |
|
if ($salva->save()) { |
|
$message = __("Impossibile verificare l'esito di modifica. Provare ad inviare nuovamente la modifica. "); |
|
$this->updateScheda(["errore" => "SI", "stato" => 0]); |
|
} |
|
return false; |
|
} |
|
|
|
$esito = reset($result->listaEsiti); // Ci interessa solo il primo |
|
|
|
if ($esito->esito->codice == "KO") { |
|
$modifica["esito"] = "KO"; |
|
$modifica["risultato"] = serialize($esito); |
|
|
|
$salva->oggetto = $modifica; |
|
$message = __("Vi è stato un problema con la modifica dell'avviso. "); |
|
|
|
// Impostiamo lo stato di errore |
|
if ($salva->save()) { |
|
// Appendo al messaggio per distinguere il caso in cui non sono state salvate le info |
|
$message .= " " . __("L'errore sarà disponibile al ricaricamento della pagina."); |
|
$this->updateScheda(["errore" => "SI", "stato" => 0]); |
|
} |
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
|
|
if ($esito->esito->codice == "WT") { |
|
$message = __("INFO - L'avviso è in corso di modifica. Questo processo solitamente richiede diverse ore.<br>Verifica l'esito nuovamente più tardi."); |
|
|
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
|
|
if ($esito->esito->codice == "OK") { |
|
|
|
$modifica["esito"] = "OK"; |
|
$modifica["risultato"] = serialize($esito); |
|
$salva->oggetto = $modifica; |
|
|
|
if ($salva->save()) { |
|
// Per via di una svista di ANAC, l'esito OK |
|
// può non contenere i nuovi id, quindi ce li andiamo a cercare... |
|
$esito_con_nuovi_id = null; |
|
foreach ($result->listaEsiti as $_esito) { |
|
if (!empty($_esito->idNuovaScheda)) { |
|
$esito_con_nuovi_id = $_esito; |
|
break; |
|
} |
|
} |
|
if ($esito_con_nuovi_id !== null) { |
|
$info = new stdClass; |
|
if (!empty($npa_originale->scheda["info"])) { |
|
$info = unserialize($npa_originale->scheda["info"]); |
|
} |
|
$info->idNuovaScheda = $esito_con_nuovi_id->idNuovaScheda; |
|
$info->idNuovoAvviso = $esito_con_nuovi_id->idNuovoAvviso; |
|
$npa_originale->updateScheda(["info" => serialize($info)]); |
|
$this->updateScheda(["uuid" => $info->idNuovaScheda , "info" => serialize($info)]); |
|
} else { |
|
$message = __("I server ANAC hanno processato in maniera errata questa modifica."); |
|
$this->updateScheda(["stato" => 100, "errore" => 'SI']); |
|
return false; |
|
} |
|
// Imposta la vecchia scheda su "Modificata" |
|
$npa_originale->updateScheda(["stato" => -10]); |
|
|
|
|
|
// Cambiamo lo stato della scheda GUUE se presente |
|
$this->publishGUUEOnSuccessfulPublication(); |
|
$this->updateScheda(["stato" => 100, "errore" => 'NO']); |
|
$metadata = $this->metadataSchede[$this->scheda["id_scheda"]]; |
|
if($metadata["lotto"] == false && $metadata["categoria"] != "pianificazione") { |
|
return $this->getCIG($message); |
|
} |
|
|
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
$message = __("Errore durante la verifica dello stato di modifica"); |
|
return false; |
|
} |
|
/** |
|
* Invia una modifica avviso o ne verifica lo stato |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return Bool|null |
|
*/ |
|
public function modify(?string &$message = null): ?Bool |
|
{ |
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione"]; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Istanziamo l'NPA originale |
|
$npa_originale = self::init(null, $this->scheda["codice_modifica"]); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di rettifica in attesa |
|
$modifica = $this->getLastOperation(null, "MODIFICA_AVVISO", NULL); |
|
// Se c'è |
|
if (!empty($modifica)) { |
|
return $this->checkModify($message); |
|
} |
|
|
|
$apiEndpoint = "pubblicazioneAvvisi"; |
|
|
|
$body = $this->getCardData(); |
|
|
|
$this->organizeRepeteableGroups($body); |
|
$XMLattachedSuccessfully = $this->attachXMLDocumentToRequestBody($body, $xml_message); |
|
|
|
if (!$XMLattachedSuccessfully) { |
|
$message = __($xml_message); |
|
return false; |
|
} |
|
|
|
$apiMethod = "update"; |
|
if (!isset($npa_originale->scheda) || empty($npa_originale->scheda["info"])) { |
|
$message = __("Errore nella scheda originale. Contattare l'Help Desk tecnico.") . " (err: info)"; |
|
return false; |
|
} |
|
// Otteniamo le info da cui ottenere gli UUID |
|
$modifica_info = unserialize($npa_originale->scheda["info"]); |
|
|
|
if (empty($modifica_info->idAvviso)) { |
|
$message = __("Errore nella scheda originale. Contattare l'Help Desk tecnico.") . " (err: idAvviso)"; |
|
return false; |
|
} |
|
|
|
if (empty($this->fascicolo["id_appalto"]) && empty($this->fascicolo["id_pianificazione"])) { |
|
$message = __("Errore nella scheda originale. Contattare l'Help Desk tecnico.") . " (err: idAppalto/idPianificazione)"; |
|
return false; |
|
} |
|
|
|
|
|
$request = [ |
|
"scheda" => [ |
|
"codice" => [ |
|
"idTipologica" => "codiceScheda", |
|
"codice" => $this->scheda["id_scheda"] |
|
], |
|
"versione" => str_replace("v", "", $this->version), |
|
"_stato" => [ |
|
"idTipologica" => "statoScheda", |
|
"codice" => "CONF" |
|
], |
|
"_dataCreazione" => date("c", strtotime($this->scheda["timestamp_creazione"])), |
|
"body" => $body |
|
] |
|
]; |
|
|
|
$request["idAvviso"] = $modifica_info->idAvviso; |
|
if (empty($this->fascicolo["id_appalto"])) { |
|
$request["idPianificazione"] = $this->fascicolo["id_pianificazione"]; |
|
} else { |
|
$request["idAppalto"] = $this->fascicolo["id_appalto"]; |
|
} |
|
|
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall($apiEndpoint, $apiMethod, $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "MODIFICA_AVVISO" |
|
]; |
|
|
|
// Verifichiamo la risposta |
|
|
|
// Se non ci viene fornita una risposta con successo |
|
if ($result->status !== 200) { |
|
if(isset($result->errori[0]->codice)) { |
|
$error_codes = array_column(json_decode(json_encode($result->errori), true), 'codice'); |
|
if (in_array('ERR50', $error_codes) || in_array('ERR44', $error_codes)) { |
|
if($this->checkModify($message)) { |
|
return true; |
|
} |
|
} |
|
} |
|
// Aggiorniamo lo status |
|
$salva->expect = array_merge($salva->expect, ["esito", "risultato"]); |
|
$salva->oggetto = array_merge($salva->oggetto, ["esito" => "KO", "risultato" => serialize($result)]); |
|
|
|
// Settiamo errore su SI |
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "SI", "stato" => 20]); |
|
} |
|
|
|
$message = __("Errore in fase di modifica della scheda. Al ricaricamento della pagina verifica gli Avvisi."); |
|
return false; |
|
} |
|
|
|
// Rettifica andata con successo |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "NO", "stato" => 90]); |
|
return true; |
|
} |
|
|
|
// Fallback generico |
|
return false; |
|
} |
|
|
|
/** |
|
* Verifica l'esito di una rettifica |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return bool|null |
|
*/ |
|
public function checkRectify(?string &$message = null): ?bool |
|
{ |
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
// Istanziamo l'NPA rettifica |
|
$npa_rettifica = self::init(null, $this->scheda["codice_rettifica"]); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di rettifica in attesa |
|
$rettifica = $this->getLastOperation(null, "RETTIFICA", NULL); |
|
|
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
if(empty($rettifica)) { |
|
$rettifica = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "RETTIFICA", |
|
]; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione", "esito", "risultato"]; |
|
} else { |
|
$salva->operazione = "UPDATE"; |
|
$salva->expect = ["codice", "esito", "risultato"]; |
|
} |
|
|
|
$operazione = "AV_RETT"; |
|
|
|
// Andiamo a chiedere al server lo stato della conferma |
|
$result = $npa_rettifica->resultNew($operazione); |
|
if (!empty($result)) { |
|
if ($result->status == 200) { |
|
// Esito non trovato |
|
if (empty($result->listaEsiti)) { |
|
$rettifica["risultato"] = serialize([]); |
|
$rettifica["esito"] = "KO"; |
|
$salva->oggetto = $rettifica; |
|
if ($salva->save()) { |
|
$message = __("Impossibile verificare l'esito di rettifica. Provare ad inviare nuovamente la rettifica. "); |
|
$this->updateScheda(["errore" => "SI", "stato" => 0]); |
|
} |
|
return false; |
|
} |
|
|
|
$wtResult = null; |
|
$okResult = null; |
|
$koResult = null; |
|
$lastResult = $result->listaEsiti[0]; |
|
|
|
foreach($result->listaEsiti as $esito) { |
|
switch($esito->esito->codice) { |
|
case "OK": |
|
if($okResult === null) $okResult = $esito; |
|
break; |
|
case "KO": |
|
if($koResult === null) $koResult = $esito; |
|
break; |
|
case "WT": |
|
if($wtResult === null) $wtResult = $esito; |
|
break; |
|
} |
|
} |
|
// Fallito / Non valido |
|
if(empty($okResult) && empty($wtResult) || $lastResult == $koResult) { |
|
|
|
// Se abbiamo un errore 44 e c'è ancora un WT vuol dire che c'è un accavallamento di rettifiche, quindi non falliamo |
|
$errorCode = $koResult->errori[0]->codice ?? null; |
|
if(!($errorCode == "ERR44" && !empty($wtResult))) { |
|
$rettifica["esito"] = "KO"; |
|
|
|
if(!empty($koResult)) |
|
$rettifica["risultato"] = serialize($koResult); |
|
|
|
$salva->oggetto = $rettifica; |
|
$message = __("Vi è stato un problema con la rettifica della scheda. "); |
|
|
|
// Impostiamo lo stato di errore |
|
if ($salva->save()) { |
|
// Appendo al messaggio per distinguere il caso in cui non sono state salvate le info |
|
$message .= " " . __("L'errore sarà disponibile al ricaricamento della pagina."); |
|
$this->updateScheda(["errore" => "SI", "stato" => 0]); |
|
} |
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
|
|
} |
|
// Successo |
|
if(!empty($okResult)) { |
|
$rettifica["esito"] = "OK"; |
|
$rettifica["risultato"] = serialize($okResult); |
|
$salva->oggetto = $rettifica; |
|
|
|
if ($salva->save()) { |
|
// Per via di una svista di ANAC, l'esito OK |
|
// può non contenere i nuovi id, quindi ce li andiamo a cercare... |
|
$esito_con_nuovi_id = null; |
|
foreach ($result->listaEsiti as $_esito) { |
|
if (!empty($_esito->idNuovaScheda)) { |
|
$esito_con_nuovi_id = $_esito; |
|
break; |
|
} |
|
} |
|
if ($esito_con_nuovi_id !== null) { |
|
$info = new stdClass; |
|
if (!empty($npa_rettifica->scheda["info"])) { |
|
$info = unserialize($npa_rettifica->scheda["info"]); |
|
} |
|
$info->idNuovaScheda = $esito_con_nuovi_id->idNuovaScheda; |
|
$info->idNuovoAvviso = $esito_con_nuovi_id->idNuovoAvviso; |
|
$npa_rettifica->updateScheda(["info" => serialize($info)]); |
|
$this->updateScheda(["uuid" => $info->idNuovaScheda,"info" => serialize($info)]); |
|
} else { |
|
$message = __("I server ANAC hanno processato in maniera errata questa rettifica."); |
|
$this->updateScheda(["stato" => 100, "errore" => 'SI']); |
|
return false; |
|
} |
|
// Imposta la vecchia scheda su "Rettificata" |
|
$npa_rettifica->updateScheda(["stato" => -20]); |
|
|
|
|
|
// Cambiamo lo stato della scheda GUUE se presente |
|
$this->publishGUUEOnSuccessfulPublication(); |
|
$this->updateScheda(["stato" => 100, "errore" => 'NO']); |
|
return true; |
|
} |
|
} |
|
// In attesa |
|
$this->updateScheda(["errore" => "NO", "stato" => 90]); |
|
$message = __("INFO - L'avviso è in corso di rettifica. Questo processo di solito richiede oltre 24 ore.<br>Verifica l'esito nuovamente più tardi."); |
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
} |
|
$message = __("Errore durante la verifica dello stato di rettifica"); |
|
return false; |
|
} |
|
/** |
|
* Invia una rettifica o ne verifica lo stato |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return Bool|null |
|
*/ |
|
public function rectify(?string &$message = null): ?Bool |
|
{ |
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione"]; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Istanziamo l'NPA rettifica |
|
$npa_rettifica = self::init(null, $this->scheda["codice_rettifica"]); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di rettifica in attesa |
|
$rettifica = $this->getLastOperation(null, "RETTIFICA", NULL); |
|
|
|
// Se c'è |
|
if (!empty($rettifica)) { |
|
return $this->checkRectify($message); |
|
} |
|
|
|
$apiEndpoint = "pubblicazioneAvvisi"; |
|
$schedaApiEndpoint = $this->getCardEndpoint(); |
|
|
|
$body = $this->getCardData(); |
|
|
|
$this->organizeRepeteableGroups($body); |
|
$XMLattachedSuccessfully = $this->attachXMLDocumentToRequestBody($body, $xml_message); |
|
|
|
if (!$XMLattachedSuccessfully) { |
|
$message = __($xml_message); |
|
return false; |
|
} |
|
|
|
$apiMethod = "amend"; |
|
if (!isset($npa_rettifica->scheda) || empty($npa_rettifica->scheda["info"])) { |
|
$message = __("Errore nella scheda originale. Contattare l'Help Desk tecnico.") . " (err: info)"; |
|
return false; |
|
} |
|
// Otteniamo le info da cui ottenere gli UUID |
|
$rettifica_info = unserialize($npa_rettifica->scheda["info"]); |
|
|
|
if (empty($rettifica_info->idAvviso)) { |
|
$message = __("Errore nella scheda originale. Contattare l'Help Desk tecnico.") . " (err: idAvviso)"; |
|
return false; |
|
} |
|
|
|
if (empty($this->fascicolo["id_appalto"]) && empty($this->fascicolo["id_pianificazione"])) { |
|
$message = __("Errore nella scheda originale. Contattare l'Help Desk tecnico.") . " (err: idAppalto/idPianificazione)"; |
|
return false; |
|
} |
|
|
|
|
|
$request = [ |
|
"scheda" => [ |
|
"codice" => [ |
|
"idTipologica" => "codiceScheda", |
|
"codice" => $this->scheda["id_scheda"] |
|
], |
|
"versione" => str_replace("v", "", $this->version), |
|
"_stato" => [ |
|
"idTipologica" => "statoScheda", |
|
"codice" => "CONF" |
|
], |
|
"_dataCreazione" => date("c", strtotime($this->scheda["timestamp_creazione"])), |
|
"body" => $body |
|
] |
|
]; |
|
|
|
$request["idAvviso"] = $npa_rettifica->getANACNoticeID(); |
|
if (empty($this->fascicolo["id_appalto"])) { |
|
$request["idPianificazione"] = $this->fascicolo["id_pianificazione"]; |
|
} else { |
|
$request["idAppalto"] = $this->fascicolo["id_appalto"]; |
|
} |
|
|
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall($apiEndpoint, $apiMethod, $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "RETTIFICA" |
|
]; |
|
|
|
// Verifichiamo la risposta |
|
|
|
// Se non ci viene fornita una risposta con successo |
|
if ($result->status !== 200) { |
|
if(isset($result->errori[0]->codice)) { |
|
$error_codes = array_column(json_decode(json_encode($result->errori), true), 'codice'); |
|
if (in_array('ERR50', $error_codes) || in_array('ERR44', $error_codes)) { |
|
if($this->checkRectify($message)) { |
|
return true; |
|
} |
|
if(str_starts_with($message, "INFO -")) { |
|
return false; |
|
} |
|
} |
|
} |
|
// Aggiorniamo lo status |
|
$salva->expect = array_merge($salva->expect, ["esito", "risultato"]); |
|
$salva->oggetto = array_merge($salva->oggetto, ["esito" => "KO", "risultato" => serialize($result)]); |
|
|
|
// Settiamo errore su SI |
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "SI", "stato" => 20]); |
|
} |
|
|
|
$message = __("Errore in fase di rettifica della scheda. Al ricaricamento della pagina verifica gli Avvisi."); |
|
return false; |
|
} |
|
|
|
// Rettifica andata con successo |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "NO", "stato" => 90]); |
|
return true; |
|
} |
|
|
|
// Fallback generico |
|
return false; |
|
} |
|
/** |
|
* Elimina l'avviso |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return ?Bool |
|
*/ |
|
public function deleteNotice(?string &$message = null): ?Bool |
|
{ |
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
global $config; |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "ELIMINA_AVVISO" |
|
]; |
|
$salva->expect = ["codice_scheda", "operazione"]; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di pubblicazione in attesa |
|
$elimina = $this->getLastOperation(null, "ELIMINA_AVVISO", NULL); |
|
|
|
// Se c'è |
|
if (!empty($elimina)) { |
|
|
|
$salva->operazione = "UPDATE"; |
|
$salva->expect = ["codice", "esito", "risultato"]; |
|
|
|
// Andiamo a chiedere al server lo stato della pubblicazione |
|
$result = $this->result("AV_SOSP"); |
|
if (!empty($result)) { |
|
|
|
if ($result->status == 200) { |
|
if (empty($result->listaEsiti)) { |
|
$message = __("Impossibile verificare l'esito dell'operazione. Contatta l'Help Desk tecnico"); |
|
return false; |
|
} |
|
|
|
$result = end($result->listaEsiti); |
|
if ($result->esito->codice == "WT") { |
|
$message = __("INFO - La scheda non è ancora stata cancellata. Ricontrolla più tardi."); |
|
return false; |
|
} |
|
// Cancellazione fallita |
|
if ($result->esito->codice == "KO") { |
|
|
|
$elimina["esito"] = "KO"; |
|
$elimina["risultato"] = serialize($result); |
|
|
|
$salva->oggetto = $elimina; |
|
$message = __("Vi è stato un problema con la cancellazione dell'avviso."); |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["pending_cancellazione" => "N"]); |
|
} |
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
|
|
if ($result->esito->codice == "OK") { |
|
|
|
$elimina["esito"] = "OK"; |
|
$elimina["risultato"] = serialize($result); |
|
$salva->oggetto = $elimina; |
|
if ($salva->save()) { |
|
$this->resetGUUEonSoftDelete(); |
|
if (count($this->getCards(false)) === 1) { |
|
// Creiamo una revisione del fascicolo |
|
$this->createDossierRevision(); |
|
} |
|
$this->updateScheda(["soft_delete" => "S"]); |
|
// Verifichiamo che l'appalto ora sia vuoto |
|
|
|
|
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
$elimina["esito"] = "KO"; |
|
$elimina["risultato"] = serialize($result); |
|
|
|
$salva->oggetto = $elimina; |
|
if ($salva->save()) { |
|
$this->updateScheda(["pending_cancellazione" => "N"]); |
|
} |
|
|
|
$message = __("Errore durante la verifica dello stato di cancellazione"); |
|
return false; |
|
} |
|
|
|
$pubblicazioneAvvisi = $this->api_config["pubblicazioneAvvisi"]; |
|
if (empty($pubblicazioneAvvisi)) { |
|
$message .= __LINE__; // Segnaposto per trovare questo errore |
|
return false; |
|
} |
|
|
|
$info = unserialize($this->scheda["info"]); |
|
|
|
// Verifichiamo sia popolato l'id avviso |
|
if (empty($info->idAvviso)) { |
|
$message = __("Problema interno con la cancellazione. Contattare l'Help Desk tecnico."); |
|
|
|
$today = date("Ymd"); |
|
error_log("[" . date('Y-m-d H:i:s') . "] - {$_SESSION["ente"]->getInfo()["dominio"]}" . PHP_EOL . |
|
"\t" . __FUNCTION__ . PHP_EOL . |
|
"\tErrore idAvviso inesistente " . PHP_EOL, 3, "{$config["npaFolder"]}/{$today}.log"); |
|
return null; |
|
} |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
$request["idAvviso"] = $this->getANACNoticeID(); |
|
|
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall("pubblicazioneAvvisi", "delete", $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
// Verifichiamo la risposta |
|
if (!empty($result->status) && ($result->status == 200)) { |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["pending_cancellazione" => "S"]); |
|
// Workaround per UX |
|
die('window.location.reload()'); |
|
return true; |
|
} |
|
$message .= __LINE__; |
|
return false; |
|
} |
|
if (isset($result->errori)) { |
|
$error_codes = array_column(json_decode(json_encode($result->errori), true), 'codice'); |
|
|
|
|
|
$error_codes_str = implode(", ", $error_codes); |
|
if (in_array('ERR16', $error_codes) || in_array('ERR58', $error_codes)) { |
|
$message = __("Lo stato dell'appalto non è compatibile con la cancellazione di questo avviso ({$error_codes_str})"); |
|
} elseif (in_array('ERR48', $error_codes) || in_array('ERR51', $error_codes)) { |
|
$message = __("Questa tipologia di avviso non è cancellabile, oppure la pubblicazione potrebbe essere in stadio troppo avanzato per richiederne la cancellazione ({$error_codes_str})"); |
|
} else { |
|
$message = __("Impossibile cancellare, il server risponde con {$error_codes_str}"); |
|
} |
|
} else { |
|
$message = __("Errore in fase di cancellazione dell'avviso."); |
|
} |
|
|
|
$salva->expect = array_merge($salva->expect, ["esito", "risultato"]); |
|
$salva->oggetto = array_merge($salva->oggetto, ["esito" => "KO", "risultato" => serialize($result)]); |
|
if ($salva->save()) { |
|
$this->updateScheda(["pending_cancellazione" => "N"]); |
|
} |
|
return false; |
|
} |
|
/** |
|
* Delete a card |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return Bool|null |
|
*/ |
|
public function delete(?string &$message = null): ?Bool |
|
{ |
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
global $config; |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$apiEndpoint = $this->scheda["stato"] > NuovaPiattaformaAppalti::TRASMITTED_STATO ? "pubblicazioneAvvisi" : $this->getCardEndpoint(); |
|
if (empty($apiEndpoint)) { |
|
$message .= __LINE__; // Segnaposto per trovare questo errore |
|
return false; |
|
} |
|
$body = json_decode($this->scheda["dati"], TRUE); |
|
$this->organizeRepeteableGroups($body); |
|
|
|
$apiMethod = "delete"; |
|
$request = $uuids = $this->getKeysAndUUIDsForCurrentRequest(); |
|
|
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall($apiEndpoint, $apiMethod, $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
$operazione = new Salva(); |
|
$operazione->debug = false; |
|
$operazione->nome_tabella = "b_npa_operazioni_schede"; |
|
$operazione->operazione = "INSERT"; |
|
$operazione->expect = ["codice_scheda", "operazione", "esito", "risultato"]; |
|
$operazione->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "ELIMINA", |
|
"esito" => "KO", |
|
"risultato" => serialize($result) |
|
]; |
|
|
|
$message = __("Errore sconosciuto in fase di eliminazione della scheda. "); |
|
|
|
// Verifichiamo che la richiesta sia andata con successo |
|
if ($result->status == 200) { |
|
|
|
$this->scheda["errore"] = "NO"; |
|
$operazione->oggetto["esito"] = "OK"; |
|
} else { |
|
$operazione->oggetto["esito"] = "KO"; |
|
$operazione->save(); |
|
if (!empty($result->errori)) { |
|
|
|
|
|
$error_codes = array_column(json_decode(json_encode($result->errori), true), 'codice'); |
|
|
|
|
|
$error_codes_str = implode(", ", $error_codes); |
|
if (in_array('ERR16', $error_codes) || in_array('ERR58', $error_codes)) { |
|
$message = __("Lo stato dell'appalto non è compatibile con la cancellazione di questa scheda ({$error_codes_str})"); |
|
} else { |
|
$message = __("Impossibile cancellare, il server risponde con {$error_codes_str}"); |
|
} |
|
/*$bind = [":codice" => $this->scheda["codice"], ":errore" => "SI"]; |
|
$sql = "UPDATE b_npa_schede SET errore = :errore WHERE codice = :codice"; |
|
$ris = $this->pdo->go($sql, $bind);*/ |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
$this->resetGUUEonSoftDelete(); |
|
// Cancelliamo la scheda |
|
$bind = [":codice" => $this->scheda["codice"], "soft_delete" => "S"]; |
|
|
|
$schedeRimanenti = $this->getCards(false); |
|
$resetDossier = count($schedeRimanenti) == 1; |
|
|
|
if ($resetDossier) |
|
$this->createDossierRevision(); |
|
|
|
$sql = "UPDATE b_npa_schede SET soft_delete = :soft_delete WHERE codice = :codice"; |
|
$ris = $this->pdo->go($sql, $bind); |
|
|
|
|
|
|
|
// Se abbiamo cancellato l'appalto ricreiamo una nuova revisione del fascicolo |
|
if ($apiEndpoint === "comunicaAppalto") { |
|
$operazione->oggetto["operazione"] = "ELIMINA_APPALTO"; |
|
} |
|
|
|
if ($apiEndpoint === "pianificazioneAppalto") { |
|
$operazione->oggetto["operazione"] = "ELIMINA_PIANO"; |
|
} |
|
|
|
$operazione->save(); |
|
return true; |
|
} |
|
private function checkAndRetrieveUniqueIdentifierForCard(string $type, array $uuids) : string { |
|
|
|
switch($type) { |
|
|
|
case 'pianificazione': |
|
if(isset($uuids["idPianificazione"])) { |
|
return $uuids["idPianificazione"]; |
|
} |
|
break; |
|
|
|
case 'appalto': |
|
if(isset($uuids["idAppalto"])) { |
|
return $uuids["idAppalto"]; |
|
} |
|
break; |
|
|
|
default: |
|
if(isset($uuids["idScheda"])) { |
|
return $uuids["idScheda"]; |
|
} |
|
break; |
|
|
|
} |
|
|
|
return $this->getUUID("uuid-non-esistente"); |
|
|
|
} |
|
/** |
|
* Send the card |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return Bool|null |
|
*/ |
|
public function send(?string &$message = null): ?Bool |
|
{ |
|
|
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
if (!$this->canBeSent()) { |
|
$message = __("La scheda non è in uno stato valido per questa operazione."); |
|
return false; |
|
} |
|
|
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
$retval = false; |
|
|
|
global $config; |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$apiEndpoint = $this->getCardEndpoint(); |
|
if (empty($apiEndpoint)) { |
|
$message .= __LINE__; // Segnaposto per trovare questo errore |
|
return false; |
|
} |
|
$apiConfig = $this->api_config[$apiEndpoint]; |
|
|
|
$body = $this->getCardData(); |
|
$this->organizeRepeteableGroups($body); |
|
$XMLattachedSuccessfully = $this->attachXMLDocumentToRequestBody($body, $xml_message); |
|
|
|
if (!$XMLattachedSuccessfully) { |
|
$message = __($xml_message); |
|
return false; |
|
} |
|
|
|
$stazioniAppaltanti = recursive_array_key_search_all("stazioniAppaltanti", $body)[0] ?? []; |
|
|
|
if(!empty($stazioniAppaltanti)) { |
|
$this->updateFascicolo(["stazioni_appaltanti" => json_encode($stazioniAppaltanti)]); |
|
} |
|
|
|
$apiMethod = "create"; |
|
$request = $uuids = $this->getKeysAndUUIDsForCurrentRequest(); |
|
$uuid = $this->checkAndRetrieveUniqueIdentifierForCard($apiConfig["type"], $uuids); |
|
if (!empty($this->scheda["uuid"])) { |
|
$apiMethod = "update"; |
|
} |
|
|
|
$request = array_merge($request, [ |
|
"scheda" => [ |
|
"codice" => [ |
|
"idTipologica" => "codiceScheda", |
|
"codice" => $this->scheda["id_scheda"] |
|
], |
|
"versione" => str_replace("v", "", $this->version), |
|
"_stato" => [ |
|
"idTipologica" => "statoScheda", |
|
"codice" => "CONF" |
|
], |
|
"_dataCreazione" => date("c", strtotime($this->scheda["timestamp_creazione"])), |
|
"body" => $body |
|
] |
|
]); |
|
|
|
|
|
|
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall($apiEndpoint, $apiMethod, $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
$this->scheda["timestamp_trasmissione"] = date("d/m/Y H:i:s"); |
|
$this->scheda["stato"] = 20; |
|
$this->scheda["errore"] = "SI"; |
|
$this->scheda["type"] = $apiConfig["type"]; |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_schede"; |
|
$salva->operazione = "UPDATE"; |
|
$salva->expect = ["codice", "uuid", "type", "stato", "timestamp_trasmissione", "errore"]; |
|
|
|
$operazione = new Salva(); |
|
$operazione->debug = false; |
|
$operazione->nome_tabella = "b_npa_operazioni_schede"; |
|
$operazione->operazione = "INSERT"; |
|
$operazione->expect = ["codice_scheda", "operazione", "esito", "risultato"]; |
|
$operazione->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "TRASMETTI", |
|
"esito" => "KO", |
|
"risultato" => serialize($result) |
|
]; |
|
|
|
$message = __("Errore in fase di invio della scheda. Al ricaricamento della pagina verifica gli Avvisi."); |
|
|
|
// Verifichiamo che la richiesta sia andata con successo |
|
if ($result->status == 200) { |
|
|
|
$this->scheda["errore"] = "NO"; |
|
$operazione->oggetto["esito"] = "OK"; |
|
|
|
if (!empty($result->idPianificazione) && empty($this->fascicolo["id_pianificazione"])) { |
|
|
|
$this->fascicolo["id_pianificazione"] = $result->idPianificazione; |
|
$this->pdo->go("UPDATE b_npa SET id_pianificazione = :id_pianificazione WHERE codice = :codice", [":codice" => $this->fascicolo["codice"], ":id_pianificazione" => $this->fascicolo["id_pianificazione"]]); |
|
} |
|
|
|
if (!empty($result->idAppalto) && empty($this->fascicolo["id_appalto"])) { |
|
|
|
$this->fascicolo["id_appalto"] = $result->idAppalto; |
|
$this->pdo->go("UPDATE b_npa SET id_appalto = :id_appalto WHERE codice = :codice", [":codice" => $this->fascicolo["codice"], ":id_appalto" => $this->fascicolo["id_appalto"]]); |
|
} |
|
|
|
if (!empty($result->idScheda)) { |
|
|
|
$this->scheda["uuid"] = $result->idScheda; |
|
} else { |
|
|
|
$result = $this->read(); |
|
|
|
if (!empty($result->appalto->scheda->_idScheda)) { |
|
$this->scheda["uuid"] = $result->appalto->scheda->_idScheda; |
|
} |
|
|
|
if (!empty($result->piano->scheda->_idScheda)) { |
|
$this->scheda["uuid"] = $result->piano->scheda->_idScheda; |
|
} |
|
} |
|
// Popoliamo il notice_id inviato |
|
if ($this->check["eform"]["required"]) { |
|
$salva->expect[] = "guue_sent_notice_id"; |
|
$this->scheda["guue_sent_notice_id"] = $this->check["eform"]["notice_id"]; |
|
} |
|
// Ritorniamo con successo |
|
$retval = true; |
|
} |
|
|
|
if (!empty($result->errori)) { |
|
|
|
$this->scheda["errore"] = "SI"; |
|
$operazione->oggetto["esito"] = "KO"; |
|
$retval = false; |
|
|
|
$error_codes = array_column(json_decode(json_encode($result->errori), true), 'codice'); |
|
|
|
// Nel caso questo sia un update e ci viene risposto con un ERR16, |
|
// questa scheda dovrebbe ritornare in "CONFERMATO" |
|
if((in_array('ERR58', $error_codes) || in_array('ERR16', $error_codes)) && $apiMethod == "update" && !empty($this->scheda["uuid"])) { |
|
$checkResult = $this->checkConfirm($message); |
|
// Conferma confermata |
|
if($checkResult !== false) { |
|
|
|
if($checkResult === true) { |
|
$message = "INFO - Lo stato della conferma è stato recuperato con successo"; |
|
} |
|
// Conferma ancora in attesa |
|
if($checkResult === null) { |
|
$message = "INFO - Lo stato della conferma è stato recuperato con successo ma è ancora in corso."; |
|
} |
|
$this->resyncScheda(); |
|
$message .= "<br> <b>Attenzione:</b> Nelle situazioni di recupero, le modifiche effettuate alla scheda potrebbero essere perse. Abbiamo risincronizzato la scheda con i server ANAC."; |
|
return false; |
|
} |
|
|
|
// Nel caso in cui si sia verificato un "ERR38 - codiceAppalto già presente" eseguo una ricerca in ANAC e provo a riassociare l'idAppalto al fascicolo |
|
} elseif (in_array('ERR38', $error_codes) && $apiConfig["type"] == "appalto") { |
|
|
|
$search = $this->search(["codiceAppalto" => $body["anacForm"]["appalto"]["codiceAppalto"]]); |
|
if (!empty($search) && $search->totRows == 1) { |
|
|
|
$result = $search->result[0]; |
|
if (!empty($result->idAppalto)) { |
|
if($this->resyncScheda()) { |
|
$this->updateFascicolo(["id_appalto" => $result->idAppalto]); |
|
$this->updateScheda(["stato" => 20, "errore" => "NO"]); |
|
$message = "INFO - Lo stato della scheda è stato recuperato con successo."; |
|
return false; |
|
} |
|
|
|
} |
|
|
|
$retval = false; |
|
$message = "Attenzione: si è verificato un errore di comunicazione con i server ANAC. Si prega di riprovare"; |
|
} |
|
} |
|
} |
|
|
|
$salva->oggetto = $this->scheda; |
|
if (!$salva->save() || !$operazione->save()) { |
|
|
|
$today = date("Ymd"); |
|
error_log("[" . date('Y-m-d H:i:s') . "] - {$_SESSION["ente"]->getInfo()["dominio"]}" . PHP_EOL . |
|
"\tNuovaPiattaformaAppalti::send()" . PHP_EOL . |
|
"\tErrore durante il salvataggio dei dati" . PHP_EOL . PHP_EOL, 3, "{$config["npaFolder"]}/{$today}.log"); |
|
|
|
$message = DEVELOP_ENV ? "Errore durante il salvataggio dei dati" : __("Errore interno in fase di trasmissione"); |
|
return false; |
|
} |
|
if($retval) |
|
$this->closeAllPendingOperations(); |
|
return $retval; |
|
} |
|
|
|
/** |
|
* Retrieves and processes information from a notice |
|
* |
|
* @return Object |
|
*/ |
|
public function readNotice(): ?Object |
|
{ |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
|
|
$pubblicazioneAvvisi = $this->api_config["pubblicazioneAvvisi"]; |
|
if (!empty($pubblicazioneAvvisi)) { |
|
|
|
$info = unserialize($this->scheda["info"]); |
|
if (!empty($info->idAvviso)) { |
|
$request = ["idAvviso" => $this->getANACNoticeID()]; |
|
$result_str = $this->apiCall("pubblicazioneAvvisi", "read", $request); |
|
if (!empty($result_str) && valid_json($result_str, $result)) { |
|
// Normalizzazione dati scheda |
|
foreach (["appalto", "piano", "avviso"] as $entry) { |
|
if (isset($result->{$entry}->scheda)) { |
|
$result->_scheda = $result->{$entry}->scheda->scheda; |
|
} |
|
} |
|
if(isset($result->scheda) && !isset($result->_scheda)) { |
|
$result->_scheda = $result->scheda; |
|
} |
|
return $result; |
|
} |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/** |
|
* Fetch data from ANAC |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return Object |
|
*/ |
|
public function read(?string &$message = null): ?Object |
|
{ |
|
$message = "Errore interno sconosciuto."; |
|
global $config; |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$apiEndpoint = $this->getCardEndpoint(); |
|
$apiConfig = $this->api_config[$apiEndpoint]; |
|
if (empty($apiConfig)) { |
|
$message .= __LINE__; // Segnaposto per trovare questo errore |
|
return NULL; |
|
} |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
$result_str = $this->apiCall($apiEndpoint, "read", $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
// Normalizzazione dati scheda |
|
foreach (["appalto", "piano", "avviso"] as $entry) { |
|
if (isset($result->{$entry}->scheda)) { |
|
$result->_scheda = $result->{$entry}->scheda; |
|
} |
|
if(isset($result->scheda) && !isset($result->_scheda)) { |
|
$result->_scheda = $result->scheda; |
|
} |
|
} |
|
|
|
return $result; |
|
} |
|
|
|
/** |
|
* Richiede lo stato della scheda al server ANAC |
|
* |
|
* @return Array|null |
|
*/ |
|
public function requestCardStatus(): ?array |
|
{ |
|
global $config; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$apiEndpoint = $this->getCardEndpoint(); |
|
$apiConfig = $this->api_config[$apiEndpoint]; |
|
if (!empty($apiConfig)) { |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
$result = $this->apiCall($apiEndpoint, "verify", $request); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
$result = json_decode($result); |
|
if ($result->status == 200) { |
|
|
|
return $result; |
|
} |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/** |
|
* Verify the card |
|
* |
|
* @return Bool |
|
*/ |
|
public function verify(): ?Bool |
|
{ |
|
|
|
return false; |
|
|
|
global $config; |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
|
|
$verifica = $this->getLastOperation(null, "VERIFICA", NULL); |
|
if (!empty($verifica)) { |
|
|
|
$salva->operazione = "UPDATE"; |
|
$salva->expect = ["codice", "esito", "risultato"]; |
|
|
|
$operazione = "AP_VERIF"; |
|
if ($this->scheda["type"] == "pianificazione") { |
|
$operazione = "PI_VERIF"; |
|
} |
|
if ($this->scheda["type"] == "scheda" || $this->scheda["type"] == "postpubblicazione") { |
|
$operazione = "SC_VERIF"; |
|
} |
|
$result = $this->result($operazione); |
|
if (!empty($result)) { |
|
|
|
if ($result->status == 200 && !empty($result->listaEsiti)) { |
|
|
|
$result = end($result->listaEsiti); |
|
if ($result->esito->codice == "KO") { |
|
|
|
$verifica["esito"] = "KO"; |
|
$verifica["risultato"] = serialize($result); |
|
|
|
$salva->oggetto = $verifica; |
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "SI"]); |
|
return true; |
|
} |
|
} |
|
|
|
if ($result->esito->codice == "OK") { |
|
|
|
$verifica["esito"] = "OK"; |
|
$verifica["risultato"] = serialize($result); |
|
|
|
$salva->oggetto = $verifica; |
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "NO"]); |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione"]; |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$apiEndpoint = $this->getCardEndpoint(); |
|
$apiConfig = $this->api_config[$apiEndpoint]; |
|
if (!empty($apiConfig)) { |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
$result = $this->apiCall($apiEndpoint, "verify", $request); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
$result = json_decode($result); |
|
if ($result->status == 200) { |
|
|
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "VERIFICA" |
|
]; |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["stato" => 25]); |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Determina se la scheda può essere trasmessa o modificata |
|
* |
|
* @return Bool |
|
*/ |
|
public function canBeSent(): Bool |
|
{ |
|
if (!$this->checkIfCardNeedsXMLDocument()) return false; |
|
return |
|
$this->scheda["stato"] == 10 || |
|
($this->scheda["stato"] > 10 && $this->scheda["stato"] <= 40 && $this->scheda["errore"] == "SI"); |
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
* Verifica che la scheda possa essere confermata |
|
* |
|
* @param ?boolean $recovery Viene settata su true se la scheda è candidata per un recupero conferma |
|
* @return bool |
|
*/ |
|
public function canBeConfirmed(?bool &$recovery = false): bool |
|
{ |
|
$recovery = false; |
|
if($this->scheda["stato"] <= 20 && $this->scheda["errore"] == "SI") { |
|
$conferma = $this->getLastOperation(null, "CONFERMA", false); |
|
if(!empty($conferma) && $conferma["esito"] === "KO") { |
|
$risultato = unserialize($conferma["risultato"]); |
|
if(!empty($risultato)) { |
|
$errorCode = $risultato->errori[0]->codice ?? ""; |
|
$recovery = $errorCode === "ERR58"; |
|
if($recovery) { |
|
return true; |
|
} |
|
} |
|
} |
|
$trasmetti = $this->getLastOperation(null, "TRASMETTI", false); |
|
if(!empty($trasmetti) && $trasmetti["esito"] === "KO") { |
|
$risultato = unserialize($trasmetti["risultato"]); |
|
if(!empty($risultato)) { |
|
$errorCode = $risultato->errori[0]->codice ?? ""; |
|
$recovery = $errorCode === "ERR58"; |
|
if($recovery) { |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return ($this->scheda["stato"] == 20 || $this->scheda["stato"] == 40) && $this->scheda["errore"] == "NO"; |
|
} |
|
|
|
|
|
/** |
|
* Verifica lo stato della conferma |
|
* |
|
* @param string|null $message Viene popolato se necessario con il messaggio d'errore |
|
* @param array|null $conferma Se disponibile, la conferma |
|
* @return Bool|null |
|
*/ |
|
public function checkConfirm(?string &$message = null, ?array $conferma = null): ?Bool { |
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
|
|
if(empty($conferma)) { |
|
$salva->operazione = "INSERT"; |
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "CONFERMA" |
|
]; |
|
} else { |
|
$salva->operazione = "UPDATE"; |
|
$salva->oggetto = $conferma; |
|
} |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Andiamo a chiedere al server lo stato della pubblicazione |
|
$operazione = "AP_CONF"; |
|
if ($this->scheda["type"] == "pianificazione") { |
|
$operazione = "PI_CONF"; |
|
} |
|
if ($this->scheda["type"] == "scheda" || $this->scheda["type"] == "postpubblicazione") { |
|
$operazione = "SC_CONF"; |
|
} |
|
// Andiamo a chiedere al server lo stato della conferma |
|
$result = $this->resultNew($operazione); |
|
if (!empty($result)) { |
|
if ($result->status == 200) { |
|
// Esito non trovato |
|
if (empty($result->listaEsiti)) { |
|
$salva->oggetto["risultato"] = serialize([]); |
|
$salva->oggetto["esito"] = "KO"; |
|
if ($salva->save()) { |
|
$message = __("Impossibile verificare l'esito di conferma."); |
|
$this->updateScheda(["errore" => "SI", "stato" => 40]); |
|
} |
|
return false; |
|
} |
|
|
|
$wtResult = null; |
|
$okResult = null; |
|
$koResult = null; |
|
$lastResult = $result->listaEsiti[0]; |
|
foreach($result->listaEsiti as $esito) { |
|
switch($esito->esito->codice) { |
|
case "OK": |
|
if($okResult === null) $okResult = $esito; |
|
break; |
|
case "KO": |
|
if($koResult === null) $koResult = $esito; |
|
break; |
|
case "WT": |
|
if($wtResult === null) $wtResult = $esito; |
|
break; |
|
} |
|
} |
|
// Fallito / Non valido |
|
if(empty($okResult) && empty($wtResult) || $lastResult == $koResult) { |
|
// Se abbiamo un errore 44 e c'è ancora un WT vuol dire che c'è un accavallamento di rettifiche, quindi non falliamo |
|
$errorCode = $koResult->errori[0]->codice ?? null; |
|
if(in_array($errorCode, ["ERR58", "ERR16"], true) && (!empty($okResult) || !empty($wtResult))) { |
|
// Se l'ultimo errore è ERR58 o ERR16 e abbiamo un esito positivo o di wait, ignoriamo l'errore |
|
} else { |
|
$salva->oggetto["esito"] = "KO"; |
|
|
|
if(!empty($koResult)) |
|
$salva->oggetto["risultato"] = serialize($koResult); |
|
|
|
$message = __("Vi è stato un problema con la conferma della scheda. "); |
|
|
|
// Impostiamo lo stato di errore |
|
if ($salva->save()) { |
|
// Appendo al messaggio per distinguere il caso in cui non sono state salvate le info |
|
$message .= " " . __("L'errore sarà disponibile al ricaricamento della pagina."); |
|
$this->updateScheda(["errore" => "SI", "stato" => 40]); |
|
} |
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
|
|
} |
|
// Successo |
|
if(!empty($okResult)) { |
|
$salva->oggetto["esito"] = "OK"; |
|
$salva->oggetto["risultato"] = serialize($okResult); |
|
|
|
if ($salva->save()) { |
|
if ($okResult !== null) { |
|
$info = new stdClass; |
|
if (!empty($this->scheda["info"])) { |
|
$info = unserialize($this->scheda["info"]); |
|
} |
|
foreach(["idAvviso", "idContratto", "idPianificazione", "idAppalto"] as $prop) { |
|
if(!empty($okResult->$prop)) { |
|
$info->$prop = $okResult->$prop; |
|
} |
|
} |
|
$this->updateScheda(["info" => serialize($info)]); |
|
} else { |
|
$message = __("I server ANAC hanno processato in maniera errata questa conferma."); |
|
$this->updateScheda(["stato" => 50, "errore" => 'SI']); |
|
return false; |
|
} |
|
// Successo totale |
|
$this->updateScheda(["stato" => 50, "errore" => 'NO']); |
|
return true; |
|
} |
|
} |
|
// In attesa |
|
if($salva->operazione === "INSERT") { |
|
$this->updateScheda(["errore" => "NO", "stato" => 40]); |
|
$salva->oggetto["esito"] = null; |
|
$salva->save(); |
|
} |
|
$message = __("INFO - La scheda è in corso di conferma. Questo processo potrebbe richiedere diverse ore.<br>Verifica l'esito nuovamente più tardi."); |
|
// Ha senso ritornare false con un errore parlante |
|
return null; |
|
} |
|
} |
|
|
|
$message = __("Errore durante la verifica dello stato di conferma"); |
|
return false; |
|
} |
|
|
|
/** |
|
* Confirm the card and start assignment phase for the CIG. |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return Bool|null |
|
*/ |
|
public function confirm(?string &$message = null): ?Bool |
|
{ |
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
if (!$this->canBeConfirmed()) { |
|
$message = __("La scheda non è in uno stato valido per questa operazione."); |
|
return false; |
|
} |
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
global $config; |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione"]; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di conferma in attesa |
|
$conferma = $this->getLastOperation(null, "CONFERMA", NULL); |
|
// Se c'è |
|
if (!empty($conferma)) { |
|
$checkResult = $this->checkConfirm($message, $conferma); |
|
if($checkResult === null) $checkResult = false; |
|
return $checkResult; |
|
} |
|
|
|
$apiEndpoint = $this->getCardEndpoint(); |
|
$apiConfig = $this->api_config[$apiEndpoint]; |
|
if (empty($apiConfig)) { |
|
$message .= __LINE__; // Segnaposto per trovare questo errore |
|
return false; |
|
} |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall($apiEndpoint, "confirm", $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "CONFERMA" |
|
]; |
|
|
|
// Verifichiamo la risposta |
|
|
|
// Se non ci viene fornita una risposta con successo |
|
if ($result->status !== 200) { |
|
$error_codes = isset($result->errori) ? array_column(json_decode(json_encode($result->errori), true), 'codice') : "Errore sconosciuto"; |
|
if(in_array('ERR58', $error_codes) || in_array('ERR16', $error_codes)) { |
|
$checkResult = $this->checkConfirm($message); |
|
// Conferma confermata |
|
if($checkResult !== false) { |
|
if($checkResult === true) { |
|
$message = "INFO - Lo stato della conferma è stato recuperato con successo"; |
|
} |
|
// Conferma ancora in attesa |
|
if($checkResult === null) { |
|
$message = "INFO - Lo stato della conferma è stato recuperato con successo ma è ancora in corso."; |
|
} |
|
$this->resyncScheda(); |
|
$message .= "<br> <b>Attenzione:</b> Nelle situazioni di recupero, le modifiche effettuate alla scheda potrebbero essere perse. Abbiamo risincronizzato la scheda con i server ANAC."; |
|
return false; |
|
} |
|
} |
|
// Aggiorniamo lo status |
|
$salva->expect = array_merge($salva->expect, ["esito", "risultato"]); |
|
$salva->oggetto = array_merge($salva->oggetto, ["esito" => "KO", "risultato" => serialize($result)]); |
|
|
|
// Settiamo errore su SI |
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "SI", "stato" => 40]); |
|
} |
|
|
|
$message = __("Errore in fase di conferma della scheda. Al ricaricamento della pagina verifica gli Avvisi."); |
|
return false; |
|
} |
|
|
|
// Conferma andata con successo |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "NO", "stato" => 40]); |
|
return true; |
|
} |
|
|
|
// Fallback generico |
|
return false; |
|
} |
|
|
|
/** |
|
* Get the CIG for the lots |
|
* |
|
* @return ?Array |
|
*/ |
|
public function getCIG(?string &$message = null, int $page = 1, $perPage = 20): ?Bool |
|
{ |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione", "esito", "risultato"]; |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$comunicaAppalto = $this->api_config["comunicaAppalto"]; |
|
if (!empty($comunicaAppalto)) { |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest("appalto"); |
|
$request["page"] = $page; |
|
$request["perPage"] = $perPage; |
|
|
|
|
|
$result = $this->apiCall("comunicaAppalto", "cig", $request, null, [], $message); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
$result = json_decode($result); |
|
if ($result->status == 200) { |
|
|
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "OTTIENI-CIG", |
|
"esito" => "OK", |
|
"risultato" => serialize($result) |
|
]; |
|
|
|
|
|
|
|
$salva->save(); |
|
if (!empty($result->result)) { |
|
|
|
foreach ($result->result as $lot) { |
|
|
|
if (!empty($lot->cig)) { |
|
|
|
$this->updateCig($lot->lotIdentifier, $lot->cig); |
|
} |
|
} |
|
if($result->totPages > $page) { |
|
// I server ANAC sono leeeeeeenti |
|
set_time_limit(0); |
|
ignore_user_abort(); |
|
return $this->getCIG($message, $page+1, $perPage); |
|
} |
|
return true; |
|
} |
|
|
|
} |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/** |
|
* Determina che la scheda possa essere pubblicata |
|
* |
|
* @return Bool |
|
*/ |
|
public function canBePublished(bool $ui = true): Bool |
|
{ |
|
if ($ui) { |
|
return ($this->scheda["stato"] == 50 || $this->scheda["stato"] == 90) && |
|
//$this->scheda["errore"] == "NO" && |
|
self::hasToBePublished($this->scheda); |
|
} |
|
return ($this->scheda["stato"] == 50 || $this->scheda["stato"] >= 90) && |
|
//$this->scheda["errore"] == "NO" && |
|
self::hasToBePublished($this->scheda); |
|
} |
|
|
|
|
|
|
|
public function updatePublicationStatus(?string &$message = null): ?Bool |
|
{ |
|
$avviso = $this->readNotice(); |
|
if (!empty($avviso) && $avviso->status == 200) { |
|
$this->updateScheda(["pubblicazione" => serialize($avviso->avviso)]); |
|
|
|
$this->checkIfCardNeedsXMLDocument(); |
|
$hasEUPublication = $this->check["eform"]["required"]; |
|
$isFullyPublished = isset($avviso->avviso->datiPubblicazioneIT) && isset($avviso->avviso->datiPubblicazioneIT->stato->codice) && $avviso->avviso->datiPubblicazioneIT->stato->codice === "PUBB"; |
|
if ($hasEUPublication && $isFullyPublished) { |
|
$isFullyPublished = isset($avviso->avviso->datiPubblicazioneEU) && isset($avviso->avviso->datiPubblicazioneEU->stato->codice) && $avviso->avviso->datiPubblicazioneEU->stato->codice === "PUBB"; |
|
} |
|
if ($isFullyPublished) { |
|
$message = "La pubblicazione della scheda è completa"; |
|
$this->updateScheda(["stato" => 110]); |
|
return true; |
|
} |
|
$message = "INFO - La pubblicazione non è ancora stata completata."; |
|
} |
|
return false; |
|
} |
|
|
|
public function checkPublish(?string &$message = null, ?array $pubblica): ?Bool { |
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
|
|
if(empty($pubblica)) { |
|
$salva->operazione = "INSERT"; |
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "PUBBLICA" |
|
]; |
|
} else { |
|
$salva->operazione = "UPDATE"; |
|
$salva->oggetto = $pubblica; |
|
} |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Andiamo a chiedere al server lo stato della pubblicazione |
|
$result = $this->result("AV_PUBB"); |
|
if (!empty($result)) { |
|
|
|
if ($result->status == 200) { |
|
if (empty($result->listaEsiti)) { |
|
$message = __("Impossibile verificare l'esito dell'operazione. Contatta l'Help Desk tecnico"); |
|
return false; |
|
} |
|
|
|
$result = end($result->listaEsiti); |
|
if ($result->esito->codice == "WT") { |
|
$message = __("INFO - La scheda non è ancora stata pubblicata. Ricontrolla più tardi."); |
|
// Aggiorniamo comunque lo stato in caso non lo sia |
|
if($salva->operazione === "INSERT") { |
|
$this->updateScheda(["errore" => "NO", "stato" => 90]); |
|
$salva->oggetto["esito"] = NULL; |
|
$salva->save(); |
|
} |
|
return null; |
|
} |
|
|
|
// Pubblicazione fallita |
|
if ($result->esito->codice == "KO") { |
|
|
|
$salva->oggetto["esito"] = "KO"; |
|
$salva->oggetto["risultato"] = serialize($result); |
|
$message = __("Vi è stato un problema con la pubblicazione dell'avviso."); |
|
|
|
// Impostiamo lo stato di errore |
|
if ($salva->save()) { |
|
// Appendo al messaggio per distinguere il caso in cui non sono state salvate le info |
|
$message .= " " . __("L'errore sarà disponibile al ricaricamento della pagina."); |
|
$this->updateScheda(["errore" => "SI"]); |
|
} |
|
// Ha senso ritornare false con un errore parlante |
|
return false; |
|
} |
|
|
|
if ($result->esito->codice == "OK") { |
|
|
|
$salva->oggetto["esito"] = "OK"; |
|
$salva->oggetto["risultato"] = serialize($result); |
|
if ($salva->save()) { |
|
|
|
// Cambiamo lo stato della scheda GUUE se presente |
|
$this->publishGUUEOnSuccessfulPublication(); |
|
$this->updateScheda(["errore" => "NO", "stato" => 100]); |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
$message = __("Errore durante la verifica dello stato di pubblicazione"); |
|
return false; |
|
} |
|
|
|
|
|
/** |
|
* Pubblica l'avviso |
|
* |
|
* @param string|null $message Se passato come riferimento, viene popolato in caso di errore con il messaggio d'errore |
|
* @return ?bool |
|
*/ |
|
public function publish(?string &$message = null): ?bool |
|
{ |
|
if (!$this->getServiceStatusBool(true)) { |
|
$message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); |
|
return false; |
|
} |
|
|
|
$metadata = $this->metadataSchede[$this->scheda["id_scheda"]]; |
|
|
|
|
|
if( |
|
$this->scheda["stato"] == 100 || ( |
|
$this->scheda["stato"] >= NuovaPiattaformaAppalti::TRASMITTED_STATO && |
|
$this->scheda["stato"] < NuovaPiattaformaAppalti::PUBLISHED_STATO && |
|
$metadata["pubblicazione_implicita"] |
|
) |
|
) { |
|
return $this->updatePublicationStatus($message); |
|
} |
|
|
|
if (!$this->canBePublished(false)) { |
|
$message = __("La scheda non è in uno stato valido per questa operazione."); |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
// Popoliamo message con qualcosa di generico |
|
$message = __("Errore interno sconosciuto "); |
|
|
|
global $config; |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "PUBBLICA" |
|
]; |
|
$salva->expect = ["codice_scheda", "operazione"]; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Andiamo a verificare che ci sia un'operazione di pubblicazione in attesa |
|
$pubblica = $this->getLastOperation(null, "PUBBLICA", NULL); |
|
|
|
|
|
// Se c'è |
|
if (!empty($pubblica)) { |
|
|
|
$checkResult = $this->checkPublish($message, $pubblica); |
|
if($checkResult === null) $checkResult = false; |
|
return $checkResult; |
|
} |
|
$pubblicazioneAvvisi = $this->api_config["pubblicazioneAvvisi"]; |
|
if (empty($pubblicazioneAvvisi)) { |
|
$message .= __LINE__; // Segnaposto per trovare questo errore |
|
return false; |
|
} |
|
|
|
$info = unserialize($this->scheda["info"]); |
|
|
|
// Verifichiamo sia popolato l'id avviso |
|
if (empty($info->idAvviso)) { |
|
$message = __("Problema interno con la pubblicazione. Contattare l'Help Desk tecnico."); |
|
|
|
$this->appendLog( |
|
"pubblicazioneAvvisi", |
|
"publish", |
|
[], |
|
"idAvviso non trovato." |
|
); |
|
return null; |
|
} |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
$request["idAvviso"] = $this->getANACNoticeID();; |
|
|
|
// Effettuiamo la richiesta |
|
$result_str = $this->apiCall("pubblicazioneAvvisi", "publish", $request, null, [], $apiMessage); |
|
|
|
$result = self::manageANACResponse($result_str, $message); |
|
if($result === null) { |
|
$message = empty($apiMessage) ? $message : $apiMessage; |
|
return false; |
|
} |
|
|
|
// Detect errori catturabili |
|
if ($result->status !== 200) { |
|
if(isset($result->errori[0]->codice)) { |
|
$error_codes = array_column(json_decode(json_encode($result->errori), true), 'codice'); |
|
if (in_array('ERR45', $error_codes)) { |
|
$checkResult = $this->checkPublish($message, null); |
|
// Pubblicazione confermata |
|
if($checkResult !== false) { |
|
|
|
if($checkResult === true) { |
|
$message = "INFO - Lo stato della pubblicazione è stato recuperato con successo"; |
|
} |
|
// Conferma ancora in attesa |
|
if($checkResult === null) { |
|
$message = "INFO - Lo stato della pubblicazione è stato recuperato con successo ma è ancora in corso."; |
|
} |
|
$this->resyncScheda(); |
|
$message .= "<br> <b>Attenzione:</b> Nelle situazioni di recupero, le modifiche effettuate alla scheda potrebbero essere perse. Abbiamo risincronizzato la scheda con i server ANAC."; |
|
return false; |
|
} |
|
// Altrimenti lasciamo cadere nel resto del codice |
|
} |
|
} |
|
} |
|
|
|
// Verifichiamo la risposta |
|
if (!empty($result->status) && $result->status == 200) { |
|
|
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "NO", "stato" => 90]); |
|
return true; |
|
} |
|
$message .= __LINE__; |
|
return false; |
|
} |
|
|
|
$message = __("Errore in fase di pubblicazione della scheda. Al ricaricamento della pagina verifica gli Avvisi."); |
|
$salva->expect = array_merge($salva->expect, ["esito", "risultato"]); |
|
$salva->oggetto = array_merge($salva->oggetto, ["esito" => "KO", "risultato" => serialize($result)]); |
|
if ($salva->save()) { |
|
$this->updateScheda(["errore" => "SI", "stato" => 90]); |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Retrieve tender information from the National Anti-Corruption Authority (ANAC) |
|
* |
|
* @return Array |
|
*/ |
|
public function getTenderInfoFromANAC(): ?Object |
|
{ |
|
|
|
global $config; |
|
|
|
$salva = new Salva(); |
|
$salva->debug = false; |
|
$salva->nome_tabella = "b_npa_operazioni_schede"; |
|
$salva->operazione = "INSERT"; |
|
$salva->expect = ["codice_scheda", "operazione", "esito"]; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$apiEndpoint = $this->getCardEndpoint(); |
|
$apiConfig = $this->api_config[$apiEndpoint]; |
|
if (!empty($apiConfig)) { |
|
|
|
$key = "idAppalto"; |
|
if ($this->scheda["type"] == "pianificazione") { |
|
$key = "idPianificazione"; |
|
} |
|
|
|
$request = [$key => $this->getUUID()]; |
|
$result = $this->apiCall($apiEndpoint, "confirm", $request); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
$result = json_decode($result); |
|
|
|
$salva->oggetto = [ |
|
"codice_scheda" => $this->scheda["codice"], |
|
"operazione" => "CONSULTA", |
|
"esito" => serialize($result) |
|
]; |
|
|
|
return $result; |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/** |
|
* Uguale a result, ma compie meno operazioni |
|
* |
|
* @param String|null $operazione |
|
* @param String|null $ricerca |
|
* @return Object|null |
|
*/ |
|
public function resultNew(?String $operazione = null, ?String $ricerca = "TUTTI_ESITI") : ?Object { |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$serviziComuni = $this->api_config["serviziComuni"]; |
|
if (empty($serviziComuni)) return null; |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
|
|
if (!isset($request["idScheda"])) { |
|
$request["idScheda"] = $this->getUUID("scheda"); |
|
} |
|
|
|
if ($operazione !== null) |
|
$request["tipoOperazione"] = $operazione; |
|
$request["tipoRicerca"] = $ricerca; |
|
|
|
$result_str = $this->apiCall("serviziComuni", "result", $request); |
|
|
|
if(!valid_json($result_str, $result)) return null; |
|
if(empty($result->listaEsiti)) return null; |
|
|
|
if (count($result->listaEsiti) > 0) { |
|
$result->listaEsiti = array_filter($result->listaEsiti, function($esito) use ($request){ |
|
if(isset($esito->idScheda)) { |
|
if(strtolower($esito->idScheda) != strtolower($request["idScheda"])) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
}, ARRAY_FILTER_USE_BOTH); |
|
// Workaround per sort |
|
$result->listaEsiti = json_decode(json_encode($result->listaEsiti), true); |
|
$dt = array_column($result->listaEsiti, 'dataControllo'); |
|
array_multisort($dt, SORT_DESC, $result->listaEsiti); |
|
$result->listaEsiti = json_decode(json_encode($result->listaEsiti)); |
|
} |
|
return $result; |
|
} |
|
|
|
/** |
|
* Retrieves the result or outcome of the operation |
|
* @param ?String $operazione |
|
* @param String $ricerca TUTTI_ESITI|ULTIMO_ESITO |
|
* |
|
* @return Object |
|
*/ |
|
public function result(?String $operazione = null, ?String $ricerca = "TUTTI_ESITI"): ?Object |
|
{ |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$serviziComuni = $this->api_config["serviziComuni"]; |
|
if (!empty($serviziComuni)) { |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
if (!isset($request["idScheda"])) { |
|
$request["idScheda"] = $this->getUUID("scheda"); |
|
} |
|
if ($operazione !== null) |
|
$request["tipoOperazione"] = $operazione; |
|
$request["tipoRicerca"] = $ricerca; |
|
|
|
$result = $this->apiCall("serviziComuni", "result", $request); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
$response = json_decode($result); |
|
if ($response->status == 200 && !empty($response->listaEsiti)) { |
|
|
|
$outcomes = $response->listaEsiti; |
|
$response->listaEsiti = []; |
|
foreach ($outcomes as $outcome) { |
|
if (strtolower($outcome->idScheda) == strtolower($request["idScheda"])) { |
|
$response->listaEsiti[] = json_decode(json_encode($outcome), 1); |
|
} |
|
} |
|
|
|
if (count($response->listaEsiti) > 0) { |
|
|
|
$dt = array_column($response->listaEsiti, 'dataControllo'); |
|
array_multisort($dt, SORT_DESC, $response->listaEsiti); |
|
$response->listaEsiti = [$response->listaEsiti[0]]; |
|
$response->listaEsiti[0] = json_decode(json_encode($response->listaEsiti[0])); |
|
|
|
if (isset($response->listaEsiti[0]->esito->codice) && $response->listaEsiti[0]->esito->codice == "OK") { |
|
|
|
try { |
|
|
|
$info = new stdClass; |
|
if (!empty($this->scheda["info"])) { |
|
$info = unserialize($this->scheda["info"]); |
|
} |
|
$info->idAppalto = $response->listaEsiti[0]->idAppalto ?? null; |
|
$info->idPianificazione = $response->listaEsiti[0]->idPianificazione ?? null; |
|
$info->idScheda = $response->listaEsiti[0]->idScheda ?? null; |
|
$info->idNuovaScheda = $response->listaEsiti[0]->idNuovaScheda ?? null; |
|
$info->idAvviso = $response->listaEsiti[0]->idAvviso ?? null; |
|
$info->idNuovoAvviso = $response->listaEsiti[0]->idNuovoAvviso ?? null; |
|
$info->idContratto = $response->listaEsiti[0]->idContratto ?? null; |
|
$this->updateScheda(["info" => serialize($info)]); |
|
} catch (\Throwable $th) { |
|
$this->appendLog( |
|
"serviziComuni", |
|
"result", |
|
[], |
|
"Errore formulazione richiesta, tutti i campi sono vuoti." |
|
); |
|
} |
|
} |
|
} |
|
|
|
return $response; |
|
} |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/** |
|
* Retrieves the status from a tender or a Lot (required CIG) |
|
* |
|
* @return Object |
|
*/ |
|
public function status(String $cig = null): ?Object |
|
{ |
|
|
|
global $config; |
|
|
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
$serviziComuni = $this->api_config["serviziComuni"]; |
|
if (!empty($settings)) { |
|
|
|
$request = $this->getKeysAndUUIDsForCurrentRequest(); |
|
if (!empty($cig)) { |
|
$request["cig"] = $cig; |
|
} |
|
$result = $this->apiCall("serviziComuni", "status", $request); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
return json_decode($result); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/** |
|
* Retrieves the result or outcome of the operation |
|
* @param String $operazione |
|
* @param String $ricerca TUTTI_ESITI|ULTIMO_ESITO |
|
* |
|
* @return Object |
|
*/ |
|
public function search(?array $parameters = []): ?Object |
|
{ |
|
global $config; |
|
date_default_timezone_set('Europe/Rome'); |
|
|
|
// Ottengo la configurazione per contattare il webservice di ANAC |
|
$apiEndpoint = $this->getCardEndpoint(); |
|
|
|
// Verifico che la richiesta abbia solo i campi compatibili con ANAC |
|
$keys = ["codiceAppalto", "cig", "lotIdentifier", "stato", "tipo", "dataCreazioneDa", "dataCreazioneA", "page", "perPage"]; |
|
$parameters = array_filter($parameters, function ($key) use ($keys) { |
|
return in_array($key, $keys); |
|
}, ARRAY_FILTER_USE_KEY); |
|
|
|
if (empty($parameters)) { |
|
$this->appendLog( |
|
$apiEndpoint, |
|
"search", |
|
$parameters, |
|
"Errore formulazione richiesta, tutti i campi sono vuoti." |
|
); |
|
return false; |
|
} |
|
|
|
// Definisco la paginazione di default |
|
if (empty($parameters["perPage"])) { |
|
$parameters["perPage"] = 20; |
|
} |
|
|
|
// Otteniamo il voucher PDND |
|
$result = $this->apiCall($apiEndpoint, "search", $parameters); |
|
if (!empty($result) && is_json($result)) { |
|
|
|
return json_decode($result); |
|
} |
|
return false; |
|
} |
|
|
|
}
|
|
|