Gestione amministrativa, costi e prezzi per preventivi
Protocollo repo V2.2: margini e prezziari non dipendono dai commit Auto-sync CRM; la sequenza operativa di deploy (pull, controllo crm_anagrafica.json, push, SSH) è documentata in app/index.php → Sync Git e tracciamento accessi.
Fonte unica coefficienti, protocollo prezzario passivo e regole di calcolo per Agenti AI
storico_rese.php, costo_orario_operaio.php, spese_generali.php, prezzario.php. Non ottimizzare i crediti su questi file.
Tre fonti collegate: tecnica (rese), economica (coefficienti orari), output commerciale (listino prezzi).
I listini anno 2025 sono disponibili in due formati: PDF (documento ufficiale) e Excel (dati). Entrambi sono indicizzati dalla ricerca globale: cercando ad esempio «opere in ferro» o un codice articolo l’utente ottiene risultati che puntano al documento originale (PDF o Excel), non a file intermedi.
| Documenti ufficiali (PDF) | Database dati (Excel) |
|---|---|
Listini in formato documento per stampa e condivisione. Specchio HTML in cache_index/*_pdf.html per l’indicizzazione. |
Listini in formato foglio (Codice, Descrizione, Prezzo). Specchio HTML in cache_index/*_xlsx.html per l’indicizzazione. |
1. LOGICA CACHE: I file in amministrazione/prezziari/2025/cache_index/ sono specchi HTML usati solo per la ricerca full-text. Non modificarli a mano: vengono generati dagli script di conversione.
2. PROTOCOLLO ORIGIN: Ogni file di cache DEVE contenere in cima il tag <!-- ORIGIN_FILE: percorso/file.pdf --> (o .xlsx). Serve a mappare il risultato di ricerca al documento binario originale; l'indexer legge questo commento e espone il link al PDF/Excel.
3. MANUTENZIONE MEMORIA: Per rigenerare la cache di file pesanti (Vol. D, E, F) è OBBLIGATORIO usare PHP con -d memory_limit=2048M. Gli script ottimizzati sono: app/scripts/prezziari_excel_to_html.php (usa setReadDataOnly, filtro fogli, limite 5000 righe) e app/scripts/prezziari_pdf_to_html.php (prime 100 pagine). Senza questo limite i file grandi causano memory exhausted.
4. SCRIPT DI EMERGENZA (manutenzione indicizzazione):
app/scripts/prezziari_excel_to_html.phpapp/scripts/prezziari_pdf_to_html.phpapp/core/search_indexer.php (argomento full)Comando consigliato (PowerShell, dalla root): php -d memory_limit=2048M app/scripts/prezziari_excel_to_html.php; poi stesso per prezziari_pdf_to_html.php; infine php app/core/search_indexer.php full.
La Ricerca Globale include entrambi i formati: in caso di match in un file di cache, il link punta al file originale (PDF o Excel) indicato nel commento ORIGIN_FILE. Rigenerazione: php app/scripts/prezziari_excel_to_html.php, php app/scripts/prezziari_pdf_to_html.php; poi php app/core/search_indexer.php full.
Struttura cartella prezziari/2025: PDF in pdf/ (ufficiali), Excel in excel/ (dati). cache_index/: file HTML generati per l'indexer (*_xlsx.html da Excel, *_pdf.html da PDF). Ogni file cache deve avere in cima <!-- ORIGIN_FILE: amministrazione/prezziari/2025/excel|pdf/NOMEFILE.ext -->. Generazione cache: php app/scripts/prezziari_excel_to_html.php (da Excel); per PDF estrarre testo e salvare in cache_index/NOMEFILE_pdf.html. Dopo aggiornamento cache: App → Sincronizza Indice o php app/core/search_indexer.php full. Link risultati ricerca → documento originale (PDF/Excel), non file cache.
💬 Chat AI Prezziari: interrogazione listini 2025 via Gemini RAG. Apri Chat Prezziari 2025
ARCHITETTURA: search_to_ai.php estrae contesto da fast_prices.json (o fallback search_index.json) e lo invia a gemini_api.php. API: app/api/chat_prezziari.php (POST message, model).
OBBLIGO DI RAGIONAMENTO: L'AI dichiara sempre il processo: FASE 1 (prezzo finito in listino) → FASE 2 (rese da storico_rese_data.inc.php) → FASE 3 (rese da web se mancano, citando fonte).
FORMATO RISPOSTA (Single-Shot): [ANALISI DELLE FONTI] | [PREZZO SU BASE UFFICIALE] | [PREZZO SU BASE AZIENDALE] | [CONCLUSIONI] | ### FINE_ANALISI ###
COSTO AZIENDALE: Solo «Costo Orario Medio» da costo_orario_operaio.php (nessuna media tra qualifiche). Margini a cascata: Totale = ((Base × 1,15) × 1,10).
MARGINI OBBLIGATORI: Costo base = Resa × Costo orario; SG = +15% sul costo base; UI = +10% su (Costo base + SG); TOTALE = Costo base + SG + UI.
RESE: Da RESE_DI_PREVENTIVO_TETTO in storico_rese_data.inc.php (voci tetto legno); se assente, Gemini può stimare indicando la fonte.
LIMITI: MAX_CONTEXT_CHARS 15000, maxOutputTokens 4096, timeout 120s. API: JSON valido anche in caso di eccezione.
FALLBACK PROSEGUI: Solo se la risposta è troncata (limite token) e non contiene ### FINE_ANALISI ###, il sistema attende 15 secondi e invia «prosegui», concatenando la seconda parte. Barra blu durante l'attesa. Rate limit 429: attesa 65 secondi.
⚠️ STATO SISTEMA: Se il sistema non funziona, Google potrebbe aver bloccato la chiave API Gemini. Per ripristinare: accedere a Google AI Studio o Google Cloud Console, creare nuova API key, aggiornare .env (GEMINI_API_KEY) o app/config/gemini_credentials.php. Verificare debug_log.txt (root) per errori.
Pipeline indicizzazione: Excel/PDF → prezziari_excel_to_html.php / prezziari_pdf_to_html.php → cache_index/*.html → generate_fast_index.php → fast_prices.json. Schema JSON: c=codice, d=descrizione, p=prezzo, s=sorgente.
Generazione cache: app/scripts/prezziari_excel_to_html.php, prezziari_pdf_to_html.php. Naming: *_xlsx.html, *_pdf.html. Obbligatorio: <!-- ORIGIN_FILE: path --> in cima.
generate_fast_index.php: php app/scripts/generate_fast_index.php. Priorità cache_index, fallback excel. Excel-HTML: regex /<tr>\s*<td>([^<]*)<\/td>...<\/tr>/u. Parte 3 PDF: extractFromParte3Pdf con regex flessibile (1h, 1 ora, ore).
search_to_ai.php (SearchToAi): getContextForQuery(). searchFastIndex() su fast_prices.json: scoring codice peso 3, descrizione 1; boost +50 codici esatti. Parametri: MAX_PREZZARI_ROWS=30, MAX_RESE_ROWS=10, MAX_LABOR_ROWS=10, MAX_CONTEXT_CHARS=8000. LABOR_COST_KEYWORDS per iniezione manodopera. searchFastIndexFallback() per RU/RM/noli. Follow-up: isFollowUpQuery(). Fallback search_index.json se fast_prices vuoto.
API chat_prezziari.php: POST message, model → getContextForQuery → GeminiClient::queryWithContext. Dati iniettati: OFFICIAL_LABOR_RATES (getOfficialLaborRates), COSTI_ORARIO_AZIENDALI (costo_orario_export), RESE_DI_PREVENTIVO_TETTO (storico_rese_data). maxOutputTokens 4096, timeout 120s. Debug: debug_log.txt (RAW_RESPONSE, JSON_DECODE_ERROR, FALLBACK_PING_RESPONSE).
Rate limit 429: 5 RPM piano Free. API restituisce HTTP 429. Frontend: attesa 65s, max 2 retry, pulsante «Riprova ora».
Barre progresso: Blu=attesa rate limit (65s/15s); Verde=trigger testuali (ANALISI FONTI 25%, PREZZO UFFICIALE 50%, PREZZO AZIENDALE 75%, CONCLUSIONI 90%, FINE_ANALISI 100%).
Auto-selezione modello: gemini-2.5-pro 100 req/giorno, gemini-2.5-flash 250/giorno. Storage: app/data/ai_usage.sqlite. ai_usage_log(), ai_usage_get_today_count(), ai_usage_get_dual_stats(). API stats: app/api/ai_usage_stats.php.
File chiave: prezziari_excel_to_html.php, prezziari_pdf_to_html.php, generate_fast_index.php; fast_prices.json, search_index.json; search_to_ai.php, gemini_api.php; chat_prezziari.php (API), chat_prezziari.php (UI); ai_usage_db.php, ai_usage_stats.php.
Troubleshooting: Chiave bloccata → nuova API key. debug_log.txt: RAW_RESPONSE (http_code, curl_error), JSON_DECODE_ERROR, FALLBACK_PING_RESPONSE. «Canale API OK»=chiave OK; «Fallback Ciao fallito»=chiave sospesa.
amministrazione/ ├── index.php (questa pagina: protocolli e documentazione AI) ├── storico_rese.php ← FONTE TECNICA: log esperienze di cantiere, Resa Media (ore/u.m.) ├── storico_rese_data.inc.php (log esperienze + calcolo Resa Media; RESE_DI_PREVENTIVO_TETTO per Chat AI) ├── costo_orario_operaio.php ← FONTE ECONOMICA: Costo Orario Medio + Monte Ore Squadra (dati in $RUOLI_SQUADRA, base $ORE_MENSILI_STD) ├── spese_generali.php (pagina dedicata SG: costi annuali, incidenza oraria, percentuale SG reale) ├── costo_orario_export.inc.php (export coefficienti per Chat Prezziari API – sync con costo_orario_operaio) ├── prezzario.php ← OUTPUT COMMERCIALE: Prezzo = Resa × Coefficiente ├── gestione.php
Il file storico_rese.php (e i dati in storico_rese_data.inc.php) gestisce un log di esperienze di cantiere per ogni lavorazione. La Resa per prezziario è usata dal Prezzario; Squadra e Qualifiche sono riferimenti vincolanti per pianificazione e preventivi. Export PDF: pulsante «Scarica tabella PDF» per ogni tabella, export client-side tramite DataTables pdfHtml5 (stesso protocollo di prezzario: A3 landscape, font 8/9pt, colonna Dettaglio storico esclusa con no-export). Vedi app/index.php – Sistemi di Esportazione PDF.
((DEI × 0,60) + Resa_Storica) / 2DEI × 0,60 (DEI -40%)La tabella include la colonna DEI -40% (resa DEI × 0,60). Arrotondamento 2 decimali. Carpenteria/Demolizioni: stessa formula (media se Resa Storica presente, altrimenti solo DEI -40%). Funzione calcola_resa_di_preventivo_carpenteria().
Istruzioni obbligatorie per storico_rese / storico_rese_data.inc.php. Tutte le spiegazioni tecniche (log, medie, formati, regole squadre) risiedono qui; la pagina storico_rese.php contiene solo dati e legenda.
Struttura tabelle unificata (Tetti, Carpenteria, Demolizioni):
COLONNE_HEADER_TETTO. Arrotondamento DEI -40% e Resa per prezziario: 2 decimali.storico_rese_data.inc.php con resa_dei, squadra_n, qualifiche, esperienze => []. Resa per prezziario tramite calcola_resa_di_preventivo_carpenteria(): se esistono esperienze con Resa Storica > 0, media tra (DEI×0,60) e Resa Storica; altrimenti solo DEI × 0,60. Icone: ⚖️ media bilanciata, 💡 solo DEI -40%.Il file costo_orario_operaio.php calcola:
$ORE_MENSILI_STD.$RUOLI_SQUADRA con quantità per ruolo, calcolo dinamico.$MONTE_ORE_MENSILE_SQUADRA dinamico.Box in cima: COSTO ORARIO MEDIO OPERAIO e MONTE ORE MENSILE SQUADRA. Sincronizzare costo_orario_export.inc.php (base_medio) con il valore mostrato.
Il file spese_generali.php è dedicato solo all'analisi SG: input costi annuali, Totale SG, Incidenza oraria SG e Percentuale SG Reale evidenziata nel KPI principale.
Default variabili operative: la voce Carburante e Pedaggi (2 Auto) usa default dinamico (60 * 2 * 220) ma resta sempre editabile per forfait annuale.
Regola: non reintrodurre nella pagina SG simulazioni prezzo/utile o calcoli commerciali. Le logiche prezzo restano in prezzario.php.
Il file prezzario.php:
storico_rese_data.inc.php, il Costo Orario Base da costo_orario_operaio.php e la Percentuale SG reale da spese_generali.php..table-responsive con overflow-x: auto e min-width per preservare leggibilità colonne.crm/anagrafica.php: A3 landscape, font 8/9pt, colonne th:not(.no-export)). Vedi app/index.php – Sistemi di Esportazione PDF.usa_media_bilanciata_carpenteria($row) in storico_rese_data.inc.php. Continuità visiva con storico_rese.php.Logica commerciale dei codici univoci (ACC, STR, ABB, FIN, ISO, VEN, SER, SIC, EXT):
cad per riflettere il tempo di movimentazione e posa dei componenti (pretagliati CNC o lavorati in opera).ml per quantificare correttamente il tempo di taglio e sfrido sui raccordi delle pendenze.prezzario.php e le rese in storico_rese.php (e in storico_rese_data.inc.php) devono essere mantenuti sincronizzati usando questi codici univoci; ogni modifica alle rese va propagata al prezzario (obbligo di rigenerazione).COEFFICIENTE_ORARIO (€/h): costo della singola ora uomo. Usato per calcolo prezzi: Prezzo = Resa × Coefficiente (es. 0,40 ore/mq × 26,73 €/h = 10,69 €/mq).
DIVISORE_34 (4 operai × 8,5 h/giorno = 34 ore uomo/giornata): usato solo per pianificazione temporale – quante giornate servono: Ore uomo totali ÷ 34. Non va mai usato per calcoli economici (prezzi, costi).
costo_orario_operaio.php è la fonte unica per Costo Orario Medio e Monte Ore Squadra. Dati in $RUOLI_SQUADRA, $ORE_MENSILI_STD (160, margine sicurezza), $CONTRIBUTI_MESI (1.100). Calcolo dinamico.
Sync: Aggiornare costo_orario_export.inc.php (base_medio) con il valore Costo Orario Medio mostrato in pagina.
Il Prezzario è un file passivo: usa rese e parametri economici già calcolati dalle fonti amministrative e genera il prezzo finale per utile 10%/20%/30%. Non ricalcola contributi, IVA o costi extra fuori dalle fonti ufficiali.
Procedura: (1) leggi Resa per prezziario (storico), Costo Orario Base (costo_orario_operaio), SG reale (spese_generali). (2) calcola Costo Reale e poi Prezzo = Costo Reale / (1 - %Utile). (3) applica applicaArrotondamentoCommerciale() prima della stampa in tabella.
OBBLIGO DI RIGENERAZIONE: Ogni modifica a storico_rese_data.inc.php, costo_orario_operaio.php o spese_generali.php richiede verifica/ricalcolo del Prezzario.
Per i lavori non presenti nel prezzario (lavori in economia), il prezzo orario da fatturare al cliente deve essere SEMPRE basato sul coefficiente con Margine 30% (base_medio / 0,70) da costo_orario_export.inc.php. Questo serve a coprire l'imprevisto e l'assenza di una resa certificata nello storico.
costo_orario_operaio.php, prezzario.php e, se toccate, storico_rese.php.Per ogni ricalcolo costi o prezzi gli Agenti AI devono leggere con MASSIMA potenza (lettura COMPLETA) i file chiave:
storico_rese.php (e dati in storico_rese_data.inc.php): rese ore/u.m. per ogni lavorazione.costo_orario_operaio.php: Costo Orario Medio e Monte Ore Squadra (dati in $RUOLI_SQUADRA, base $ORE_MENSILI_STD).spese_generali.php: analisi SG reale (totale, incidenza oraria, percentuale SG).prezzario.php: tabelle commerciali dinamiche (prezzi 10%/20%/30%) con arrotondamento a scaglioni.Non ottimizzare i crediti su questi file; rispettare la gerarchia (fonte tecnica → fonte economica → output commerciale) e la regola ferrea sulle rese.
storico_rese.php / storico_rese_data.inc.php, costo_orario_operaio.php, prezzario.php (MASSIMA potenza).costo_orario_operaio.php e SG reale da spese_generali.php; poi applica Prezzo = (Resa × Costo × (1+SG)) / (1-Margine).storico_rese_data.inc.php. Verifica che le tabelle mostrino sempre le tre colonne Prezzo (10% u.i.), Prezzo (20% u.i.), Prezzo (30% u.i.) con arrotondamento commerciale.⚠️ Non introdurre contributi o IVA in prezzario. SG è consentita solo come valore letto dalla pagina dedicata spese_generali.php. Non modificare le rese in storico_rese se non da dati certi dai diari.
costo_orario_operaio.php (array $RUOLI_SQUADRA, $ORE_MENSILI_STD, $CONTRIBUTI_MESI). I box si aggiornano dinamicamente.costo_orario_export.inc.php (base_medio) con il Costo Orario Medio mostrato; il prezzario usa quel valore per i coefficienti.⚠️ Risultato atteso: Costo orario, storico rese e prezzario coerenti; nessun coefficiente obsoleto; rese modificate solo se da diari.
storico_rese_data.inc.php e prendi una lavorazione a campione (es. Isolante Sopra Perlina: resa 0,278 ore/mq).Costo_Orario_Medio / 0,70. Calcola: Listino = Resa × Coeff_30%; Sconto 12% = Listino × 0,88; Sconto 22% = Listino × 0,78.Uso: Eseguire periodicamente o dopo modifiche a rese/coefficienti per garantire coerenza matematica tra storico rese, costo orario e listino prezzi.
Costo_Orario_Medio / 0,70 (da costo_orario_export.inc.php o prezzario).Uso: Quando il cliente richiede una lavorazione non in listino: stimare le ore uomo necessarie e fatturare con il prezzo orario al 30% per coprire imprevisti e assenza di resa certificata.
Costo Orario Operaio – Squadra tipo 2 team: configurazione per ruoli/quantità ($RUOLI_SQUADRA) con 8 operai totali. Ore standard su base 160h/mese ($ORE_MENSILI_STD) per margine sicurezza, contributi fissi 1.100 €/mese, calcolo dinamico Costo Orario Medio e Monte Ore Mensile Squadra.
Spese Generali – Pagina dedicata SG: KPI primario «Percentuale Spese Generali Reale» in cima; output focalizzato su Totale SG, Incidenza Oraria SG e Percentuale SG. Rimossi simulatore prezzo e dati commerciali non pertinenti.
Prezzario dinamico multi-margine: lettura fonti da costo orario + SG + rese; tre colonne utili (10% / 20% / 30%) con arrotondamento commerciale a scaglioni e colonna Descrizione dedicata.
Storico Rese: colonna “Note” rinominata in “Descrizione”; DEI -40% (×0,60), arrotondamento 2 decimali, struttura unificata tabelle.
Responsive Amministrazione: tabelle principali avvolte in .table-responsive (scroll orizzontale), con header sticky e larghezze minime per evitare wrap verticale estremo della Descrizione su smartphone.
COEFFICIENTE_ORARIO vs DIVISORE_34: COEFFICIENTE = €/h per prezzi; DIVISORE_34 = ore/giorno per pianificazione. Non mischiare.