scheda["codice"])) return []; if (!empty($this->suggestionsCache)) { return $this->suggestionsCache; } $integrationPath = NuovaPiattaformaAppalti::INTEGRATION_PATHS[$this->fascicolo["modulo_riferimento"]]; if (empty($this->fascicolo["id_riferimento"])) { return []; } global $root; $this->info["modulo"] = $this->fascicolo["modulo_riferimento"]; $this->info["codice_elemento"] = $this->fascicolo["id_riferimento"]; $integration = []; if (!file_exists("{$root}/backend/{$integrationPath}")) { return []; } $callIntegration = function ($path) use (&$integration, $root) { $contents = require "{$root}/backend/{$path}"; if (is_array($contents)) { $integration = array_merge($integration, $contents); } }; $callIntegration($integrationPath); $this->integration = $integration; $stazioneAppaltante = $this->integration["STAZIONEAPPALTANTE"] ?? []; $this->suggestionsCache = []; #region Suggerimenti Generali $this->addSuggestion( "appalto.linkDocumenti", $this->integration["PUBLIC_URL"] ?? null, self::urlSuggetionMode() ); $_oggetto = isset($this->integration["GENERAL"]["oggetto"]) ? self::cleanSuggestion($this->integration["GENERAL"]["oggetto"], "appalto.datiBase.oggetto") : ""; if (!empty($_oggetto)) { $this->addSuggestion("appalto.datiBase.oggetto", $_oggetto); } if ($this->fascicolo["modulo_riferimento"] == "gare") { $this->addSuggestionsGara(); } // Prezzo di riferimento $prezzoRiferimento = $this->integration["GENERAL"]["prezzoRiferimento"] ?? null; if ($prezzoRiferimento !== null) { $this->addSuggestion("appalto.datiBase.importo", currency_format($prezzoRiferimento)); } // Codice Appalto e stazione appaltante principale if (!empty($stazioneAppaltante)) { $letter = "P"; if (DEVELOP_ENV) { global $config; if (isset($config["npaLetter"])) { $letter = $config["npaLetter"]; } } // Generiamo il codice appalto $versione_interna = $this->fascicolo["versione_interna"] ?? 0; if($versione_interna >= 4) { $codiceAppalto = $this->integration["GENERAL"]["codiceAppalto"]; if (!empty($this->fascicolo["revisione_fascicolo"])) { $codiceAppalto .= "_v{$this->fascicolo['revisione_fascicolo']}"; } } else { $codiceAppalto = strtoupper("{$letter}{$_SESSION["ente"]->codice}-E{$stazioneAppaltante["codice"]}-{$this->integration["MODULO"]}-ID-{$this->oggetto->info["id"]}"); if (!empty($this->fascicolo["revisione_fascicolo"])) { $codiceAppalto .= "_rev{$this->fascicolo['revisione_fascicolo']}"; } } // Se siamo una modifica od una rettifica, allora prendiamo il codice appalto della scheda che stiamo andando a rettificare if(!empty($this->scheda["codice_modifica"]) || !empty($this->scheda["codice_rettifica"])) { $codiceSchedaOriginale = empty($this->scheda["codice_modifica"]) ? $this->scheda["codice_rettifica"] : $this->scheda["codice_modifica"]; $schedaOriginale = NuovaPiattaformaAppalti::fetchSchede(["dati"], ["codice" => $codiceSchedaOriginale], false, false); if(!empty($schedaOriginale) && valid_json($schedaOriginale["dati"], $schedaOriginale, true)) { $codiceAppaltoOriginale = DotPath::Get($schedaOriginale, "anacForm.appalto.codiceAppalto"); if(!empty($codiceAppaltoOriginale)) { $codiceAppalto = $codiceAppaltoOriginale; } } } if($this->fascicolo["codice_appalto"] !== $codiceAppalto) { $this->updateFascicolo(["codice_appalto" => $codiceAppalto]); } $this->addSuggestion("appalto.codiceAppalto", $codiceAppalto); $this->addSuggestion("stazioniAppaltanti.0.codiceFiscale", $stazioneAppaltante["cf"]); $this->addSuggestion("stazioniAppaltanti.0.codiceAusa", $stazioneAppaltante["codice_ausa"]); $this->addSuggestion("datiContratto.codiceAusa.0", $stazioneAppaltante["codice_ausa"]); } $idPianificazione = $this->fascicolo["id_pianificazione"] ?? ""; $this->addSuggestion( "appalto.idPianificazione", $idPianificazione, empty($idPianificazione) ? NPASuggestionType::AUTOFILL : NPASuggestionType::FORCED ); #endregion // Per una scheda di lotto usiamo solo il lotto corrente, altrimenti tutti $codice_lotto_npa = $this->scheda["codice_lotto"] ?? 0; if (!empty($codice_lotto_npa)) { $this->addSuggestionsLotto($codice_lotto_npa); } else { foreach ($this->getLots() as $lot) { $this->setLotto($lot["codice_lotto_npa"]); $ignoreDeserta = false; if(isset($this->scheda["id_scheda"])) { $idScheda = $this->scheda["id_scheda"]; $metadata = $this->metadataSchede[$idScheda]; $ignoreDeserta = $metadata['categoria'] === 'affidamento'; } $agg = $this->getPartecipanti(true); if($ignoreDeserta && ($lot["stato"] == 95 || empty($agg))) { continue; } $this->unsetLotto(); $this->addSuggestionsLotto($lot["codice_lotto_npa"]); } } return $this->suggestionsCache; } /** * Aggiunge i suggerimenti relativi alle scadenze della procedura * * @return void */ private function addSuggestionsScadenze(int $indiceLotto) { if (empty($this->integration["ROUNDS"])) return; $isLotto = false; if(!empty($this->scheda["codice"])) { $idScheda = $this->scheda["id_scheda"]; $metadata = $this->metadataSchede[$idScheda]; $isLotto = $metadata["lotto"]; } $scadenzaOfferte = null; $scadenzaInviti = null; $rounds = $isLotto ? [end($this->integration["ROUNDS"])] : $this->integration["ROUNDS"]; foreach ($rounds as $round) { if (empty($round["scadenza"])) continue; if ($round["infoFase"]["graduatoria"]) { $scadenzaOfferte = mysql2datetime($round["scadenza"]); } else { $scadenzaInviti = mysql2datetime($round["scadenza"]); } } if($scadenzaOfferte !== null) { $this->addSuggestion( "lotti.$indiceLotto.datiBaseTerminiInvio.oraScadenzaPresentazioneOfferte", $scadenzaOfferte, NPASuggestionType::FORCED ); $this->addSuggestion( "elencoSoggetti.$indiceLotto.dataScadenzaPresentazioneOfferta", $scadenzaOfferte, NPASuggestionType::FORCED ); } elseif($scadenzaInviti !== null) { $this->addSuggestion( "lotti.$indiceLotto.datiBaseTerminiInvio.scadenzaPresentazioneInvito", $scadenzaInviti, NPASuggestionType::FORCED ); } } /** * Aggiunge un suggerimento alla cache dei suggerimenti * * @param string $tag Dotpath della chiave del suggerimento (senza anacForm.) * @param mixed $value Valore del suggerimento * @param string $forced * @return void */ private function addSuggestion(string $tag, $value, string $forced = NPASuggestionType::FORCED) { DotPath::set($this->suggestionsCache, "anacForm.$tag", [ "forced" => $forced, "value" => $value ]); } /** * Aggiunge i suggerimenti puramente relativi alle gare * * @return void */ private function addSuggestionsGara() { /** * @var gara */ $gara = $this->oggetto; // Strumenti svolgimento $procedura = $gara->getProcedura(); $accordoQuadro = false; switch ($procedura["key"]) { case "aq": case "aqe": $strumentiSvolgimento = 6; $accordoQuadro = true; break; case "mercato": case "meex": $strumentiSvolgimento = 3; break; case "sda": $strumentiSvolgimento = 7; break; default: $elaborazione = $gara->getRisposteElaborazione(); if (($elaborazione["asta"] ?? "N") == "S") { $strumentiSvolgimento = 2; } else { $strumentiSvolgimento = 5; } } $this->addSuggestion( "appalto.strumentiSvolgimentoProcedure.codice", $strumentiSvolgimento, NPASuggestionType::AUTOFILL ); // Asta elettronica $this->addSuggestion( "appalto.datiBaseStrumentiProcedura.astaElettronica", $strumentiSvolgimento == 2 ? "true" : "false", // True se strumenti svolgimento è asta elettronica, NPASuggestionType::AUTOFILL ); // Tipo procedura $tipoProcedura = $procedura["bdncp"][0] ?? null; if ($tipoProcedura !== null) { // Tipo procedura $this->addSuggestion( "appalto.datiBaseProcedura.tipoProcedura.codice", $tipoProcedura, NPASuggestionType::AUTOFILL ); } /*$this->addSuggestion( "appalto.datiBaseStrumentiProcedura.accordoQuadro.codice", "none", );*/ if(!empty($this->oggetto->info["derivazione"]) && $procedura["key"] == "aq") { $accordo = accordiQuadro::init($this->oggetto->info["derivazione"]); if(!empty($accordo->info["cig"])) { $this->addSuggestion( "appalto.cigAccordoQuadro", $accordo->info["cig"] ); } } } /** * Shorthand per aggiungere un suggerimento ai partecipanti * * @param integer $indiceLotto Indice del lotto * @param integer $indicePartecipante Indice del partecipante * @param string $key Chiave del suggerimento * @param mixed $value Valore del suggerimento * @param string $forced Forzato? * @return void */ private function addSuggestionPartecipante(int $indiceLotto, int $indicePartecipante, string $key, $value, string $forced = NPASuggestionType::FORCED) { $this->addSuggestion("elencoSoggetti.$indiceLotto.partecipanti.$indicePartecipante.$key", $value, $forced); $this->addSuggestion("elencoSoggettiRichiedenti.$indiceLotto.soggettiInteressati.$indicePartecipante.$key", $value, $forced); } /** * Aggiunge i suggerimenti relativi ai partecipanti * * @param integer $indiceLotto Indice del lotto * @param array $partecipanti Array contenente i partecipanti * @param array $invitati Array contenente gli invitati alla procedura * @return void */ private function addSuggestionsPartecipanti(int $indiceLotto, array $partecipanti, array $invitati = []) { if (!empty($invitati)) { $invitatiNonPartecipanti = array_filter($invitati, function ($invitato, $key) use ($partecipanti) { foreach ($partecipanti as $partecipante) { if (trim(strtoupper($partecipante["codice_fiscale"])) == trim(strtoupper($invitato["codice_fiscale"]))) { return false; } } return true; }, ARRAY_FILTER_USE_BOTH); $indiceInvitato = 0; if(!empty($invitatiNonPartecipanti)) { $this->locked_repetitions["invitatiCheNonHannoPresentatoOfferta"] = true; } foreach ($invitatiNonPartecipanti as $invitato) { $this->addSuggestion("elencoSoggetti.$indiceLotto.invitatiCheNonHannoPresentatoOfferta.$indiceInvitato.codiceFiscale", $invitato["codice_fiscale"]); $this->addSuggestion("elencoSoggetti.$indiceLotto.invitatiCheNonHannoPresentatoOfferta.$indiceInvitato.denominazione", $invitato["ragione_sociale"]); $indiceInvitato++; } } $this->locked_repetitions["partecipanti"] = true; $this->locked_repetitions["soggettiInteressati"] = true; foreach ($partecipanti as $partecipante) { $codice_partecipante = $partecipante["codice"]; // Preveniamo edge case in cui due entry di tabelle diverse // hanno lo stesso codice if(isset($partecipante["codice_padre"])) { $codice_partecipante += 1000000000; } $indicePartecipante = $this->indiciPartecipanti[$indiceLotto][$codice_partecipante] ?? count($this->indiciPartecipanti[$indiceLotto] ?? []); $this->indiciPartecipanti[$indiceLotto][$codice_partecipante] = $indicePartecipante; $idPartecipante = $partecipante["identificativo_partecipante"]; $codiceFiscale = $partecipante["codice_fiscale"]; $ragioneSociale = $partecipante["ragione_sociale"]; $paese = $partecipante["stato"] ?? null; $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "idPartecipante", $idPartecipante); $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "codiceFiscale", $codiceFiscale); $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "denominazione", $ragioneSociale); if (!empty($paese)) { $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "paeseOperatoreEconomico", $paese, NPASuggestionType::AUTOFILL); } if (!empty($partecipante["ruoloOE"])) { $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "ruoloOE.codice", $partecipante["ruoloOE"], NPASuggestionType::AUTOFILL); } if(!empty($partecipante["avvalimento"])) { $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "avvalimento", "true", NPASuggestionType::AUTOFILL); $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "tipologiaAvvalimento.codice", $partecipante["avvalimento"], NPASuggestionType::AUTOFILL); } else { $this->addSuggestionPartecipante($indiceLotto, $indicePartecipante, "avvalimento", "false", NPASuggestionType::AUTOFILL); } } } /** * Shortand per aggiungere un suggerimento per gli aggiudicatari * * @param integer $indiceLotto Indice del lotto * @param integer $indiceAggiudicatario Indice dell'aggiudicatario * @param string $key Chiave del suggerimento * @param mixed $value Valore del suggerimento * @param boolean $forced Forzato? * @return void */ private function addSuggestionAggiudicatario(int $indiceLotto, int $indiceAggiudicatario, string $key, $value, string $forced = NPASuggestionType::FORCED) { $this->addSuggestion("aggiudicazioni.$indiceLotto.partecipanti.$indiceAggiudicatario.$key", $value, $forced); $this->addSuggestion("aggiudicazioni.$indiceLotto.offertePresentate.$indiceAggiudicatario.$key", $value, $forced); $this->addSuggestion("partecipante.$key", $value, $forced); } /** * Aggiunge i suggerimenti relativi agli aggiudicatari * * @param integer $indiceLotto Indice del lotto * @param array $partecipanti Array contenente gli aggiudicatari * @return void */ private function addSuggestionsAggiudicatari(int $indiceLotto, array $partecipanti) { $affidamento = false; if(!empty($this->scheda["codice"])) { $metadata = $this->metadataSchede[$this->scheda["id_scheda"]]; $affidamento = str_contains(strtolower("affidamento diretto"), $metadata["evento"]); } foreach ($partecipanti as $partecipante) { // Per i non affidamenti prendiamo senza raggruppamenti o avvalimenti perchè sono già stati definiti if(!$affidamento && $partecipante["isChild"]) { continue; } $isAggiudicatario = ($partecipante["aggiudicatario"] ?? "N") == "S"; // negli affidamenti prendiamo solo gli aggiudicatari if($affidamento && !$isAggiudicatario) { continue; } // TODO Inserire anche i non aggiudicatari se necessario if(!$isAggiudicatario) continue; $this->locked_repetitions["aggiudicazioni"] = true; $codice_partecipante = $partecipante["codice"]; // Preveniamo edge case in cui due entry di tabelle diverse // hanno lo stesso codice if(isset($partecipante["codice_padre"])) { $codice_partecipante += 1000000000; } $idPartecipante = $partecipante["identificativo_partecipante"]; $codiceFiscale = $partecipante["codice_fiscale"]; $ragioneSociale = $partecipante["ragione_sociale"]; $paeseOE = $partecipante["stato"] ?? null; $indiceAggiudicatario = $this->indiciAggiudicatari[$indiceLotto][$codice_partecipante] ?? count($this->indiciAggiudicatari[$indiceLotto] ?? []); $this->indiciAggiudicatari[$indiceLotto][$codice_partecipante] = $indiceAggiudicatario; $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "idPartecipante", $idPartecipante); $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "codiceFiscale", $codiceFiscale); $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "denominazione", $ragioneSociale); $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "aggiudicatario", $isAggiudicatario ? "true" : "false"); $importoAggiudicazione = 0; if($isAggiudicatario) { $importoAggiudicazione = $this->lot["importoAggiudicazione"]; } if($importoAggiudicazione > 0) { $this->addSuggestion("comunicazione.importo", currency_format($importoAggiudicazione), NPASuggestionType::FORCED); } else { $this->addSuggestion("comunicazione.importo", "", NPASuggestionType::AUTOFILL); } $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "importo", currency_format($importoAggiudicazione), NPASuggestionType::AUTOFILL); if (!empty($paeseOE)) { $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "paeseOperatoreEconomico", $paeseOE, NPASuggestionType::OPTIONAL); $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "paeseOperatoreEconomico", $paeseOE, NPASuggestionType::OPTIONAL); } if (!empty($partecipante["ruoloOE"])) { $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "ruoloOE.codice", $partecipante["ruoloOE"], NPASuggestionType::AUTOFILL); $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "ruoloOE.codice", $partecipante["ruoloOE"], NPASuggestionType::AUTOFILL); } if(!empty($partecipante["avvalimento"])) { $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "avvalimento", "true", NPASuggestionType::AUTOFILL); $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "tipologiaAvvalimento.codice", $partecipante["avvalimento"], NPASuggestionType::AUTOFILL); } else { $this->addSuggestionAggiudicatario($indiceLotto, $indiceAggiudicatario, "avvalimento", "false", NPASuggestionType::AUTOFILL); } } } /** * Ottiene il lotto delle integrazioni a partire dal codice lotto NPA * * @param integer $codiceLottoNpa * @return bool */ private function getIntegrationLot(int $codiceLottoNpa): bool { // Otteniamo il lotto NPA $this->setLotto($codiceLottoNpa); $npaLot = $this->lot; // Cerchiamo la corrispondenza con i lotti passati dalle integrazioni foreach ($this->integration["LOT"] as $integrationLot) { if ($integrationLot["codice"] == $npaLot["codice"]) { $this->lot = array_merge($this->lot, $integrationLot); return true; } } return false; } /** * Cache di getPartecipanti per questioni di performance * * @var array */ private $cachePartecipanti = []; /** * Ritorna gli invitati alla procedura corrente * * @return array */ public function getInvitati(): array { if(empty($this->lot)) { return []; } $retval = []; if (!method_exists($this->oggetto, "getInvitati")) { return $retval; } // Otteniamo gli invitati $invitati = $this->oggetto->getInvitati(); if (!empty($invitati)) { $invitati = array_column($invitati, "codice_operatore"); $ooee = oeManager::getOE($invitati); if (!empty($ooee)) { foreach ($ooee as $oe) { $retval[] = [ "codice" => $oe["codice"], "codice_fiscale" => $oe["codice_fiscale_impresa"], "ragione_sociale" => $oe["ragione_sociale"], ]; } } } if (!method_exists($this->oggetto, "getInvitatiManuali")) { return $retval; } // Otteniamo gli invitati manuali $invitatiManuali = $this->oggetto->getInvitatiManuali(); if (!empty($invitatiManuali)) { foreach ($invitatiManuali as $oe) { $retval[] = [ "codice" => $oe["codice"], "codice_fiscale" => $oe["codice_fiscale_operatore"], "ragione_sociale" => $oe["denominazione"], ]; } } return $retval; } /** * Ottiene i partecipanti relativi a questo fascicolo * * @param boolean $only_aggiudicatari Se true vengono ritornati solo i partecipanti aggiudicatari * @param boolean $include_raggruppamenti Se true vengono inclusi anche i partecipanti dei raggruppamenti, condividendo lo stesso idPartecipante * @param boolean $noCache Se true non viene usata la cache * @return array|null */ public function getPartecipanti(bool $only_aggiudicatari, bool $include_raggruppamenti = true, bool $noCache = false, bool $subappalto = false): ?array { if(empty($this->lot)) { return []; } $codice_lotto_npa = $this->lot["codice_lotto_npa"]; $id_scheda = ($this->scheda["id_scheda"] ?? null); // Cache per velocizzare chiamate successive if (!$noCache && isset($this->cachePartecipanti[$codice_lotto_npa][$only_aggiudicatari ? 1 : 0][$include_raggruppamenti ? 1 : 0][$subappalto ? 1 : 0])) { return $this->cachePartecipanti[$codice_lotto_npa][$only_aggiudicatari ? 1 : 0][$include_raggruppamenti ? 1 : 0][$subappalto ? 1 : 0]; } if (! empty($this->oggetto) && method_exists($this->oggetto, "getPartecipanti")) { $shouldUseInvitati = $id_scheda === "S1" && ($this->oggetto->round["infoFase"]["graduatoria"] ?? false); $isExpired = $this->oggetto->info["stato"] >= 21 || !empty($this->oggetto->quickPanel); if (! $isExpired && is_a($this->oggetto, "gara")) { if (! ($shouldUseInvitati || ($id_scheda === "S1" && count($this->oggetto->getRounds()) > 1))) { return []; } } if ($shouldUseInvitati) { $participantsData = $this->getInvitati(); } else { $participantsData = $this->oggetto->getPartecipanti(); } $finalParticipantsData = []; if (!empty($participantsData)) { $participantsData = array_column($participantsData, null, "codice"); $versione_interna = $this->fascicolo["versione_interna"] ?? 0; foreach ($participantsData as &$participant) { if ($only_aggiudicatari && $participant["aggiudicatario"] !== "S") { continue; } // Generiamo comunque la versione v1 dell'idPartecipante perchè viene usata come fallback $idPartecipanteV1 = uuidgen_v4("{$participant["codice"]}-{$participant["codice_fiscale"]}-{$participant["ragione_sociale"]}" . "tuttogare_npa" . $this->fascicolo["codice"]); switch ($versione_interna) { case 3: case 4: // La v3 cerca di usare quello preso dalla tabella e fa fallback alla v1 $participant["identificativo_partecipante"] = !empty($participant["uuid"]) ? $participant["uuid"] : $idPartecipanteV1; break; case 2: case 1: $participant["identificativo_partecipante"] = $idPartecipanteV1; break; default: $md5 = md5("{$participant["codice"]}-{$participant["codice_fiscale"]}-{$participant["ragione_sociale"]}"); $participant["identificativo_partecipante"] = substr($md5, 0, 8) . '-' . substr($md5, 8, 4) . '-' . substr($md5, 12, 4) . '-' . substr($md5, 16, 4) . '-' . substr($md5, 20); break; } $participant["codice_lotto_npa"] = $codice_lotto_npa; $participant["ruoloOE"] = 3; // Ente unico $participant["isChild"] = false; // Ente unico // Dalla v2 andiamo pure a pescarci i raggruppamenti if ($include_raggruppamenti && $versione_interna >= 2 && isset($participant["uuid"])) { $raggruppamento = $this->oggetto->getRaggruppamento($participant["codice"]); if (!empty($raggruppamento)) { $participant["ruoloOE"] = 1; // Mandatario } if(method_exists($this->oggetto, "getAvvalimento")) { $avvalimento = $this->oggetto->getAvvalimento($participant["codice"]); $participant["has_avvalimento"] = !empty($avvalimento); } } if($shouldUseInvitati) { $participant["fromInvitati"] = true; unset($participant["ruoloOE"]); } $finalParticipantsData[] = $participant; // Dati di avvalimento e raggruppamento $subData = []; if (!empty($avvalimento)) { foreach ($avvalimento as $partecipanteAvvalimento) { $cf = trim(strtoupper($partecipanteAvvalimento["codice_fiscale_impresa"])); // Usiamo il codice fiscale come differenziante $target = &$subData[$cf]; $target = $partecipanteAvvalimento; $target["identificativo_partecipante"] = $participant["identificativo_partecipante"]; $target["codice_lotto_npa"] = $codice_lotto_npa; $target["codice_fiscale"] = $partecipanteAvvalimento["codice_fiscale_impresa"]; $target["ruoloOE"] = 4; // Ausiliaria $target["isChild"] = true; // Ausiliaria $target["avvalimento"] = $partecipanteAvvalimento["ruolo"]; $target["aggiudicatario"] = $participant["aggiudicatario"] ?? "N"; $target["uuid"] = uuidgen_v4("{$target["codice"]}-{$target["codice_fiscale"]}-{$partecipanteAvvalimento["ragione_sociale"]}" . "tuttogare_npa" . $this->fascicolo["codice"]); } } if (!empty($raggruppamento)) { foreach ($raggruppamento as $partecipanteGruppo) { $cf = trim(strtoupper($partecipanteGruppo["codice_fiscale_impresa"])); // Usiamo il codice fiscale come differenziante $target = &$subData[$cf]; // Se esiste già sovrascriviamo solo alcuni dati if(empty($target)) $target = $partecipanteGruppo; $target["identificativo_partecipante"] = $participant["identificativo_partecipante"]; $target["codice_lotto_npa"] = $codice_lotto_npa; $target["codice_fiscale"] = $partecipanteGruppo["codice_fiscale_impresa"]; $target["aggiudicatario"] = $participant["aggiudicatario"] ?? "N"; $target["isChild"] = true; // ruoloOE del raggruppamento prende priorità $target["ruoloOE"] = 2; // Mandante $target["uuid"] = uuidgen_v4("{$target["codice"]}-{$target["codice_fiscale"]}-{$partecipanteGruppo["ragione_sociale"]}" . "tuttogare_npa" . $this->fascicolo["codice"]); } } foreach($subData as $subPartecipante) { $finalParticipantsData[] = $subPartecipante; } } } if($subappalto) { $subappaltatori = $this->getPartecipantiFromSubappalto(); $subappaltatori = array_column($subappaltatori, null, "codice_fiscale"); foreach (array_column($subappaltatori, null, "codice_fiscale") as $codice_fiscale_subappaltatore => $subappaltatore) { if(array_search($codice_fiscale_subappaltatore, array_column($finalParticipantsData, "codice_fiscale")) === false) { $finalParticipantsData[] = $subappaltatore; } } } if (!$noCache) { $this->cachePartecipanti[$codice_lotto_npa][$only_aggiudicatari ? 1 : 0][$include_raggruppamenti ? 1 : 0][$subappalto ? 1 : 0] = $finalParticipantsData; } return $finalParticipantsData; } return null; } /** * Recupera i partecipenti trasmessi nelle schede di subappalto ANAC (RSU1) * * @return Array */ public function getPartecipantiFromSubappalto() : Array { $retval = []; $select = ["codice", "dati"]; $filters = [ "codice_npa" => $this->fascicolo["codice"], "codice_lotto" => $this->lot["codice_lotto_npa"], "stato" => [">=", 50], "errore" => "NO", "id_scheda" => "RSU1" ]; $schede = self::fetchSchede($select, $filters, false, true); if(! empty($schede)) { foreach($schede as $scheda) { $info = json_decode($scheda["dati"], TRUE); $retval[] = [ 'codice' => null, 'codice_operatore' => null, 'codice_round' => null, 'codice_lotto' => $this->lot['codice'], 'stato' => 'IT', 'codice_fiscale' => $info['anacForm']['subappalto']['codiceFiscaleDittaACascata'], 'ragione_sociale' => $info['anacForm']['subappalto']['denominazioneDittaACascata'] ?? 'OPERATORE ECONOMICO', 'ragione_sociale' => 'OE ' . $info['anacForm']['subappalto']['codiceFiscaleDittaACascata'], 'aggiudicatario' => 'N', 'posizione' => '0', 'utente_modifica' => '4', 'timestamp' => '', 'uuid' => generateUUIDFromString(md5('OE-SUBAPPALTO-SCHEDA-RSU1-'.$scheda['codice'].'-'.$info['anacForm']['subappalto']['codiceFiscaleDittaACascata'])), 'identificativo_partecipante' => generateUUIDFromString(md5('OE-SUBAPPALTO-SCHEDA-RSU1-'.$scheda['codice'].'-'.$info['anacForm']['subappalto']['codiceFiscaleDittaACascata'])), 'codice_lotto_npa' => $this->lot["codice_lotto_npa"], 'ruoloOE' => 3, "isSubAppaltatore" => true, 'isChild' => false, 'has_avvalimento' => false, ]; // if(isset($info["anacForm"]["subappalto"]["codiciFiscaliOE"]) && is_array($info["anacForm"]["subappalto"]["codiciFiscaliOE"])) { // foreach ($info["anacForm"]["subappalto"]["codiciFiscaliOE"] as $codice_fiscale) { // if(! empty($codice_fiscale)) { // $retval[] = [ // 'codice' => null, // 'codice_operatore' => null, // 'codice_round' => null, // 'codice_lotto' => $this->lot['codice'], // 'stato' => 'IT', // 'codice_fiscale' => $codice_fiscale, // 'ragione_sociale' => 'OPERATORE ECONOMICO ' . $codice_fiscale, // 'aggiudicatario' => 'N', // 'posizione' => '0', // 'utente_modifica' => '4', // 'timestamp' => '', // 'uuid' => generateUUIDFromString(md5('OE-SUBAPPALTO-SCHEDA-RSU1-'.$scheda['codice'].'-'.$codice_fiscale)), // 'identificativo_partecipante' => generateUUIDFromString(md5('OE-SUBAPPALTO-SCHEDA-RSU1-'.$scheda['codice'].'-'.$codice_fiscale)), // 'codice_lotto_npa' => $this->lot["codice_lotto_npa"], // 'ruoloOE' => 3, // "isSubAppaltatore" => true, // 'isChild' => false, // 'has_avvalimento' => false, // ]; // } // } // } } return $retval; } return []; } /** * Shorthand per aggiungere un suggerimento al quadroEconomicoStandard * * @param integer $indiceLotto Indice del lotto * @param string $key Chiave del suggerimento * @param mixed $value Valore del suggerimento * @param string $forced Forzato? * @return void */ private function addSuggestionLotto(int $indiceLotto, string $key, $value, string $forced = NPASuggestionType::FORCED) { $this->addSuggestion("lotti.$indiceLotto.$key", $value, $forced); $this->addSuggestion("aggiudicazioni.$indiceLotto.$key", $value, $forced); } /** * Aggiunge i suggerimenti relativi ad un lotto * * @param integer $codiceLottoNpa Codice lotto NPA * @return void */ private function addSuggestionsLotto(int $codiceLottoNpa) { $oldLot = $this->lot; try { if(!$this->getIntegrationLot($codiceLottoNpa)) { return; } $lot = $this->lot; $this->locked_repetitions["lotti"] = true; $this->locked_repetitions["elencoSoggetti"] = true; $this->locked_repetitions["elencoSoggettiRichiedenti"] = true; $this->locked_repetitions["elencoIncarichi"] = true; $indiceLotto = $this->indiciLotto[$codiceLottoNpa] ?? count($this->indiciLotto); $this->indiciLotto[$codiceLottoNpa] = $indiceLotto; // Popoliamo la select dei partecipanti $aggiudicatariNoRaggruppamenti = $this->getPartecipanti(true, false); $aggiudicatari_options = []; foreach ($aggiudicatariNoRaggruppamenti as $partecipante) { $aggiudicatari_options[$partecipante["identificativo_partecipante"]] = "{$partecipante["ragione_sociale"]} ({$partecipante["codice_fiscale"]})"; } if (!empty($aggiudicatari_options)) { $this->addSuggestion( "datiContratto.idPartecipante", ["options" => $aggiudicatari_options], NPASuggestionType::OPTIONAL ); } $partecipanti = $this->getPartecipanti(false, true); $invitati = []; if ($this->scheda["id_scheda"] !== "S1") { $invitati = $this->getInvitati($codiceLottoNpa); } // Partecipanti if ($this->has_partecipanti || $this->scheda["id_scheda"] === "S1") { $this->addSuggestionsPartecipanti($indiceLotto, $partecipanti, $invitati); } // Aggiudicatari if ($this->has_aggiudicatari || in_array($this->scheda["id_scheda"], ["CM1", "CM2", "S4"], true)) { $this->addSuggestionsAggiudicatari($indiceLotto, $partecipanti); } /* TODO trovare una corrispondenza tra codici NUTS e codici ISTAT se possibile $this->addSuggestion( "lotti.$lotIndex.codIstat.codice", $codiceIstat, false ); */ // Dati base lotto $this->locked_repetitions["lotti"] = true; $this->addSuggestion("elencoIncarichi.$indiceLotto.cig", $lot["cig"]); $this->addSuggestion("elencoSoggettiRichiedenti.$indiceLotto.cig", $lot["cig"]); $this->addSuggestion("elencoSoggetti.$indiceLotto.cig", $lot["cig"]); $this->addSuggestion("aggiudicazioni.$indiceLotto.cig", $lot["cig"]); $this->addSuggestion("datiContratto.cig.$indiceLotto", $lot["cig"]); $this->addSuggestion("cig", $lot["cig"]); $this->addSuggestion("lotti.$indiceLotto.cig", $lot["cig"]); $tipologia = $this->integration["MODULO"] == "concorsi" ? "services" : $this->integration["GENERAL"]["tipologia"] ?? null; $this->addSuggestionLotto($indiceLotto, "datiBaseDocumenti.url", $this->integration["PUBLIC_URL"], self::urlSuggetionMode()); $this->addSuggestionLotto($indiceLotto, "lotIdentifier", "LOT-" . str_pad($lot["numero"], 4, "0", STR_PAD_LEFT)); $this->addSuggestionLotto($indiceLotto, "datiBase.oggettoContratto.codice", $tipologia); $this->addSuggestionLotto($indiceLotto, "datiBase.oggetto", self::cleanSuggestion($lot["oggetto"], "aggiudicazioni.$indiceLotto.datiBase.oggetto")); // scadenzaPresentazioneInvito e oraScadenzaPresentazioneOfferte, iterare tutti i round ed in base a // alla tipologia, impostare (non forzatamente) la scadenza // La CPV $this->addSuggestionLotto($indiceLotto, "datiBaseCPV.tipoClassificazione.codice", "cpv"); $this->addSuggestionLotto($indiceLotto, "datiBaseCPV.cpvPrevalente.codice", $lot["cpv"]); // lotti.0.datiBaseDocumenti.lingue.codice => it $nuts = DotPath::get($this->integration, "GENERAL.extraInfo.gara.nuts"); if ($nuts) { $this->addSuggestionLotto($indiceLotto, "datiBaseContratto.codNUTS.codice", $nuts, NPASuggestionType::OPTIONAL); } $this->addSuggestionLotto($indiceLotto, "cupLotto.0", $lot["cup"]); $this->addSuggestionLotto($indiceLotto, "cup", $lot["cup"]); $this->addSuggestion("comunicazione.cup", $lot["cup"], NPASuggestionType::FORCED); $importoTotale = $lot["importoTotale"] ?? null; if ($importoTotale !== null) { $this->addSuggestionLotto( $indiceLotto, "finanziamenti.0.importo", currency_format($importoTotale), NPASuggestionType::AUTOFILL ); $this->addSuggestion("lotti.$indiceLotto.datiBase.importo", currency_format($importoTotale), NPASuggestionType::AUTOFILL); } // if (isset($lot["criterio"])) { $this->addSuggestion("lotti.$indiceLotto.datiBaseAggiudicazione.criteriAggiudicazione.codice", $lot["criterio"] == "vantaggiosa" ? "quality" : "price"); } // $this->addSuggestion("lotti.$lotIndex.datiBaseTerminiInvio.oraScadenzaPresentazioneOfferte"]; $this->addSuggestionLotto($indiceLotto, "quadroEconomicoStandard.impForniture", "0.00", NPASuggestionType::AUTOFILL); $this->addSuggestionLotto($indiceLotto, "quadroEconomicoStandard.impServizi", "0.00", NPASuggestionType::AUTOFILL); $this->addSuggestionLotto($indiceLotto, "quadroEconomicoStandard.impLavori", "0.00", NPASuggestionType::AUTOFILL); $this->addSuggestionLotto($indiceLotto, "quadroEconomicoStandard.impTotaleSicurezza", currency_format($lot["oneriNoRibasso"] ?? 0), NPASuggestionType::AUTOFILL); // Importo del lotto $importoRibassabile = currency_format($lot["importoRibassabile"] ?? ($lot["importoBase"] ?? 0)); $chiaveImportoLotto = [ "services" => "impServizi", "supplies" => "impForniture", "works" => "impLavori", ][$tipologia] ?? null; if ($chiaveImportoLotto !== null) { $this->addSuggestionLotto( $indiceLotto, "quadroEconomicoStandard." . $chiaveImportoLotto, $importoRibassabile, NPASuggestionType::AUTOFILL ); } $sommeOpzioniRinnovi = $lot["importoOpzioni"] ?? 0; $this->addSuggestionQuadroEconomicoStandard($indiceLotto, "quadroEconomicoStandard.sommeOpzioniRinnovi", currency_format($sommeOpzioniRinnovi)); $this->addSuggestion("lotti.$indiceLotto.opzioniRinnovi", ($sommeOpzioniRinnovi > 0) ? "true" : "false", NPASuggestionType::AUTOFILL); if ($tipologia === "works") { if(!empty($lot["CAT_SOA"])) { foreach ($lot["CAT_SOA"] as $cs) { $indiceRipetibile = 0; $categoria = $this->pdo->go("SELECT id FROM b_categorie_soa WHERE codice = :codice AND id <> 'ALTRO'", [":codice" => $cs["codice_categoria"]])->fetch(PDO::FETCH_COLUMN, 0); if (!empty($categoria)) { if ($cs["prevalente"] == "S") { $this->addSuggestionLotto($indiceLotto, "categoria.codice", $categoria); } else { $this->addSuggestionLotto($indiceLotto, "categoriaScorporabile.$indiceRipetibile.codice", $categoria); $indiceRipetibile++; } } } } } else { $categoriaLotto = ["services" => "FS", "supplies" => "FB"][$tipologia] ?? "999"; $this->addSuggestionLotto($indiceLotto, "categoria.codice", $categoriaLotto, NPASuggestionType::AUTOFILL); } $this->addSuggestionsScadenze($indiceLotto); } finally { // Resettiamo tutto com'era if(empty($oldLot)) { $this->lot = null; if(isset($this->oggetto)) $this->oggetto->lotto = null; } else { $this->setLot($oldLot["codice_lotto_npa"]); } } } }