[ "id" => 0, "color" => "var(--danger)", "icon" => "fas fa-times-circle", "title" => "Documento non disponibile" ], 1 => [ "id" => 1, "color" => "var(--primary)", "icon" => "fas fa-check-circle", "title" => "Documento disponibile" ], 2 => [ "id" => 2, "color" => "var(--warning)", "icon" => "fas fa-check-circle", "title" => "Documento scaduto" ] ]; /** * Getter * * @param mixed $name * @return void */ public function __get($name) { if ($name === 'info') { return get_object_vars($this); } if ($name === 'scheda' || $name == 'form') { return null; } // Se la proprietà richiesta non è 'info', gestisci come errore o restituisci null trigger_error("Proprietà non definita: {$name}", E_USER_NOTICE); return null; } private function __construct() { self::setup(); $this->api_config = require dirname(__DIR__, 2) . "/inc/configurazione.php"; $this->stato = self::$stati->{0}; $this->stato_utilizzo = self::$utilizzo->{$this->stato_utilizzo ?? "001"}; $this->stato_comprova = self::$comprova->{$this->stato_comprova ?? "001"}; $this->stato_richiesta = ! empty($this->stato_richiesta) ? self::$stati_richiesta->{$this->stato_richiesta} : (object) ["descrizione" => (object) ["it" => "/"]]; if(! empty($this->codice)) { if(! empty($this->data_inserimento)) { $this->stato = self::$stati->{1}; } if(! empty($this->data_fine_validita) && strtotime($this->data_fine_validita) < strtotime('now')) { $this->stato = self::$stati->{2}; } } } /** * Inizializza le varibili statiche * * @return void */ private static function setup() : void { // Inizializzo gli stati self::$stati = (object) json_decode(json_encode(self::STATI)); // Inizlializzo le query global $pdo; if (!self::$queryInitialized) { self::$stm = $pdo->prepare("SELECT * FROM b_fvoe_documenti WHERE `tipo_documento` = :tipo_documento AND `codice_fvoe` = :codice_fvoe AND (`data_inserimento` IS NOT NULL OR `data_inserimento` <> '') ORDER BY `data_fine_validita` DESC LIMIT 0,1"); self::$stmDocumento = $pdo->prepare("SELECT * FROM b_fvoe_documenti WHERE `tipo_documento` = :tipo_documento AND `codice_fvoe` = :codice_fvoe AND (`data_inserimento` IS NULL OR `data_inserimento` = '') ORDER BY `timestamp_creazione` DESC LIMIT 0,1"); self::$stmRequestData = $pdo->prepare("SELECT `richiesta` FROM `b_fvoe_operazioni` WHERE (`richiesta` IS NOT NULL OR `richiesta` <> '') AND `tipo_documento` = :tipo_documento AND `codice_fvoe` = :codice_fvoe ORDER BY `timestamp` DESC LIMIT 0,1"); self::$stmPendingDocument = $pdo->prepare("SELECT * FROM b_fvoe_documenti WHERE `tipo_documento` = :tipo_documento AND `codice_fvoe` = :codice_fvoe AND (`id_documento` IS NULL OR `id_documento` = '') ORDER BY `codice` DESC LIMIT 0,1"); self::$queryInitialized = true; } // Iniziallizzo la stato delle richieste di documento if(empty(self::$stati_richiesta)) { $stato_richiesta = jsonToArray(__DIR__ . DIRECTORY_SEPARATOR . "json" . DIRECTORY_SEPARATOR . "stato_richiesta_documento.json"); $stato_richiesta = array_column($stato_richiesta, null, "codice"); self::$stati_richiesta = json_decode(json_encode($stato_richiesta)); } // Iniziallizzo la tipologia di utilizzo del documento if(empty(self::$utilizzo)) { $utilizzo = jsonToArray(__DIR__ . DIRECTORY_SEPARATOR . "json" . DIRECTORY_SEPARATOR . "stato_utilizzo.json"); $utilizzo = array_column($utilizzo, null, "codice"); self::$utilizzo = json_decode(json_encode($utilizzo)); } // Iniziallizzo la comprova del documento if(empty(self::$comprova)) { $comprova = jsonToArray(__DIR__ . DIRECTORY_SEPARATOR . "json" . DIRECTORY_SEPARATOR . "comprova_documento.json"); $comprova = array_column($comprova, null, "codice"); self::$comprova = json_decode(json_encode($comprova)); } // Inizializzo le tipoligie di documento if(empty(self::$tipologia_documenti)) { self::$tipologia_documenti = jsonToArray(__DIR__ . "/json/tipologia_documenti.json"); self::$tipologia_documenti = array_column(self::$tipologia_documenti, null, "codice"); } // Inizializzo il path dei modelli di dati da richiedere if(empty(self::$path_modelli_dati_aggiuntivi)) { self::$path_modelli_dati_aggiuntivi = dirname(__DIR__, 2) . '/versions/' . NuovaPiattaformaAppalti::LATEST_SDK_VERSION . '/fvoe'; } } /** * Inizializza la classe * * @param mixed $tipo_documento * @return self */ public static function init(string $tipo_documento, int $codice_fvoe) : self { self::setup(); if(empty(self::$tipologia_documenti[$tipo_documento])) { throw new InvalidArgumentException("Tipologia di documento non valida", 1); } $document = new self(); $document->tipo_documento = $tipo_documento; $document->codice_fvoe = $codice_fvoe; $document->oggetto = self::$tipologia_documenti[$tipo_documento]["descrizione"]["it"]; $document->stato_richiesta = "001"; return $document; } /** * Get pending document record * * @return FVOEDocument */ public function getPendingDocument() : FVOEDocument { if($this->documentsCollection()->areThereDocumentsPendingRelease() && $this->documentsCollection()->haveRequestsBeenMade()) { self::$stmPendingDocument->execute([":tipo_documento" => $this->tipo_documento, ":codice_fvoe" => $this->codice_fvoe]); if(self::$stmPendingDocument->rowCount() > 0) { self::$stmPendingDocument->setFetchMode(PDO::FETCH_CLASS, 'FVOEDocument'); return self::$stmPendingDocument->fetch(); } } } /** * Verifica se una specifica classe documentale richiede dati aggiuntivi * * @param mixed $tipo_documento * @return bool */ public static function hasFormRequest(?string $tipo_documento) : bool { self::setup(); return ! empty(self::$tipologia_documenti[$tipo_documento]["modelloDati"]) && file_exists(self::$path_modelli_dati_aggiuntivi . '/' . self::$tipologia_documenti[$tipo_documento]["modelloDati"] . '.json'); } /** * Ottieni il form per la richiesta della classe documentale * * @param mixed $tipo_documento * @return array */ public static function getFormRequest(string $tipo_documento) : ?array { self::setup(); if(self::hasFormRequest($tipo_documento)) { return jsonToArray(self::$path_modelli_dati_aggiuntivi . '/' . self::$tipologia_documenti[$tipo_documento]["modelloDati"] . '.json'); } return null; } /** * Make repeatable object for ajax request. * * @param Array $settings * @return String */ public function makeRepeatableObject(array $settings): String { $settings["codice"] = $this->codice; $settings["tipo_documento"] = $this->tipo_documento; $settings["codice_fvoe"] = $this->codice_fvoe; return base64_encode(gzdeflate(json_encode($settings))); } private function fixCustomFields(array &$data) { foreach ($data as $key => &$element) { if(is_array($element)) { if (array_keys(array_merge($element)) === range(0, count($element) - 1)) { $element = array_values($element); } if(!empty($element['Giorno']) && !empty($element['Mese']) && !empty($element['Anno'])) { $element["DataDiNascitaCustomField"] = "{$element['Giorno']}/{$element['Mese']}/{$element['Anno']}"; } self::fixCustomFields($element); } } } /** * Get data already transmitted for the current document type * * @return array */ public function getCardData(): array { self::$stmRequestData->execute([":tipo_documento" => $this->tipo_documento, ":codice_fvoe" => $this->codice_fvoe]); if(self::$stmRequestData->rowCount() == 1) { $data = self::$stmRequestData->fetch(PDO::FETCH_COLUMN, 0); $data = unserialize($data); self::fixCustomFields($data); return $data; } return []; } /** * Stampa il form per una specifica classe documentale * * @param mixed $tipo_documento * @return void */ public function printDocumentRequestForm(?string $tipo_documento = null) : void { if(empty($tipo_documento)) { $tipo_documento = $this->tipo_documento; } $form = FVOEDocument::getFormRequest($tipo_documento); if(! empty($form["content"])) { $this->printSection($form["content"]); ?> $settings) { if (isset($data[$key])) { if(is_array($data[$key])) { // Gestisco le ripetizioni if(isset($data[$key][0])) { foreach ($data[$key] as $index => &$subvalues) { self::setDocumentRequestFormData($subvalues, $settings); } } else { self::setDocumentRequestFormData($data[$key], $settings); } } else { if($key == "CF") { if(isset($data["DatiNascita"]["CodiceCatastoStato"]) && $data["DatiNascita"]["CodiceCatastoStato"] == "Z000") { $data["DatiNascita"]["CodiceCatastoComuneItaliano"] = codice_catasto_from_cf($data[$key]); } } if($key == "CF_Soggetto") { $data["LuogoNascita_Soggetto"] = codice_catasto_from_cf($data[$key]); } if($key == "DataDiNascitaCustomField") { $date = date2mysql($data[$key]); $date = \DateTime::createFromFormat('Y-m-d', $date); $data["Giorno"] = $date->format('d'); $data["Mese"] = $date->format('m'); $data["Anno"] = $date->format('Y'); unset($data["DataDiNascitaCustomField"]); } if($key == "DataNascita_Soggetto") { $date = date2mysql($data[$key]); $date = \DateTime::createFromFormat('Y-m-d', $date); $data[$key] = $date->format('Y-m-d\T00:00:00+02:00'); } switch ($settings['format']) { case 'datetime': case 'date-time': $date = datetime2mysql($data[$key]); $date = \DateTime::createFromFormat('Y-m-d H:i', $date); if (! is_bool($date)) { $data[$key] = $date->format(\DateTime::ATOM); } break; default: break; } } } } } /** * Richiedi un documento a un ente certificato * * @param string $tipologia * @param ?array $data * @return bool */ public function request(?array $data = [], ?string &$message = "") : bool { global $config; // Prevalorizziamo $message $message = __("Errore interno sconosciuto"); // Verifichiamo che sia settata una tipologia di documento if(empty($this->tipo_documento) || empty($this->codice_fvoe)) { $message = __("Impossibile completare la richiesta: è necessario selezionare una tipologia di documento e un fascicolo OE."); return false; } // Verifichiamo che i servizi siano disponibili if (! $this->getServiceStatusBool(true)) { $message = __("Impossibile completare la richiesta: i server ANAC non sono raggiungibili. Riprova tra qualche minuto."); return false; } // Verifica se la richiesta può essere inviata self::$stm->execute([":tipo_documento" => $this->tipo_documento, ":codice_fvoe" => $this->codice_fvoe]); if(self::$stm->rowCount() > 0) { self::$stm->setFetchMode(PDO::FETCH_CLASS, 'FVOEDocument'); $document = self::$stm->fetch(); if(strtotime($document->data_fine_validita) > strtotime("+ 1 month")) { $message = __("Impossibile completare la richiesta: il documento non è in scadenza o scaduto."); return false; } } $this->fvoe = FVOE::init($this->codice_fvoe, FVOEInizializationType::CODICE_FVOE); $this->fascicolo = $this->fvoe->fascicolo; $documento = new Salva(); $documento->debug = false; $documento->nome_tabella = "b_fvoe_documenti"; $documento->operazione = "INSERT"; $documento->expect = ["codice_fvoe", "tipo_documento", "oggetto", "stato_richiesta"]; $documento->oggetto = [ "codice_fvoe" => $this->codice_fvoe, "tipo_documento" => $this->tipo_documento, "oggetto" => $this->oggetto, "stato_richiesta" => $this->stato_richiesta ]; self::$stmDocumento->execute([":tipo_documento" => $this->tipo_documento, ":codice_fvoe" => $this->codice_fvoe]); if(self::$stmDocumento->rowCount() > 0) { self::$stmDocumento->setFetchMode(PDO::FETCH_CLASS, 'FVOEDocument'); $doc = self::$stmDocumento->fetch(); $documento->operazione = "UPDATE"; $documento->expect[] = "codice"; $documento->oggetto["codice"] = $doc->codice; } $this->codice = $documento->oggetto["codice"] = $documento->save(); $operazione = new Salva(); $operazione->debug = false; $operazione->nome_tabella = "b_fvoe_operazioni"; $operazione->operazione = "INSERT"; $operazione->expect = ["codice_documento", "codice_partecipante", "codice_lotto", "operazione", "codice_fvoe", "tipo_documento"]; $operazione->oggetto = [ "codice_fvoe" => $this->codice_fvoe, "codice_documento" => $this->codice, "codice_partecipante" => $this->fvoe->info["uuid_partecipante"], "tipo_documento" => $this->tipo_documento, "codice_lotto" => $this->fvoe->info["codice_lotto_npa"], "operazione" => "RICHIESTA-DOCUMENTO" ]; date_default_timezone_set('Europe/Rome'); $claims = $this->getAnacJwsCustomClaims($this->api_config["fvoe"]["endpoint"]); // Andiamo a verificare che ci sia un'operazione di richiesta documento in attesa $richiesta_documento = FVOE::fetchLastOperation($this->codice, null, null, "RICHIESTA-DOCUMENTO", null, false); // Se c'è if (! empty($richiesta_documento)) { // Popoliamo il messaggio di errore $message = __("Errore durante la richiesta del documento"); $operazione->operazione = "UPDATE"; $operazione->expect = ["codice", "esito", "risultato"]; $operazione->oggetto = $richiesta_documento; // Andiamo a chiedere al server lo stato della richiesta di accesso $request = [ "idRichiesta" => $richiesta_documento["id_richiesta"] ]; $apiMessage = ""; $result_str = $this->apiCall("fvoe", "check-document-request", $request, $claims, [], $apiMessage); if (is_json($result_str)) { $message = __("Impossibile verificare l'esito dell'operazione. Contatta l'Help Desk tecnico."); $result = json_decode($result_str); $operazione->oggetto["risultato"] = serialize($result); $operazione->oggetto["esito"] = "KO"; if ($result->status == 200 && ! empty($result->result)) { global $config; $message = null; foreach ($result->result as $document_request) { if($document_request->idRichiesta == $richiesta_documento["id_richiesta"]) { $documento->operazione = "UPDATE"; $documento->expect = ["codice", "stato_richiesta", "id_documento"]; $documento->oggetto = [ "codice" => $this->codice, "stato_richiesta" => $document_request->stato->codice, "id_documento" => $document_request->idDocumento ?? null, ]; $documento->save(); } } $operazione->oggetto["esito"] = "OK"; $operazione->save(); return true; } $operazione->save(); return false; } return false; } // Verifico se è necessario trasmettere dati aggiuntivi if(! empty(self::$tipologia_documenti[$this->tipo_documento]["modelloDati"]) && empty($data)) { $message = __("Impossibile completare la richiesta: il documento richiede dati aggiuntivi non presenti nel form."); return false; } // Creiamo la richiesta di accesso al documento $request = [ "chiaveAccesso" => simple_decrypt($this->fvoe->info["chiave_accesso"], $config["simple_encrypt"]["chiave_fvoe"]), // "codiceFiscaleSoggetti" => "", "tipoDocumento" => json_encode([ // "idTipologica" => "tipoDocumento", "codice" => $this->tipo_documento ]), // "datiRichiesta" => "" ]; if(! empty(self::$tipologia_documenti[$this->tipo_documento]["modelloDati"])) { $form = self::getFormRequest($this->tipo_documento); $types = $this->getFormInputsTypes($form["content"]); self::setDocumentRequestFormData($data, $types); $request["datiRichiesta"] = json_encode($data); // $request["datiRichiesta"]["type"] = str_replace('_', '', self::$tipologia_documenti[$this->tipo_documento]["modelloDati"]); } $operazione->expect[] = "richiesta"; $operazione->oggetto["richiesta"] = serialize($request["datiRichiesta"] ?? null); // Effettuiamo la richiesta $apiMessage = ""; $claims = $this->getAnacJwsCustomClaims($this->api_config["fvoe"]["endpoint"]); // $result_str = $this->apiCall("fvoe", "documents-request", $request, $claims, [], $apiMessage); $result_str = $this->apiCall("fvoe", "request-document", $request, $claims, [], $apiMessage); $result = self::manageANACResponse($result_str, $message); if($result === null) { $message = empty($apiMessage) ? $message : $apiMessage; return false; } $message = __("Errore in fase di invio della richiesta."); // Verifichiamo la risposta // Se non ci viene fornita una risposta con successo if ($result->status !== 200) { // Aggiorniamo lo status $operazione->expect[] = "esito"; $operazione->oggetto["esito"] = "KO"; $operazione->expect[] = "risultato"; $operazione->oggetto["risultato"] = serialize($result); $operazione->save(); $message = __("Errore in fase di richiesta del documento."); if(! empty($result->errori)) { $dictionary = NuovaPiattaformaAppalti::getErrorsDictionary(); foreach ($result->errori as $errore) { $errorInfo = $dictionary[$errore->codice ?? null] ?? []; if(! empty($errorInfo["descrizione"]["it"]) && $errorInfo["codice"]) { $message = "{$errorInfo["codice"]} {$errorInfo["descrizione"]["it"]}"; } } } elseif(! empty($result->title)) { $message = "{$message} {$result->title}"; } return false; } // Conferma andata con successo $operazione->expect[] = "id_richiesta"; $operazione->oggetto["id_richiesta"] = $result->idRichiesta; if ($operazione->save()) { $message = ""; return true; } return false; } /** * get the related FVOEDocumentsCollection * * @return FVOEDocumentsCollection */ public function documentsCollection() : FVOEDocumentsCollection { return new FVOEDocumentsCollection($this->codice_fvoe, $this->tipo_documento); } }