[
"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"]);
?>
}
}
/**
* Set form request data before send
*
* @param array &$data
* @param array $types
* @return void
*/
private static function setDocumentRequestFormData(array &$data, array $types) {
foreach ($types as $key => $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);
}
}