WooBiller

Rilasci stable

Changelog

Cronologia dei rilasci stable del plugin WooBiller. Si aggiorna in automatico ad ogni nuova pubblicazione.

v2.8.0

  • Nuovo (Scheda ordine): "Genera Documento" applica le impostazioni di un trigger. Il nuovo campo "Impostazioni da" propone il trigger attivo che corrisponde all'ordine per il tipo scelto (tipo cliente, metodo di pagamento, tipo documento scelto dal cliente; a parità il trigger fallito su quest'ordine, poi quello con lo Stato destinazione uguale allo stato attuale) e ne applica oggetto visibile e interno, nota del trigger (seguita da quella scritta nel riquadro), data, anagrafica cliente su Fatture in Cloud, sezionale (con il sezionale automatico e più categorie chiede quale usare), bollo, centro di ricavo, categorie, scadenza bonifico, descrizione prodotti, IVA personalizzata; sotto il campo un riepilogo indica oggetto, data, email, invio allo SdI, anagrafica e sezionale. Come il trigger invia l'email al cliente, la fattura elettronica allo SdI e scrive la nota "Emessa …". "Nessuna — generazione libera" è il comportamento di prima; se nessun trigger corrisponde il riquadro lo segnala. Fino alla 2.7.9 la generazione manuale ignorava tutte queste impostazioni: i documenti uscivano con l'oggetto predefinito, senza oggetto interno e senza email al cliente.
  • Nuovo (Scheda ordine): un riquadro per ogni trigger che non è riuscito a generare il documento, con motivo, data, "Riprova generazione documento" e "Ignora".
  • Nuovo (Errori): il rifiuto per limite di documenti del piano (Fatture in Cloud HTTP 409 "Hai superato il limite di documenti inclusi nella tua licenza per l'anno …") e i limiti del piano WooBiller sono riconosciuti come tali: avviso rosso "ordini con documento non generato" nelle pagine WooBiller e negli ordini, con l'elenco degli ordini e "Riprova tutti" — un ordine alla volta, dal più vecchio, con avanzamento; si ferma al primo nuovo errore di limite del piano o se il collegamento a Fatture in Cloud risulta revocato o mancante (non per i problemi di rete momentanei); salta e indica gli ordini annullati / rimborsati e quelli con trigger eliminato o disattivato. L'avviso comprende anche gli errori registrati dalle versioni precedenti; "Nascondi avviso" lo nasconde finché non si registra un nuovo errore. Nel log il limite di documenti di Fatture in Cloud compare ora come righe WARNING senza stack trace (prima ERROR con lo stack trace completo).
  • Fix (Riprova): ogni trigger fallito ha ora il suo errore: prima un secondo trigger fallito sovrascriveva il primo e qualsiasi documento emesso cancellava gli errori di tutti gli altri trigger. "Riprova" non risponde più "Documento rigenerato con successo" quando nessun documento è stato emesso (totale 0, sezionale da scegliere), rifiuta i trigger disattivati, registra il nuovo errore se fallisce di nuovo, lavora con il blocco per ordine come i trigger automatici e con l'email "Allega all'email ordine WC" invia l'email dedicata (prima il cliente non riceveva nulla). Se il documento risulta creato sul servizio ma non salvato in WooBiller, "Riprova" si rifiuta di emetterlo una seconda volta.
  • Fix (Trigger): "Mantieni documento precedente" disattivo — per Proforma, Preventivo e Rapporto di intervento il vecchio documento viene cancellato solo dopo che il nuovo è stato creato (un errore di creazione lo faceva perdere). Per Fattura, Fattura Elettronica, Ricevuta e Nota di credito resta cancellato prima, per non lasciare buchi nella numerazione; se la creazione fallisce, l'ordine riceve una nota con il numero cancellato e l'errore lo indica.
  • Fix (Trigger): Ripetizione "Se ordine modificato" e "Smart" non scattavano mai (impronta dell'ordine calcolata in due modi diversi e una condizione impossibile sullo stato). Ora, quando l'ordine raggiunge lo stato del trigger, il documento viene generato se il trigger non ne ha ancora creato uno per quell'ordine, oppure se prodotti, totale, metodo di pagamento, Codice Fiscale o Partita IVA sono cambiati rispetto al suo ultimo documento; i documenti già emessi contano come "non modificato". Attenzione: un trigger con queste Ripetizioni, finora mai scattato, dopo l'aggiornamento emette documenti ed email dal primo ordine che raggiunge il suo stato. "Smart" si comporta come "Se ordine modificato" (lo Stato partenza vale per tutti i trigger).
  • Fix (Fattura elettronica): una fattura elettronica creata a mano non poteva essere inviata allo SdI da WooBiller ("Verifica e invia a SdI" assente, e il server rispondeva "risulta già trasmessa"). Ora il pulsante compare e, prima di toccare il documento, WooBiller chiede a Fatture in Cloud lo stato SdI reale: se risulta già trasmessa o se Fatture in Cloud non risponde non fa nulla.
  • Fix (Scheda ordine): il link "Visualizza" dei documenti non faceva nulla; ora apre il documento.
  • Fix (Documenti): data e anno dei documenti generati senza data del trigger sono nel fuso orario del sito (prima UTC: tra mezzanotte e l'1 / le 2 uscivano con il giorno precedente).
  • Tecnico: nuova Services\BillingException (categorie license_limit, plan_limit, rate_limited, auth, network, server, validation, orphaned, unknown; HTTP status come codice, eccezione originale come previous) lanciata da FICService::create_document(), OpenAPIService::create_document() e dai limiti del piano in DocumentManager; nuova Triggers\GenerationFailures (meta _woobiller_last_error con una voce per trigger, compatibile con il formato precedente; ordini trovati con una query sulla tabella dei meta ordine, HPOS o legacy; riepilogo dell'avviso nel transient woobiller_failed_generations_summary, svuotato a ogni scrittura); Triggers\OrderFingerprint; TriggerEvaluator::static_mismatch() / changed_since(); TriggerManager::fire_trigger() restituisce l'esito, build_document_overrides(), after_document_created(), generate_with_trigger() (sotto il lock per ordine, con nuovo controllo dei doppioni), manual_trigger_choices(), with_order_lock(), same_type_documents(); "Genera Documento" lavora sotto il lock per ordine e annota "forzata" / "seconda … confermata" solo a documento creato; DocumentType::is_fiscal(); create_document() accetta l'override meta (source, applied_trigger_id, order_fingerprint in meta_json); i documenti manuali restano con trigger_id 0; AJAX woobiller_failed_generations, woobiller_dismiss_failure, woobiller_retry_document con trigger_id (risposta status, errore code/category), woobiller_generate_document con trigger_id e sezionale_category (codice sezionale_choice); nuova Admin\GenerationFailuresNotice (opzione woobiller_failed_generations_dismissed, azione admin_post_woobiller_dismiss_failures_notice); FICEInvoiceService::describe_row() restituisce state_unknown, nuovo is_resendable_status(), status() con parametro $strict; LicenseGate::documents_used_this_year() conta l'anno nel fuso del sito come doc_year; HTTP 402 classificato come limite del piano; nuove chiavi i18n JS manual_trigger_*, failure_quota_*, retry_all_*, repetitionOrderModified, repetitionSmart e sezionalePick* (prima non localizzate).

v2.7.9

  • Fix (Lista ordini): dopo un errore di generazione (es. limite documenti della licenza Fatture in Cloud) l'icona di errore e il pulsante "Riprova" restavano accanto al documento anche dopo averlo generato con "Genera Documento" dalla scheda ordine: solo un trigger andato a buon fine li toglieva, e "Riprova" a quel punto finiva sul blocco anti-duplicato. Ora "Genera Documento" li rimuove quando il documento creato è dello stesso tipo di quello fallito (una Proforma creata a mano non nasconde il "Riprova" di una Ricevuta fallita); lo stesso vale per l'icona di scelta del sezionale. Gli ordini già interessati si sistemano da soli: la colonna WooBiller non mostra più errore e "Riprova" se esiste un documento di quel tipo emesso dopo l'errore.
  • Migliorato (Lista ordini): il pulsante "Sincronizza documenti Fatture in Cloud" è ora accanto al titolo "Ordini", dopo "Aggiungi ordine", con lo stesso stile; prima stava nella barra dei filtri, attaccato al bordo superiore della tabella. Durante la sincronizzazione ruota l'icona del pulsante.
  • Tecnico: TriggerManager::clear_markers_for_doc_type( WC_Order, string $doc_type ) (rimuove _woobiller_last_error e _woobiller_pending_sezionale dello stesso tipo, chiamato da DocumentAjax::generate()) e TriggerManager::is_error_superseded( array $error, array $docs ) (usato da OrdersColumn, sola lettura); stili del pulsante di sincronizzazione spostati da inline a woobiller-admin.css (.wb-orders-sync), senza float.

v2.7.8

  • Fix (Trigger): un ordine poteva ricevere due documenti dello stesso tipo (es. due Ricevute con due numeri e due email al cliente) quando due trigger attivi con lo stesso tipo documento avevano condizioni che si intersecavano — alla stessa transizione di stato o a due transizioni consecutive. "Crea solo la prima volta" controllava solo i documenti creati dallo stesso trigger, e nessun passaggio verificava se l'ordine avesse già un documento di quel tipo. Ora per Ricevuta, Fattura e Fattura Elettronica un trigger non viene eseguito se l'ordine ha già un documento dello stesso tipo creato da un altro trigger o a mano: vince il primo in ordine di elenco, il secondo lascia una nota privata sull'ordine e una riga WARNING nel log (sempre scritta), senza consumare numeri, inviare email né mostrare l'icona "Riprova"; con "Riprova" o dal popup del sezionale compare un messaggio esplicito. Vale anche per il riavvio dopo il completamento dei dati fiscali. I documenti dello stesso trigger seguono come prima Ripetizione e Mantieni documento precedente; Nota di credito, Proforma, Preventivo e Rapporto di intervento sono esenti; Ricevuta e Fattura sullo stesso ordine restano permesse.
  • Fix (Trigger): due richieste simultanee che salvano un cambio di stato dello stesso ordine (es. webhook del gateway di pagamento e ritorno al negozio del cliente, due amministratori, un cron) potevano emettere due documenti prima che l'una vedesse quello dell'altra — anche dallo stesso trigger con "Crea solo la prima volta". Ora i trigger di un ordine vengono eseguiti una richiesta alla volta (lock MySQL per ordine, attesa massima 20 secondi): la seconda richiesta trova il documento creato dalla prima e non ne emette un altro. Se il lock non è disponibile (attesa scaduta, database senza GET_LOCK) il trigger procede come prima e il log riporta una riga WARNING.
  • Nuovo (Trigger): segnalazione dei trigger sovrapposti — al salvataggio il popup elenca i trigger con cui il nuovo si sovrappone (anche con Stato destinazione diverso) e lo segnala già mentre cambi i campi; il trigger viene salvato comunque. La pagina Trigger mostra un avviso con le coppie in conflitto e un'icona accanto al nome dei trigger coinvolti. Nuova colonna "Pagamento" nella tabella dei trigger.
  • Fix (Trigger): gli errori di salvataggio del popup (es. stato destinazione vuoto) finivano dietro il popup e non si vedevano: ora compaiono dentro il popup. Un doppio clic su "Salva" creava due trigger identici: il pulsante viene disabilitato durante la richiesta. L'ordinamento per trascinamento veniva salvato sui trigger sbagliati (indice e ID scambiati): ora persiste correttamente — ricontrolla l'ordine dei trigger dopo l'aggiornamento, perché decide quale trigger vince. La tabella mostrava "da: any" per lo stato partenza "Qualsiasi". Un metodo di pagamento non riconosciuto al salvataggio veniva silenziosamente salvato come "Qualsiasi": ora il salvataggio lo segnala.
  • Fix (Email): quando il PDF non poteva essere allegato all'email dedicata, la nota sull'ordine incolpava Fatture in Cloud ("non raggiungibile o file mancante") anche quando la causa era la cartella temporanea del server dentro la root pubblica (es. WP_TEMP_DIR che punta a uploads/) o non scrivibile, e il log suggeriva di definire WP_TEMP_DIR anche quando era proprio quella definizione il problema. Ora la nota indica la causa reale con il percorso rifiutato, la sua origine (WP_TEMP_DIR, cartella temporanea di PHP o ripiego su wp-content) e la soluzione; nelle pagine WooBiller e nella lista ordini compare un avviso con l'ultimo ordine interessato e il pulsante "Nascondi avviso", che sparisce da solo quando la cartella è a posto; nel popup del trigger, sotto "Modalità invio", compare lo stesso avviso. Il messaggio su Fatture in Cloud resta per i soli errori di download.
  • Nota: la cartella temporanea viene rifiutata anche quando sta sotto la root di WordPress (prima il controllo guardava solo wp-content, e senza separatore: public_html/tmp passava). I siti in questa situazione ricevono l'email con il solo link di download, la nota e l'avviso: definisci WP_TEMP_DIR in wp-config.php su una cartella fuori dalla root pubblica.
  • Fix (Fatture in Cloud): cliccando un documento eliminato su Fatture in Cloud (o appartenente a un account collegato diverso) compariva "Impossibile recuperare il PDF … controlla la connessione"; ora una pagina 404 spiega che il documento non esiste più su Fatture in Cloud, con il link alla scheda ordine e le due vie per allineare WooBiller (Sincronizza o elimina dalla scheda ordine), e nel log una sola riga WARNING senza stack trace; il token scaduto rimanda a Credenziali API. Il link di download del cliente sullo stesso documento risponde "Documento non più disponibile" (410, prima 503 "momentaneamente non disponibile") e non richiama Fatture in Cloud ai click successivi. Nessuna rimozione automatica. Il client HTTP verso Fatture in Cloud ha ora un timeout (30 s, 10 s di connessione): prima una chiamata poteva restare appesa.
  • Migliorato (Sincronizza): controlla solo i documenti del servizio attivo (le righe OpenAPI non vengono più cercate su Fatture in Cloud ed eliminate); raccoglie prima tutte le righe mancanti e le elimina alla fine (eliminando durante la paginazione ogni rimozione saltava un documento oltre i 100); se troppi documenti risultano mancanti (più di 3 e più del 10% dei controllati) non rimuove nulla e chiede conferma "Procedere comunque?" — un 404 di massa può essere un account collegato diverso; ogni rimozione scrive una riga nel log e una nota privata sull'ordine, che ricorda che un trigger potrà emettere un nuovo documento dello stesso tipo al prossimo cambio di stato; riusa lo stesso client per tutte le chiamate invece di crearne uno per documento.
  • Migliorato (Scheda ordine): "Genera Documento" chiede conferma se l'ordine ha già una Ricevuta, Fattura o Fattura Elettronica dello stesso tipo (con il numero esistente); confermando, il secondo documento viene creato e annotato nella timeline dell'ordine.
  • Tecnico: DocumentType::single_issue_types() / is_single_issue() con filtro woobiller_single_issue_doc_types (default ricevuta, fattura, fattura_elettronica); DocumentRepository::find_by_order_and_type(); guardia in TriggerManager::fire_trigger() (dopo "Salta se totale = 0", prima della sospensione per dati fiscali) con filtro woobiller_trigger_skip_duplicate_doc_type( bool $skip, TriggerModel, WC_Order, object $existing ); TriggerManager::handle_status_change() esegue i trigger di un ordine sotto lock MySQL GET_LOCK( 'woobiller_order_<hash db+prefisso>_<id>' ) (attesa 20 s, filtro woobiller_order_lock_timeout( int $seconds, int $order_id ), valore negativo = disattivato); nuova classe TriggerOverlapDetector; la risposta AJAX di salvataggio trigger porta overlaps e warnings; DocumentEmailSender::temp_dir_status() e resolve_attachment() con codice esito (ok, no_source, temp_under_webroot, temp_not_writable, fic_bootstrap, fic_download); nuova classe AttachmentNotice su admin_notices, opzione woobiller_attachment_failure (rimossa alla disinstallazione) e azione admin_post_woobiller_dismiss_attachment_notice; FICService::fetch_pdf_url_result() (stati ok, missing, auth, error; fetch_fresh_pdf_url() invariata) e http_status(); timeout in FICClient; sentinella missing nel transient woobiller_fic_pdf_url_{id}; DocumentManager::sync_with_active_service( bool $force = false ) restituisce anche missing e suspicious; codici AJAX sync_suspicious (ripost con force=1) e duplicate_doc_type (ripost con force_duplicate=1); nuove chiavi i18n JS close, trigger_overlap_*, duplicate_doc_confirm.

v2.7.7

  • Migliorato (Dati fiscali): quando il cliente completa i dati di fatturazione dalla pagina di riepilogo ordine, la nota privata sull'ordine riporta ora, oltre all'IP (troncato), i dati inseriti o modificati con il loro valore — tipologia cliente, Ragione Sociale, Codice Fiscale, Partita IVA, SDI, PEC, tipo documento, nell'ordine del checkout — e cosa succede dopo: generazione del documento riavviata subito, oppure al prossimo cambio di stato se nessun documento era in attesa.

v2.7.5

  • Migliorato (Dati fiscali): quando un documento viene messo in attesa e il plugin invia automaticamente al cliente l'email di richiesta dati di fatturazione, l'ordine riceve una nota privata con destinatario e dati mancanti, come già avveniva per l'invio manuale da "Chiedi dati mancanti". Se l'invio non riesce (ordine senza email di fatturazione o messaggio rifiutato dal sistema di posta) la nota lo dice, invece di lasciare l'informazione solo nel log.

v2.7.3

  • Fix (modulo di completamento dati): nella pagina di ringraziamento il modulo "Completa i dati di fatturazione" poteva mostrare tutti i campi insieme, senza la distinzione Privato / Azienda configurata per il checkout, e senza errori in console. Succedeva quando alla pagina non arrivava uno dei tre elementi da cui dipendeva la visibilità: lo script del modulo, il blocco di configurazione in linea (spesso rimosso o riordinato dai plugin di ottimizzazione/cache che combinano o ritardano gli script) oppure il foglio di stile, oppure quando la pagina di ringraziamento non è quella standard di WooCommerce. Ora lo stato iniziale — campi visibili e obbligatori per la tipologia preselezionata, tutti nascosti finché la tipologia non è scelta quando c'è il segnaposto — viene calcolato sul server; la configurazione viaggia dentro il modulo stesso; le righe nascoste usano anche l'attributo HTML hidden, efficace pure senza il foglio di stile del plugin; script e stile vengono caricati anche quando la pagina non è la pagina "ordine ricevuto" standard.
  • Tecnico: attributo data-wb-config sulla sezione #woobiller-completion (sostituisce wp_localize_script( 'woobiller-completion', 'WooBillerCompletion' ), che il JS legge ancora come ripiego); lo script parte su DOM ready; CompletionForm accoda gli asset anche al momento del rendering (riga di log di debug quando accade); regola CSS .woobiller-completion__row[hidden].

v2.7.2

  • Nuovo (Dati fiscali): opzione "Sospendi anche le ricevute" (attiva di default). Un ordine da Apple Pay / Google Pay arriva senza i campi WooBiller; fino alla 2.7.1 il trigger Ricevuta emetteva subito il documento su Fatture in Cloud senza Codice Fiscale e, esistendo già un documento, il recupero post-ordine (email di completamento, modulo in pagina di ringraziamento, pulsante "Chiedi dati mancanti") non partiva mai. Ora anche le ricevute vengono messe in attesa quando manca un dato obbligatorio a checkout. Attenzione corrispettivi giornalieri: un ordine in attesa non è idoneo finché il cliente non completa i dati; se il corrispettivo del giorno è già stato generato, l'ordine va gestito a mano (l'opzione si può disattivare).
  • Migliorato (Dati fiscali): un ordine senza tipologia cliente (pagamenti express, ordini creati da admin o via API) viene sospeso e la tipologia viene chiesta al cliente insieme ai campi previsti dal checkout — non si presume più nulla. Prima valevano i flag obbligatori globali: l'email elencava Codice Fiscale e Partita IVA insieme, e un negozio con la sola P.IVA obbligatoria per le aziende vedeva sospese le ricevute dei privati.
  • Migliorato (modulo di completamento dati): replica la selezione del checkout — tipologia cliente con le stesse opzioni, ordine e placeholder, Ragione Sociale per le aziende, Codice Fiscale / Partita IVA / SDI / PEC nell'ordine configurato in Campi Checkout, Tipo documento con le liste e i predefiniti per tipologia. Nessun campo compare finché la tipologia non è scelta; la Ragione Sociale viene salvata sull'ordine (e sul profilo del cliente registrato).
  • Migliorato (recupero automatico): per un cliente registrato i dati vengono copiati per primi dal suo ordine precedente più recente — tipologia, tipo documento, Codice Fiscale, Partita IVA, SDI, PEC e Ragione Sociale — poi dal profilo per i campi ancora vuoti; per gli ospiti restano gli ordini precedenti con la stessa email (ora anche con tipo documento e ragione sociale). Il predefinito del tipo documento viene applicato come al checkout.
  • Fix (Trigger): con Fatture in Cloud l'email dedicata con "Modalità invio: Allegato PDF" partiva senza allegato e senza link (il PDF esiste solo su Fatture in Cloud e nessuno lo scaricava). Ora il PDF viene scaricato da Fatture in Cloud al momento dell'invio, allegato con nome parlante (es. "Ricevuta-12-2026.pdf") e rimosso dal server a fine richiesta: nessuna copia salvata in uploads. Se il download fallisce l'email parte comunque con il link e l'ordine riceve una nota privata.
  • Fix (Trigger): la "Modalità invio" del trigger non veniva mai letta. Ora "Allegato PDF" allega e "Link di download" inserisce un pulsante verso un link firmato a scadenza (180 giorni), valido anche per gli ospiti e indipendente da wp-admin; per i documenti Fatture in Cloud apre sempre un PDF aggiornato, per quelli OpenAPI scarica il file locale. La riga "Modalità invio" compare solo per "Email dedicata": "Allega all'email ordine WC" allega sempre.
  • Fix (scheda ordine): l'azione "Invia … al cliente" del menu Azioni ordine non faceva nulla (hook registrato con un nome diverso da quello che WooCommerce lancia). Ora rispedisce l'email dedicata con il PDF; il documento deve appartenere all'ordine aperto.
  • Migliorato: se il sistema di posta rifiuta l'email del documento l'ordine riceve una nota privata (prima il log segnava "inviata" comunque); un rinnovo abbonamento sospeso per dati mancanti riceve una nota privata (l'email al cliente resta soppressa per i rinnovi).
  • Nota: la modalità "Allega all'email ordine WC" è invariata e non raggiunge l'email della stessa transizione che ha generato il documento (WooCommerce la invia prima che il trigger giri): per una consegna affidabile usa "Email dedicata".
  • Tecnico: opzione woobiller_fiscal_gate_receipts; il filtro woobiller_fiscal_gate_doc_types riceve ora quattro voci di default; OrderFiscalStatus::missing_fields() può restituire billing_customertype, billing_company e billing_doctype; FieldManager::customer_type_options() (spostata da IntegratedCheckout); FICService::download_pdf_to() / is_fic_host(); DocumentEmailSender::send() terzo parametro $send_type e attachment_filename(); nuova CustomerDocumentDownloadController su template_redirect (?woobiller_doc=), filtri woobiller_document_download_link_ttl e woobiller_document_download_rate_limits; OrderActions::register_dynamic_handler() su woocommerce_process_shop_order_meta (priorità 45); il riavvio automatico dopo il salvataggio admin passa da priorità 30 a 60 (dopo il blocco fatturazione WooCommerce).

v2.7.1

  • Nuovo (Campi Checkout): opzione "Codice Fiscale — uso sul documento per le aziende". Quando il campo Codice Fiscale è visibile anche alle aziende e viene usato per un dato diverso (es. "codice fiscale personale" di chi ordina, per il tesseramento), si può decidere cosa riportare sul documento fiscale per gli ordini di tipo Azienda: il valore del campo (predefinito, comportamento invariato), la Partita IVA (CF = P.IVA, come fa Fatture in Cloud) oppure nessun codice fiscale (solo Partita IVA, ammesso dal tracciato FatturaPA). I dati salvati su ordine e profilo non cambiano; i privati non sono interessati.
  • Migliorato: la scelta vale in un solo punto per tutti i canali — documento Fatture in Cloud, payload OpenAPI, PDF, reinvio SdI, anagrafica clienti Fatture in Cloud (ricerca e aggiornamento per codice fiscale) e controllo pre-emissione, che con l'opzione attiva declassa ad avviso un codice fiscale personale non valido (non va sul documento) e segnala la Partita IVA mancante.
  • Migliorato (scheda ordine): sotto il campo Codice Fiscale del pannello "WooBiller — Dati fatturazione" compare una nota su cosa riporta il documento quando l'opzione è attiva.
  • Tecnico: chiave document_azienda in fields_config[billing_codfisc] (valori field | piva | none), FieldManager::codfisc_document_policy(), OrderFiscalStatus::document_tax_code() / document_tax_code_uses_field(), normalizzatori FieldValidator::normalize_codice_fiscale() / normalize_partita_iva() (prima privati in CoreRules).

v2.7.0

  • Nuovo (trigger): "Controllo pre-emissione". Prima di creare il documento — e prima di consumare qualsiasi numero — il trigger verifica i dati dell'ordine con le regole del tracciato FatturaPA/SdI (Codice Fiscale e Partita IVA con cifra di controllo, CF e P.IVA entrambi assenti, CAP italiano a 5 cifre, sigla provincia, codice SDI di 7 caratteri, PEC valida, indirizzo/città/nazione presenti) e, con Fatture in Cloud, con i vincoli della sua API (connessione attiva, tipo IVA mappato per ogni aliquota — a 0% sempre in modo esplicito —, conto di saldo, coerenza fra righe e importo del pagamento). Tre modalità per trigger: "Blocca l'emissione se ci sono errori" (predefinita, anche per i trigger esistenti: l'ordine viene sospeso con una bandierina arancione nella colonna WooBiller e l'elenco dei problemi nella scheda ordine; salvando i dati corretti la generazione riparte da sola), "Solo avviso" (log + nota privata, emette comunque) e "Disattivato". Gli avvisi (azienda senza ragione sociale o P.IVA, SDI e PEC assenti, oggetto troppo lungo, caratteri non supportati, bollo senza le condizioni di legge, totale zero) non bloccano mai.
  • Nuovo (trigger): "Correzione automatica dei dati" (attiva di default): prima del controllo il plugin normalizza maiuscole in Codice Fiscale, codice SDI e sigla provincia, rimuove spazi, punti e prefisso "IT" dalla Partita IVA e gli spazi dal CAP, e annota ogni modifica in una nota privata dell'ordine. Non inventa mai dati mancanti.
  • Nuovo (scheda ordine): pulsante "Verifica dati" nel riquadro "WooBiller — Documenti": esegue il controllo sul tipo documento selezionato senza generare nulla e propone "Applica correzioni" quando esistono normalizzazioni disponibili. "Genera Documento", se trova errori, apre una finestra con l'elenco e i pulsanti "Applica correzioni e riprova" / "Genera comunque".
  • Nuovo (Fatture in Cloud): verifica XML e invio SdI reali. Creare una fattura elettronica su Fatture in Cloud non la trasmette allo SdI; fino alla 2.6.4 il plugin non chiamava l'endpoint di invio e segnava comunque il documento come trasmesso. Ora, con "Trasmissione SDI" attiva sul trigger, dopo la creazione il plugin chiede a Fatture in Cloud la verifica dell'XML e poi, secondo la nuova impostazione in "Impostazioni Fatture in Cloud → Fattura elettronica / SdI", esegue una prova di invio a vuoto (predefinito "Solo verifica": nulla viene trasmesso) oppure l'invio reale ("Verifica e invia allo SdI"). Gli errori restituiti da Fatture in Cloud compaiono nella scheda ordine (sotto il documento) e nella colonna WooBiller (icona nuvola rossa); il nuovo pulsante "Verifica e invia a SdI" aggiorna l'anagrafica sul documento esistente, riverifica e invia, senza creare un secondo documento. Lo stato SdI ("non inviata", "inviata", …) compare accanto al documento; le fatture elettroniche emesse con versioni precedenti non lo riportano.
  • Migliorato (Fatture in Cloud): per le fatture elettroniche a clienti esteri il codice destinatario inviato è "XXXXXXX" (regola SdI) e la provincia viene inviata solo se è una sigla di due lettere; lo stesso vale per l'anagrafica clienti sincronizzata.
  • Fix: nella colonna ordini il messaggio d'errore ripeteva il prefisso "Errore creazione documento Fatture in Cloud:" perché la pulizia cercava ancora il vecchio prefisso "FIC:".
  • Tecnico: nuovo namespace WooBiller\Preflight (PreflightValidator, CoreRules, FICRules, PreflightAutofix, PreflightGate, PreflightIssue/PreflightReport), colonne preflight_mode / preflight_autofix su wb_triggers (create automaticamente all'aggiornamento), meta ordine _woobiller_pending_preflight e _woobiller_einvoice_error, action AJAX woobiller_preflight_check e woobiller_resend_einvoice, filtro woobiller_preflight_issues, classi FICResolver / FICTotals estratte da FICDocumentAdapter (comportamento invariato) e FICDocumentAdapter::build_entity() pubblico, nuova FICEInvoiceService (xml_verify, send con dry_run, ei_status, error_reason, modifica dell'entità), opzione woobiller_fic_sdi_send_mode; submitted_tax vale 1 solo dopo una trasmissione reale e il meta_json dei documenti registra ei_status / ei_verify.

v2.6.4

  • Fix (licenza): disattivando il plugin da WP-Admin → Plugin e riattivandolo, la licenza spariva e andava reinserita a mano. Alla disattivazione il plugin liberava lo slot di attivazione sul portale ma conservava il token locale; al primo heartbeat dopo la riattivazione il portale rifiutava il token ormai stantio (domain_locked / invalid_token) e il plugin cancellava chiave, token, PAT e manifest. Ora la disattivazione del plugin non tocca la licenza, né in locale né sul portale: i cicli disattiva/riattiva (manutenzione, test, debug di conflitti) conservano la licenza attiva.
  • Fix (licenza): lo slot di attivazione sul portale viene liberato alla disinstallazione del plugin solo se è attiva l'opzione "Elimina tutti i dati (tabelle DB, opzioni) alla disinstallazione del plugin" (WooBiller → Impostazioni, riga "Disinstallazione"), l'unico percorso che cancella anche il token locale. Senza l'opzione, eliminare il plugin conserva licenza locale e slot sul portale: una reinstallazione successiva riparte senza reinserire la chiave. Per liberare lo slot in modo esplicito resta il pulsante "Disattiva / Cambia licenza" nella pagina Licenza.

v2.6.3

  • Migliorato (trigger): i campi "Oggetto (visibile)", "Oggetto interno" e "Contenuto nota" hanno ora accanto un pannello apribile "Placeholder disponibili" con l'elenco completo degli otto placeholder e una descrizione breve di ciascuno. Un clic sul placeholder lo inserisce nel campo, alla posizione del cursore.
  • Fix (trigger): la descrizione di "Oggetto (visibile)" rimandava ai placeholder "elencati sotto", ma l'unico elenco presente nel popup stava oltre cento righe più in basso, dentro la riga "Contenuto nota" — che è nascosta quando "Nota documento" non è "Testo personalizzato". Nel caso più comune il rimando puntava quindi al nulla.
  • Migliorato (trigger): "Oggetto interno" dichiara ora di supportare i placeholder (li ha sempre supportati, senza dirlo) e l'elenco in linea nella descrizione di "Contenuto nota" è stato sostituito dal pannello consultabile.

v2.6.1

  • Fix critico: gli ordini con un coupon di sconto non emettevano più il documento. Fatture in Cloud rispondeva 422 "Il totale dei pagamenti non corrisponde al totale da pagare" e il trigger restava in errore. Causa: le righe prodotto viaggiavano al prezzo di listino (get_subtotal(), pre-sconto) mentre l'importo del pagamento era il totale realmente incassato (get_total(), post-sconto) — su un ordine da 260 € con coupon -20 % il documento valeva 260 e il pagamento 208. Ora tutte le righe usano l'importo effettivamente addebitato al cliente, come già facevano spedizione e commissioni. Gli ordini rimasti in errore si riemettono con "Riprova" dalla scheda ordine. Nessun impatto sugli ordini senza coupon.
  • Fix: l'"Imponibile" mostrato sul PDF e inviato come importo_netto a OpenAPI era anch'esso pre-sconto, quindi più alto del totale del documento. Ora è la somma delle righe realmente inviate.
  • Fix: le righe con quantità 0 (bundle e omaggi generati da alcuni plugin) venivano inviate con quantità 0 e prezzo pieno, altra causa dello stesso errore 422. L'intero importo della riga finisce ora su una unità.
  • Fix: a ogni controllo aggiornamenti finivano nel log due righe ERROR PluginUpdater::log_api_error con HTTP 404 su woobiller.php e readme.txt. Non erano errori reali: la libreria di aggiornamento rileggeva quei file dall'albero del repository di distribuzione, che però contiene solo gli ZIP di release e non il sorgente. Le due richieste non vengono più eseguite — due chiamate HTTP in meno per controllo e log pulito dai falsi errori.
  • Fix: il filtro diagnostico sull'esito del controllo aggiornamenti era registrato su un nome di hook inesistente (mancava il prefisso puc_) e non ha quindi mai prodotto output.
  • Tecnico: nuova riconciliazione pre-volo in FICDocumentAdapter::to_fic_request() che replica il calcolo dei totali di Fatture in Cloud e, se diverge oltre il centesimo, logga imponibile, IVA, ritenuta e importo del pagamento PRIMA della chiamata API (diagnostica pura, nessuna correzione automatica di importi fiscali). Nuovi PluginUpdater::short_circuit_repo_content_requests() su pre_http_request e PluginUpdater::contents_api_prefix(); hook corretto in puc_request_update_result-woobiller; DocumentModel::$subtotal ora calcolato dalla somma di line_items invece che da $order->get_subtotal().

v2.6.0

  • Nuovo: pulsante "Chiedi dati mancanti" nel pannello "WooBiller — Dati fatturazione" della scheda ordine. Apre un popup con i dati obbligatori mancanti rilevati, il destinatario e la data dell'ultimo invio; su conferma manda al cliente la stessa email di completamento del recupero automatico (link alla pagina di riepilogo ordine con il modulo). Utile quando il cliente ha perso la mail, quando l'ordine non è stato messo in attesa dal gate o quando l'invio automatico è disattivato. L'invio manuale ignora l'opzione "Email di completamento" e il limite di una mail per ordine, e scrive una nota ordine. Se la mail non avrebbe effetto (ordine annullato, modulo in pagina di ringraziamento disattivato, sospensione documenti disattivata su ordine non in attesa, nessun dato mancante, documento già emesso, email assente) il popup lo spiega e l'invio è bloccato. Se dopo un invio manuale l'ordine viene messo in attesa dal gate, la mail automatica non parte una seconda volta.
  • Fix: le email inviate da WooBiller fuori dal cambio stato ordine (email di completamento dati dal pulsante manuale; email "separata" del documento dopo "Riprova", scelta sezionale o completamento dati da parte del cliente) arrivavano senza intestazione e piè di pagina del template email WooCommerce, perché il mailer veniva inizializzato dopo la costruzione del corpo.
  • Tecnico: nuova action AJAX admin woobiller_request_fiscal_data; nuovo meta ordine _woobiller_fiscal_request_sent (data, utente, contatore). La regola "il cliente può ancora completare i dati" è centralizzata in CompletionService::completion_blocker() ed è usata sia dal modulo in pagina di ringraziamento sia dall'invio manuale, così i due non possono divergere.

v2.4.0

  • Nuovo: scheda "Etichette campi" in Campi Checkout — etichetta e testo di aiuto personalizzabili per ogni campo WooBiller (Tipologia cliente, Ragione Sociale, Codice Fiscale, Partita IVA, Codice SDI, PEC, Tipo documento) e per le voci "Privato"/"Azienda" della select tipologia cliente. Le etichette valgono su checkout classico, checkout a blocchi, form di completamento dati fiscali, scheda ordine e profilo utente. Caso d'uso tipico: chiedere alle aziende il codice fiscale del legale rappresentante riusando il campo Codice Fiscale, reso visibile anche alle aziende e rinominato.
  • Nuovo: campo checkout "Tipo documento" — il cliente sceglie il documento fiscale che vuole ricevere. L'admin decide, separatamente per privati e aziende, se mostrare la select e quali tipi documento rendere selezionabili. La scelta è salvata sull'ordine (_woobiller_doctype) ed è modificabile dalla scheda ordine; i trigger scattano solo se il loro tipo documento coincide con la scelta del cliente, mentre campo vuoto o disabilitato lasciano il comportamento invariato. Disponibile su checkout classico, a blocchi e nel form di completamento dati.
  • Nuovo (Pro): override dell'aliquota IVA per singolo trigger, in un popup dedicato — "Forza un unico tipo IVA" su tutte le righe, oppure "Mappa per tax class" valida solo per quel trigger (le classi non mappate seguono la mappatura globale). Permette di emettere ricevute e fatture con tipi IVA diversi dallo stesso negozio. Solo Fatture in Cloud; verificato server-side dalla licenza.
  • Nuovo: placeholder {prodotto} (nome del primo prodotto dell'ordine) e {prodotti} (tutti i prodotti, separati da virgola, senza duplicati) per oggetto e nota del documento.
  • Nuovo: l'oggetto del documento viene troncato automaticamente al limite di Fatture in Cloud — 200 caratteri sulle fatture elettroniche, 255 sugli altri documenti — su confine di parola con "…", con avviso nel log. Prima un oggetto troppo lungo (tipico con {prodotti}) produceva un errore API all'emissione.
  • Miglioramento: nella mappatura "Aliquote IVA → Tipi IVA Fatture in Cloud" ogni opzione mostra anche l'ID Fatture in Cloud, nel formato "Descrizione (22%) - (ID: 12)", per distinguere tipi IVA con la stessa descrizione.
  • Tecnico: il checkout a blocchi ora legge etichette e opzioni dalla configurazione campi WooBiller (prima erano fisse nel codice). Due nuove colonne su wb_triggers (vat_override_mode, vat_override_json) create automaticamente all'aggiornamento.

v2.3.1

  • Fix: durante un guasto del portale la pagina impostazioni Fatture in Cloud non ritenta più il recupero di aliquote IVA e conti di pagamento a ogni caricamento. Un fallimento viene ora ricordato per 2 minuti: il 4 settembre 2026 questo comportamento produceva 10 chiamate al portale in 25 secondi e consumava il budget di refresh token della connessione. Il pulsante "Aggiorna" continua a forzare un recupero immediato.

v2.3.0

  • Nuovo: recupero automatico dei dati fiscali per gli ordini da pagamenti express (Apple Pay / Google Pay) che saltano il form di checkout — i campi mancanti (tipologia cliente, CF, P.IVA, SDI, PEC) vengono compilati dal profilo dell'utente registrato con la stessa email di fatturazione o dall'ordine più recente con la stessa email, senza mai sovrascrivere valori già presenti e copiando solo i campi coerenti con la tipologia cliente. Opzionale: l'ordine ospite viene assegnato al cliente registrato corrispondente.
  • Nuovo: quando i dati fiscali obbligatori restano mancanti, la generazione di Fatture, Fatture Elettroniche e Note di Credito viene messa in attesa (nessun numero documento consumato) invece di emettere documenti con dati vuoti. Le ricevute non vengono mai sospese, per non alterare il calcolo dei corrispettivi giornalieri. Lista tipi documento filtrabile via woobiller_fiscal_gate_doc_types.
  • Nuovo: modulo di completamento dati nella pagina di ringraziamento (order-received) — il cliente inserisce i dati mancanti subito dopo l'acquisto express e il documento viene emesso automaticamente. In parallelo parte una singola email con link sicuro (chiave ordine) per completare in un secondo momento. L'endpoint è write-once, con validazione bloccante, rate-limiting e lock di concorrenza; il modulo non premostra mai i dati recuperati agli ospiti.
  • Nuovo: icona dedicata "dati mancanti" nella colonna WooBiller della lista ordini e avviso nel metabox dell'ordine; completando i campi dal pannello "WooBiller — Dati fatturazione" e salvando, la generazione riparte da sola. Il pulsante "Riprova" con dati ancora mancanti mostra ora un errore esplicito con l'elenco dei campi.
  • Fix: sui checkout classici i pagamenti express di Stripe (payment request / express checkout element) non vengono più bloccati dalla validazione dei campi obbligatori WooBiller (il wallet falliva con "campo obbligatorio"): la validazione riconosce la richiesta express (payment_request_type/express_payment_type senza campi WooBiller nel POST), lascia passare il pagamento e traccia l'origine in _woobiller_express_origin; i dati vengono recuperati col nuovo flusso post-acquisto. Comportamento forzabile via filtro woobiller_express_checkout_origin.
  • Nuovo: tab "Dati fiscali" in Impostazioni con i toggle delle nuove funzioni (tutte attive di default; il recupero è disponibile solo in modalità checkout "Integrata"). La sospensione documenti si ripulisce da sola sugli ordini annullati/rimborsati/falliti; per i rinnovi di WooCommerce Subscriptions l'email di completamento è soppressa.
  • Tecnico: nuovo namespace WooBiller\FiscalData (OrderFiscalStatus, OrderReconciler, CompletionService, CompletionForm), nuovi meta ordine _woobiller_pending_fiscal_data, _woobiller_reconciled_at, _woobiller_express_origin; IntegratedCheckout::normalize_for_storage() ora pubblico; TriggerManager::record_error_on_order() ora pubblico.

v2.2.0

  • Nuovo: il plugin è ora tradotto in 13 lingue oltre all'italiano — inglese, francese, spagnolo, portoghese, tedesco, croato, sloveno, greco, polacco, olandese, rumeno, ungherese e lettone. I file di traduzione (.po/.mo) sono inclusi nella cartella languages/ e WordPress carica automaticamente la lingua del sito (text domain woobiller).
  • I nomi propri del sistema fiscale italiano restano invariati in tutte le lingue (Fatture in Cloud, OpenAPI, SDI, PEC, AdE), mentre i termini generici vengono localizzati (es. Partita IVA → VAT Number/USt-IdNr., Codice Fiscale → Tax Code).
  • Tecnico: aggiunto tools/build-translations.php (generatore .po/.mo con auto-verifica, senza dipendere da msgfmt/WP-CLI) e ripulito languages/woobiller.pot — bump Project-Id-Version a 2.2.0 e deduplicate 3 stringhe ripetute.

v2.1.0

  • Nuovo: condizione trigger "Metodo di pagamento" — il trigger si attiva solo se l'ordine usa il metodo di pagamento selezionato (default "Qualsiasi").
  • Nuovo: per i trigger su bonifico, opzione "Scadenza personalizzata" (1/2/3/7/30/60 giorni e 30/60 giorni fine mese) applicata come scadenza di pagamento sul documento Fatture in Cloud.
  • Nuovo: opzione trigger "Descrizione prodotto" — aggiunge a ogni riga del documento la descrizione breve o completa del prodotto WooCommerce.
  • Nuovo: retrocompatibilità WooFatture — alla prima installazione importa una sola volta i campi fiscali (CF, P.IVA, SDI, PEC) dai meta di WooFatture verso WooBiller, su ordini e profili cliente, senza mai sovrascrivere i valori già presenti.
  • Migliorato: la creazione di conti di saldo su Fatture in Cloud ora supporta i conti bancari con IBAN/SIA/CUC (riservata ai piani Pro/Agency).

v1.8.6

  • Fix: il pulsante "Connetti a Fatture in Cloud" via WooBiller restituiva "Portale ha risposto con HTTP 422". Causa: il pairing token generato dal plugin era 69 caratteri (UUID4 + suffisso random) ma il portale lo vincola a max 64 (la PK della tabella oauth_pairings è varchar(64)). Ora il token è un UUID4 puro a 36 caratteri — sempre 122 bit di entropia, ampiamente sufficienti per un monouso da 15 minuti.

v1.7.1

  • Fix: licenza scaduta non segnalata, updater che continuava a girare in silenzio — segnalato dall'utente. Causa: LicenseGate::state() calcolava lo stato confrontando solo il timestamp exp del JWT (con grace window di 7 giorni offline) e ignorava completamente plan_expires_at (scadenza della sottoscrizione). Se il piano scadeva mentre il token JWT era ancora valido (es. heartbeat che rinnovava proattivamente exp a copertura della grace), lo stato restava STATUS_ACTIVE, LicenseState::can_receive_updates() ritornava true, PluginUpdater::boot() wirava PUC e l'admin non vedeva nessuna notice. Nuovo stato dedicato STATUS_PLAN_EXPIRED: viene emesso quando plan_expires_at < now() ma il token JWT è ancora crittograficamente valido. In questo stato le funzionalità Pro restano abilitate (il cliente ha pagato per il periodo in cui sta emettendo documenti — LicenseState::is_pro() include ora anche PLAN_EXPIRED), ma l'updater è disabilitato e l'admin vede un banner persistente con CTA "Rinnova licenza" che punta a https://portal.woobiller.com/renew?key=<chiave>.
  • Stilizzazione moderna delle notice admin WooBiller — sia il banner persistente di stato licenza (LicenseAdminBanner) sia le notice transient di azione admin (NoticeManager: success/error/warning/info dopo salvataggi, import, ecc.) adottano ora un look distinto dal default WordPress: background colorato per severity (verde/blu/arancio/rosso), icone SVG circolari a sinistra, border radius 8px, box shadow, pulsante CTA inline stilizzato. Classi prefissate woobiller-notice per evitare collisioni con altri plugin. Nuovo file admin/css/woobiller-notices.css enqueued globalmente in wp-admin (~3KB) così le notice sono coerenti su qualunque schermata, non solo sulle pagine WooBiller. Icone usate da admin/img/icons/: icon-info-solid.svg, icon-alert-triangle.svg, icon-warning-alt.svg, icon-check-circle-solid.svg, icon-close.svg.
  • Helper LicenseAdminPage::renewal_url($key) — costante RENEWAL_URL = 'https://portal.woobiller.com/renew' + concatenazione ?key=<chiave_url_encoded> quando la chiave è disponibile, così il portale può pre-compilare il flusso di rinnovo senza richiedere all'admin di reincollarla. Usato dai banner STATUS_PLAN_EXPIRED e STATUS_GRACE_OVER.

v1.7.0

  • Fix: campi WooBiller invisibili al checkout quando billing_country non è preselezionato — la diagnostica 1.6.5 ha rivelato che su un cliente (formazionescienzemotorie.com) il filter IntegratedCheckout::add_billing_fields girava regolarmente e i 5 campi (billing_customertype, billing_codfisc, billing_piva, billing_sdi, billing_pec) erano effettivamente presenti nel DOM HTML server-side — ma sparivano visivamente. Causa identificata in public/js/woobiller-checkout.js: la funzione updateFieldVisibility() usava un check strict $('#billing_country').val() === 'IT' per decidere se mostrare i campi, e il branch else chiamava $row.hide() su tutti i field WooBiller. Sui siti senza billing_country preselezionato (utente loggato che non ha mai salvato un paese, oppure WooCommerce con "Posizione predefinita del cliente" lasciato vuoto e senza geolocation MaxMind), il primo render JS trovava val() === '' → tutti i campi WooBiller nascosti immediatamente al DOMContentLoaded.
  • Fix architetturale lato JS: separazione fra "gate di visibilità" (permissivo) e "gate di required + validazione" (strict). Nuovo helper isItalyOrUnset() che ritorna true quando il country è '' (non scelto) oppure 'IT' — usato SOLO in updateFieldVisibility() per la decisione show/hide. isItalySelected() (che ritorna true solo per IT esplicito) continua a guidare l'attributo required, l'asterisco label, runWooBillerValidation() sul submit, e attachBlurHandlers() per la validazione inline di CF/PIVA/SDI/PEC. Effetto pratico: l'utente che entra in checkout senza paese preselezionato vede i campi fiscali (label (facoltativo), niente asterisco); appena seleziona "Italia" parte la pre-compilazione del required-per-tipo; se invece sceglie un paese non italiano, i campi si nascondono e si azzerano come prima. Nessuna regressione su siti con default IT (in quel caso showForIT === isIT === true). Comportamento identico fra classic checkout e Fluid Checkout — la modifica non dipende da cfg.fluid_checkout_active.
  • Nuova opzione admin: "Paese predefinito al checkout" (WooBiller → Campi Checkout → in fondo alla sezione "Verifica indirizzo email"). Risolve un effetto collaterale fastidioso di WC: il setting "WooCommerce → Impostazioni → Generale → Posizione predefinita del cliente" su "Paese/regione del negozio" pre-imposta sia paese sia provincia (es. con base "Italia — Pisa", ogni cliente parte con provincia PI già selezionata, anche se vive a Roma). Non c'è modo nativo in WC di pre-impostare solo il paese senza la provincia/stato della base address. La nuova opzione WooBiller permette di scegliere un country code (o "Usa impostazione WooCommerce" per delegare a WC come prima) che viene applicato via i filtri ufficiali default_checkout_billing_country e default_checkout_shipping_country — solo il paese, niente forzatura della provincia. L'opzione è gated: se il country code stored non è più presente nella lista WC (es. l'admin ha attivato "Vendita solo a paesi specifici" dopo averla salvata), IntegratedCheckout::filter_default_country() ritorna il default WC originale invece di propagare un valore invalido.
  • Codice impattato: src/Admin/Pages/FieldsPage.php (lettura option default_billing_country + handler save con whitelist sui country WC), admin/views/pages/fields.php (nuova <select> con tutti i paesi WC + opzione di default vuota), src/Checkout/IntegratedMode/IntegratedCheckout.php (registrazione due filtri default_checkout_*_country + helper filter_default_country con guard sulla disponibilità del code).

v1.6.5

  • Diagnostica per il caso "campi WooBiller non compaiono": cliente con Fluid Checkout attivo + plugin 1.6.x ha riportato che i campi WooBiller (CF, P.IVA, SDI, PEC, Tipologia Cliente) sparivano dal checkout — anche dopo il fix 1.6.4 che gating-ava la registrazione block-fields. Senza accesso al sito è impossibile distinguere fra (a) la pagina checkout usa il block WC invece dello shortcode classic (e quindi IntegratedCheckout::add_billing_fields non gira mai), (b) la pagina usa il classic ma qualcosa downstream rimuove i campi, (c) i campi sono nel DOM ma nascosti da CSS/JS. Aggiungo qui logging diagnostico granulare che l'admin può attivare al volo.
  • IntegratedCheckout::add_billing_fields() emette ora un debug log ogni volta che il filter woocommerce_billing_fields viene chiamato: dice quanti billing fields sono nella collezione finale, quali campi WooBiller sono stati aggiunti effettivamente (billing_codfisc, billing_piva, ecc.), e se billing_email_confirmation è presente. Gated da "Log dettagliato" (Impostazioni → Avanzate). Quando l'admin abilita quel toggle e ricarica il checkout, può aprire WooCommerce → Stato → Log → source woobiller e vedere immediatamente: SE c'è una riga "filter fired, WooBiller fields in result: [...]" → il classic flow gira normalmente e i campi vengono aggiunti dal lato server; il problema è downstream (CSS / Fluid / altro plugin che li nasconde). SE NON c'è quella riga → il filter woocommerce_billing_fields non viene chiamato per niente — tipicamente perché la pagina checkout è configurata col block woocommerce/checkout invece dello shortcode [woocommerce_checkout].
  • FluidCheckoutCompat::is_active() ora usa anche is_plugin_active() oltre a class_exists('FluidCheckout'). Robustness contro falsi positivi: in alcuni scenari (OPcache stale, file lasciati da disinstall, plugin/temi che definiscono una classe con stesso nome) il class_exists da solo ritornava true anche su siti dove Fluid Checkout era stato disabilitato dall'admin. Effetto: il compat layer si attivava su siti che non usavano più Fluid, generando falsi messaggi console "Fluid Checkout detected" che disorientavano nella diagnostica. Detection in due step: fast path class_exists (evita autoload superfluo) + conferma is_plugin_active('fluid-checkout/fluid-checkout.php') con lazy require di wp-admin/includes/plugin.php sul frontend.

v1.6.4

  • Fix critico — campi WooBiller scompaiono sul classic checkout con Fluid Checkout (regressione 1.6.0): segnalato da un cliente che usa Fluid Checkout sopra il classic shortcode WC; dopo l'upgrade a 1.6.x, i campi WooBiller (Tipo Cliente, CF, P.IVA, SDI, PEC) non venivano più mostrati sul checkout. Causa: la nuova BlockCheckoutFields introdotta in 1.6.0 registrava i 5 campi via woocommerce_register_additional_checkout_field() (l'API ufficiale di WC), pensando che renderizzasse solo nel block checkout. Ma da WC ≈9.6 quell'API è stata unificata e renderizza i campi SIA nel block SIA nel classic checkout. Risultato: i campi venivano dichiarati DUE volte sul classic — una via woocommerce_billing_fields filter (IntegratedCheckout::add_billing_fields(), comportamento storico), una via additional fields API (BlockCheckoutFields::register_fields(), nuovo 1.6.0). Fluid Checkout, vedendo nomi/IDs sovrapposti tra i due set, ne nascondeva uno o entrambi nel suo layout → campi spariti.
  • Fix architetturale 1.6.4: la registrazione block è ora condizionale alla detection runtime "questa pagina checkout usa effettivamente il block?". BlockCheckoutCompat::register() differisce su init priority 5 e poi chiama should_register() che usa has_block('woocommerce/checkout', $checkout_page_post) per verificare. Solo i siti che hanno realmente il <!-- wp:woocommerce/checkout --> nel content della pagina checkout (= sito che usa il block checkout di WC) registrano i campi via additional fields API. I siti con classic shortcode (anche se avvolto da Fluid Checkout, WoodMart layout, qualunque altro tema custom) saltano la registrazione block → IntegratedCheckout è di nuovo single source of truth per il flow classic → niente più duplicati → Fluid Checkout vede solo il vecchio flow noto e funzionante.
  • Behavior preservato per il block checkout: i siti che hanno il block checkout effettivamente in pagina continuano a vedere i campi WooBiller renderizzati via l'API ufficiale, comportamento 1.6.0+ invariato. La compat block flow descritta nei changelog 1.6.0/1.6.2 (auto-recovery, validate_callback, BlockCheckoutMetaSync, IntegrationInterface per JS) resta totalmente attiva per i siti block.
  • Fail-soft sulla detection: a qualunque incertezza (has_block() non disponibile, pagina checkout non trovata, content vuoto), should_register() ritorna false — preferiamo non registrare i field block in caso ambiguo, così non rischiamo di rompere il classic flow. Falso negativo (sito block non rilevato) → fail-soft: campi non appaiono nel block ma classic continua a funzionare. Falso positivo → duplicati = bug 1.6.0-1.6.3 che vogliamo evitare.
  • Conferma email duplicata su Fluid Checkout (segnalata ma non ancora risolta): l'utente ha riportato anche un secondo problema — il campo "Conferma indirizzo email" appare due volte sul checkout con Fluid attivo. Questo NON è causato dalla regressione 1.6.0 (il campo è solo nel classic flow, mai aggiunto via additional fields API), ma da come Fluid Checkout posiziona il proprio email field. Il fix richiede un compat aggiuntivo dentro a FluidCheckoutCompat, da addressare in un round successivo. Rimane prioritario il fix dei campi mancanti — quello sì invece risolto qui.

v1.6.3

  • Pagina Licenza riprogettata come control center per gli aggiornamenti — feedback dell'admin: il pulsante "Verifica ora" sulla pagina Licenza in 1.6.x era fuorviante perché il nome suggeriva un'azione limitata alla verifica della licenza, mentre in realtà rifresca anche il manifest di integrità (cosa che ha risolto il problema TAMPERED in 1.6.1-1.6.2). Inoltre la pagina mancava di informazioni di base sull'aggiornamento (versione installata, disponibilità di nuove release, canale licenza) che obbligavano l'admin a navigare separatamente a WP-Admin → Plugin o → Aggiornamenti per avere il quadro completo.
  • Nuova sezione "Versione e aggiornamenti" sulla pagina Licenza (admin/views/pages/license.php): box che mostra (i) Versione installata del plugin (da WOOBILLER_VERSION); (ii) Canale di rilascio in chiaro — "Stabile" o "Beta (sperimentale)" — con helper text che spiega cosa significa per il sito; (iii) Aggiornamento disponibile con la nuova versione + bottone primario "Aggiorna a X.Y.Z" che linka direttamente a wp-admin/update.php?action=upgrade-plugin&plugin={basename}&_wpnonce=... (one-click, niente bisogno di andare alla pagina Aggiornamenti WP separata). Quando il sito è già sull'ultima versione disponibile per il canale, la cella mostra "Sei sulla versione più recente". Il valore di "disponibile" viene letto da $transient->response[<basename>]->new_version, popolato da PluginUpdater + PUC sulla base del canale licenza.
  • Pulsante rinominato a "Verifica e sincronizza ora" + tooltip + helper text dettagliato sotto: l'admin capisce ora che il click esegue tre operazioni in un colpo solo (heartbeat licenza + download manifest integrità + refresh check aggiornamenti plugin). Risolve la confusione semantica del nome precedente.
  • handle_recheck() forza ANCHE il refresh PUC (oltre al heartbeat licenza che già faceva): delete_site_transient('update_plugins') + wp_update_plugins() chiamato sincronamente prima del heartbeat, così la sezione "Aggiornamento disponibile" sulla pagina Licenza si aggiorna immediatamente dopo il click — senza dover andare separatamente a "Bacheca → Aggiornamenti → Controlla di nuovo". Side benefit: anche WP-Admin → Plugin mostra subito l'eventuale nuova notifica di update, e questo era il problema riportato del sito beta che aveva 1.6.1 installato ma non vedeva la 1.6.2 disponibile (PUC non aveva ancora ri-controllato GitHub dal suo ciclo 12h).
  • Messaggio post-verifica più descrittivo: "Verifica completata. Licenza attiva, manifest di integrità rifrescato e controllo aggiornamenti del plugin eseguito." invece del precedente "Verifica completata. Licenza attiva." — chiarisce che la sincronizzazione ha coperto tutti e tre i fronti.

v1.5.25

  • UX — la select "Modalità tipo cliente" su WooBiller → Campi Checkout passa da 3 a 5 voci, esponendo direttamente le due varianti che fino a 1.5.24 erano accessibili solo riaprendo il Setup Wizard: "Entrambi — Privato (default)" e "Entrambi — Azienda (default)". Le altre 3 voci restano invariate ("Entrambi — l'utente sceglie", "Considera tutti Privati", "Considera tutti Aziende"). Le due nuove varianti pre-selezionano rispettivamente Privato o Azienda sul checkout (l'utente può comunque cambiare il tipo), mentre "Entrambi — l'utente sceglie" forza la select del checkout a partire vuota costringendo l'utente a scegliere esplicitamente. Internamente la pagina campi continua a scrivere lo stesso tuple di option storato dal wizard (customer_types_mode + customertype_default + customertype_show_placeholder + customertype_option_order), quindi non c'è duplicazione di logica: il checkout consumer (IntegratedCheckout::get_customer_type_options() + public/js/woobiller-checkout.js) non ha bisogno di modifiche e tratta i 4 option key esattamente come prima. La translation tra il valore della select (5 voci) e il tuple canonico vive in due helper privati di FieldsPage (compute_effective_ct_mode() per la lettura, persist_ct_mode_choice() per il save), così i due path restano speculari e impossibili da divergere.

v1.5.23

  • Fix icone plugin in WP-Admin → priority collision con Plugin Update Checker. Da 1.5.21 i filter site_transient_update_plugins e plugins_api_result iniettavano gli URL delle icone (https://portal.woobiller.com/images/logo.{svg,png}) sul payload di aggiornamento, ma in update-core.php / Bacheca → Aggiornamenti / modale "Visualizza dettagli" l'icona continuava a mostrare il placeholder dashicons grigio. Causa: il nostro filter girava a priorità default 10 sullo stesso hook su cui PUC registra injectUpdate() (anch'essa priorità 10). A pari priorità l'ordine di esecuzione è quello di registrazione — noi registriamo PRIMA di PucFactory::buildUpdateChecker(), quindi il nostro filter girava per primo, poi injectUpdate() di PUC re-impiazzava INTERAMENTE l'oggetto $transient->response[<basename>] con il suo toWpFormat() (che non popola icons perché il GitHub release zip non contiene info.json con icone dichiarate). Risultato: le icone iniettate da noi venivano cancellate al write successivo di PUC. Fix: spostato il nostro filter a priorità 99 così gira DOPO injectUpdate() — abbiamo l'ultima parola sull'oggetto, le icone sopravvivono al merge e arrivano integre alla UI WP. Nessun cambio funzionale al di fuori della UI (le icone sono solo branding).

v1.5.22

  • Compatibilità WordPress 6.9 — header Tested up to: 6.9 aggiunto all'header del plugin e a readme.txt. Senza questo campo WP-Admin (in particolare la lista plugin e la pagina aggiornamenti) mostrava Compatibilità con WordPress <versione corrente>: sconosciuta perché il plugin dichiarava solo Requires at least: 6.0 senza un valore "tested" — i siti su 6.9.x si vedevano l'etichetta gialla. Test funzionale eseguito su WordPress 6.9.4 + WooCommerce 9.0; nessuna regressione introdotta dalla compat declaration (è puramente metadata letta dall'updater + dalla UI WP, non altera codice).

v1.5.21

  • Branding — icona del plugin visibile su WordPress: WP-Admin → Plugin, Bacheca → Aggiornamenti e modale "Visualizza dettagli" mostravano un placeholder grigio al posto del logo WooBiller perché Plugin Update Checker non popolava i campi icons[] nel payload site_transient_update_plugins né nella response di plugins_api. Aggiunti due filter dedicati in WooBiller\Updater\PluginUpdater: site_transient_update_plugins inietta icons['svg' / '2x' / '1x' / 'default'] sull'entry del plugin (sia in response quando c'è un update disponibile, sia in no_update quando il plugin è aggiornato), e plugins_api_result inietta gli stessi campi nella response della modale "Visualizza dettagli" (action plugin_information, slug woobiller). Gli URL puntano alle risorse pubbliche del portale: https://portal.woobiller.com/images/logo.svg (vector, preferita da WP quando disponibile) e https://portal.woobiller.com/images/logo.png (fallback raster 800×800 RGBA per i 3 slot raster). I filter girano PRIMA del license gate di boot() perché l'icona del plugin va mostrata indipendentemente dallo stato della licenza (anche un Free / no_license deve vedere il branding corretto in lista plugin).

v1.5.20

  • Log spam fix — PluginUpdater::boot e IntegratedCheckout::filter_checkout_fields scrivevano via Logger::trace() su WC > Status > Logs (source: woobiller) a ogni page load. Su un sito attivo questo produceva migliaia di righe identiche al giorno ("wiring PUC against repo…", "enableReleaseAssets() applied", "license channel=stable; prereleases NOT considered", "re-injected billing_company into billing fields"), al punto che aprire il file di log da Chrome lo bloccava per minuti. Logger::trace() per design scrive sempre a INFO bypassando il toggle debug_log — corretto per eventi a basso volume (es. trigger fired), sbagliato per eventi che fanno parte del normale boot/render. Queste righe sono state demote a Logger::debug(), quindi compaiono solo quando l'admin abilita esplicitamente "Debug log" dalle impostazioni. Eventi rari (errori PUC, manca PAT, manca composer install, prima volta che si re-inietta un campo) restano visibili come warning/error. Nessun cambio di comportamento — solo meno rumore.

v1.5.19

  • Licensing — rifiuto terminale del server licenze: quando il server risponde con uno slug "terminale" (invalid_token, license_not_found, license_revoked, license_expired, license_suspended, domain_locked, aud_mismatch), il plugin cancella subito stato locale (license_key, license_token, github_pat, manifest integrity) e disattiva il cron heartbeat. Al successivo caricamento della pagina admin viene mostrato di nuovo il form di attivazione. Caso d'uso tipico: l'admin cancella la licenza dal portale → il plugin se ne accorge al primo heartbeat e si auto-pulisce, invece di rimanere in stato zombie con un token che il server non riconosce più.
  • Licensing — messaggi d'errore umani: nuova classe WooBiller\Licensing\LicenseServerException con tabella di traduzione italiana per ogni slug. Sostituisce il vecchio "Server licenze ha risposto con errore (401): invalid_token" con "La licenza non è più valida." (e analoghi per gli altri slug: license_not_found → "La chiave di licenza non esiste.", license_revoked → "Questa licenza è stata revocata.", ecc.). Il getMessage() dell'eccezione è già localizzato — la pagina Licenza che cattura \Throwable e mostra il messaggio non richiede modifiche per beneficiarne.
  • UX: il pulsante "Disattiva su questo sito" diventa "Disattiva / Cambia licenza" per esplicitare il caso d'uso secondario (disattivare per inserire una chiave diversa, non solo per spegnere le Pro).
  • UX — banner admin persistente per stati che richiedono attenzione: nuova classe WooBiller\Admin\LicenseAdminBanner hookata su admin_notices. Mostra un banner rosso (non dismissibile) su ogni pagina admin quando la licenza è in uno stato bloccante: no_license ("Inserisci una chiave per attivare la fatturazione automatica."), grace_over ("La tua licenza è scaduta il {data}. Rinnovala dal portale per riattivare le funzionalità Pro."), revoked ("La tua licenza è stata revocata dal supporto..."), tampered ("I file di WooBiller installati non corrispondono alla versione ufficiale..."). Per gli stati più morbidi (invalid_aud "La licenza che hai inserito risulta attivata su un altro dominio..."; grace "Il server di WooBiller non risponde da qualche giorno...") usa il colore giallo warning. Ogni banner include un CTA contestuale ("Attiva licenza", "Rinnova licenza", "Reinserisci chiave", ecc.) che linka alla pagina Licenza. Sparisce automaticamente quando lo stato torna active. Skip su: pagina Licenza stessa (ridondante), utenti senza manage_woocommerce, richieste AJAX/REST.
  • UX — messaggi specifici dopo "Verifica ora" sulla pagina Licenza: il pulsante "Verifica ora" in 1.5.18 mostrava un generico "Verifica completata, ma la licenza non risulta più attiva su questo sito." anche quando il motivo esatto era recuperabile dallo stato del token. Ora nuovo helper LicenseAdminPage::diagnose_state() mappa ogni LicenseState::STATUS_* a una frase italiana specifica: scaduta cita la data esatta ("La tua licenza è scaduta il 12 maggio 2026..."), revocata indirizza al supporto, tampered spiega come reinstallare, invalid_aud spiega che la chiave è attivata su un altro dominio. Persone inesperte non devono più decifrare cosa sia un "no longer active" generico.
  • Canali di release: il plugin ora legge il claim channel dal token JWT del server licenze (introdotto nel portale 0.11.9) e si comporta di conseguenza. Due canali supportati: stable (default — comportamento storico) e beta. Sito con canale beta: (a) PluginUpdater chiama enablePreReleases() su PUC, quindi gli aggiornamenti GitHub marcati come prerelease vengono considerati candidati; (b) il server consegna il manifest_url della build beta corrispondente, quindi l'IntegrityChecker valida correttamente contro gli hash della build beta installata — niente più falsi positivi "tampered" sui beta-tester. L'attivazione è completamente server-driven: lo sviluppatore flippa la singola licenza su channel = beta da Filament; il sito cliente riceve il nuovo canale al prossimo heartbeat (max 12h, o immediato con "Verifica ora") senza richiedere accesso FTP o shell al wp-config.php. Token emessi prima di questo cambio (senza claim channel) vengono trattati come stable — fully backward-compatible.
  • UX — banner "canale beta": quando LicenseGate::state()->is_beta() è true il LicenseAdminBanner mostra un avviso giallo info ("WooBiller — canale beta: questo sito è registrato sul canale beta...") su ogni pagina admin (inclusa la pagina Licenza). Serve da reminder costante che il sito sta ricevendo build pre-release e va trattato come ambiente di test.

v1.5.14

  • UI: tutte le icone dei documenti emessi nella colonna "WooBiller" della lista ordini WooCommerce ora usano dashicons-pdf invece di icone differenziate per tipo (era dashicons-admin-network per la fattura elettronica, che assomigliava a una "chiave"). Stessa icona usata anche nella tabella riepilogo della dashboard principale del plugin (page=woobiller) per coerenza visiva. Aggiunta inoltre l'intestazione "Link documento" alla colonna finale della dashboard.

v1.5.13

  • Fix critico auto-update: la costante WOOBILLER_GITHUB_REPO_URL puntava a https://github.com/hardweb/woobiller-dist mentre il repo ufficiale di distribuzione è https://github.com/hardweb-it/woobiller-dist (con -it). Plugin Update Checker interrogava un URL 404 e nessun aggiornamento veniva mai mostrato in WP-Admin → Plugin. Corretta la costante. IMPORTANTE: le installazioni con versione ≤ 1.5.12 non possono ricevere questo fix automaticamente (sono bloccate sull'URL sbagliato) — l'aggiornamento a 1.5.13 va fatto manualmente UNA volta (Plugin → Aggiungi nuovo → Carica plugin → seleziona woobiller-1.5.13.zip → Sostituisci), dopodiché tutte le future versioni arriveranno automaticamente.

v1.5.12

  • UX: il Codice Fiscale viene ora normalizzato in MAIUSCOLO al salvataggio (meta ordine, user meta dei clienti registrati e admin-edit dell'ordine), così tutti i consumatori a valle (payload Fatture in Cloud / OpenAPI, metabox ordine, export documenti) vedono sempre la forma canonica accettata da Agenzia delle Entrate. In più, una regola CSS (text-transform: uppercase) sul campo #billing_codfisc mostra immediatamente in maiuscolo i caratteri digitati dall'utente per dare un feedback visivo coerente.

v1.5.11

Note di rilascio non disponibili per questa versione.