GestGazziNet: differenze tra le versioni

Da GazziNet.
Vai alla navigazione Vai alla ricerca
Nessun oggetto della modifica
Nessun oggetto della modifica
 
(2 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
Versione 31/5/2025  - 3.0  
Versione 31/5/2025  - 3.0  


<pre>
 
✅ Requisiti comuni (tutti i sistemi)
✅ Requisiti comuni (tutti i sistemi)
Python 3 installato
Python 3 installato
Riga 10: Riga 10:


Solo Linux: CUPS installato e il comando lp funzionante per la stampa
Solo Linux: CUPS installato e il comando lp funzionante per la stampa


🔧 INSTALLAZIONE SU UBUNTU / DEBIAN
🔧 INSTALLAZIONE SU UBUNTU / DEBIAN
Riga 25: Riga 27:


🔧 INSTALLAZIONE SU RED HAT / CENTOS / ORACLE LINUX
🔧 INSTALLAZIONE SU RED HAT / CENTOS / ORACLE LINUX
1. Installa Python e tkinter
1. Installa Python e tkinter
bash
sudo dnf install python3 python3-tkinter
Copia
Su Oracle Linux potresti dover abilitare EPEL per tkinter:
Modifica
 
sudo dnf install python3 python3-tkinter
sudo dnf install epel-release
Su Oracle Linux potresti dover abilitare EPEL per tkinter:
sudo dnf install python3-tkinter


bash
Copia
Modifica
sudo dnf install epel-release
sudo dnf install python3-tkinter
2. Avvia l'applicazione
2. Avvia l'applicazione
bash
cd ~/GestGN
Copia
python3 gestgn.py
Modifica
 
cd ~/GestGN
python3 gestgn.py
🪟 INSTALLAZIONE SU WINDOWS
🪟 INSTALLAZIONE SU WINDOWS
1. Installa Python
1. Installa Python
Scarica ed esegui l’installer da https://www.python.org/downloads/
Scarica ed esegui l’installer da https://www.python.org/downloads/
⚠️ Assicurati di selezionare “Add Python to PATH” durante l’installazione.
 
2. Verifica Python e tkinter
Apri il prompt dei comandi (CMD) e digita:


⚠️ Assicurati di selezionare “Add Python to PATH” durante l’installazione.
python --version


2. Verifica Python e tkinter
Per testare tkinter:
Apri il prompt dei comandi (CMD) e digita:


cmd
python -m tkinter
Copia
Modifica
python --version
Per testare tkinter:


cmd
Copia
Modifica
python -m tkinter
3. Salva il file gestgn.py e avvialo
3. Salva il file gestgn.py e avvialo
Doppio clic sul file oppure da CMD:
Doppio clic sul file oppure da CMD:


cmd
cd C:\percorso\della\cartella
Copia
python gestgn.py
Modifica
cd C:\percorso\della\cartella
python gestgn.py
🖨️ Supporto stampa con Brother QL-700 (Linux)
Installa i driver:
 
bash
Copia
Modifica
sudo apt install printer-driver-brlaser
Verifica che la stampante sia installata con nome Brother_QL_700 (modifica nel codice se il nome è diverso).
 
Per test stampa:
 
bash
Copia
Modifica
echo "Test stampa" > /tmp/test.txt
lp -d Brother_QL_700 /tmp/test.txt
📁 Struttura consigliata della cartella GestGN
css
Copia
Modifica
GestGN/
├── gestgn.py
├── gestgn.db          ← creato automaticamente
└── allegati_pdf/      ← usato per i file PDF
 
 








</pre>





Versione attuale delle 23:54, 30 mag 2025

Versione 31/5/2025 - 3.0


✅ Requisiti comuni (tutti i sistemi) Python 3 installato

Librerie standard: tkinter, sqlite3, shutil, ecc. (inclusi con Python)

Stampante Brother QL-700 configurata (opzionale per stampa etichette)

Solo Linux: CUPS installato e il comando lp funzionante per la stampa


🔧 INSTALLAZIONE SU UBUNTU / DEBIAN

1. Installa Python e tkinter

sudo apt update
sudo apt install python3 python3-tk

2. Clona o copia i file dell'app

Salva il file Python (es. gestgn.py) in una cartella dedicata, ad esempio ~/GestGN.

3. Avvia l'applicazione

cd ~/GestGN
python3 gestgn.py

🔧 INSTALLAZIONE SU RED HAT / CENTOS / ORACLE LINUX

1. Installa Python e tkinter

sudo dnf install python3 python3-tkinter
Su Oracle Linux potresti dover abilitare EPEL per tkinter:
sudo dnf install epel-release
sudo dnf install python3-tkinter

2. Avvia l'applicazione

cd ~/GestGN
python3 gestgn.py

🪟 INSTALLAZIONE SU WINDOWS 1. Installa Python

Scarica ed esegui l’installer da https://www.python.org/downloads/
⚠️ Assicurati di selezionare “Add Python to PATH” durante l’installazione.

2. Verifica Python e tkinter

Apri il prompt dei comandi (CMD) e digita:
python --version
Per testare tkinter:
python -m tkinter

3. Salva il file gestgn.py e avvialo Doppio clic sul file oppure da CMD:

cd C:\percorso\della\cartella
python gestgn.py




# === MIGLIORAMENTI APPLICATI A GESTGN ===
# Ristrutturazione completa con GUI completa e migliorata.

import os
import sqlite3
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from datetime import datetime
import shutil
import platform
import subprocess

DB_FILE = 'gestgn.db'
PDF_DIR = 'allegati_pdf'
FORNITORI = ['Amazon', 'Unieuro', 'Comet', 'MediaWorld']

os.makedirs(PDF_DIR, exist_ok=True)

# Utility

def centra_finestra(finestra, padre):
    finestra.update_idletasks()
    w, h = finestra.winfo_width(), finestra.winfo_height()
    x = padre.winfo_x() + (padre.winfo_width() // 2) - (w // 2)
    y = padre.winfo_y() + (padre.winfo_height() // 2) - (h // 2)
    finestra.geometry(f"{w}x{h}+{x}+{y}")

def apri_pdf(nome_file):
    path = os.path.join(PDF_DIR, nome_file)
    if not os.path.exists(path):
        messagebox.showerror("Errore", "File PDF non trovato")
        return
    try:
        if platform.system() == 'Windows':
            os.startfile(path)
        elif platform.system() == 'Darwin':
            subprocess.run(['open', path])
        else:
            subprocess.run(['xdg-open', path])
    except Exception as e:
        messagebox.showerror("Errore apertura PDF", str(e))

# Database

def inizializza_db():
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS inventory (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        id_code TEXT UNIQUE,
        riferimento TEXT,
        fornitore TEXT,
        descrizione TEXT,
        pdf_file TEXT,
        created_at TEXT,
        active INTEGER DEFAULT 1
    )''')
    conn.commit()
    conn.close()

def genera_codice():
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute("SELECT MAX(id) FROM inventory")
    max_id = c.fetchone()[0]
    conn.close()
    return f"INV-{(max_id or 0) + 1:04d}"

def aggiungi_articolo(rif, fornitore, descrizione, pdf_path):
    codice = genera_codice()
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    nome_pdf = ""
    if pdf_path:
        estensione = os.path.splitext(pdf_path)[1]
        nome_pdf = f"{codice}_{datetime.now().strftime('%Y%m%d_%H%M%S')}{estensione}"
        try:
            shutil.copy(pdf_path, os.path.join(PDF_DIR, nome_pdf))
        except Exception as e:
            messagebox.showerror("Errore copia PDF", str(e))
            return
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute('''INSERT INTO inventory (id_code, riferimento, fornitore, descrizione, pdf_file, created_at)
                 VALUES (?, ?, ?, ?, ?, ?)''',
              (codice, rif.strip(), fornitore.strip(), descrizione.strip(), nome_pdf, now))
    conn.commit()
    conn.close()

def carica_articoli(attivi=True):
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute("SELECT * FROM inventory WHERE active=? ORDER BY created_at DESC", (1 if attivi else 0,))
    risultati = c.fetchall()
    conn.close()
    return risultati

def modifica_articolo(id, riferimento, fornitore, descrizione):
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute("UPDATE inventory SET riferimento=?, fornitore=?, descrizione=? WHERE id=?",
              (riferimento.strip(), fornitore.strip(), descrizione.strip(), id))
    conn.commit()
    conn.close()

def elimina_logicamente(id):
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute("UPDATE inventory SET active=0 WHERE id=?", (id,))
    conn.commit()
    conn.close()

def recupera_articolo(id):
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute("UPDATE inventory SET active=1 WHERE id=?", (id,))
    conn.commit()
    conn.close()

# GUI (completa con tutte le funzioni)
class GestGNApp:
    def __init__(self, root):
        self.root = root
        root.title("GestGN - Gestione Inventario IA Docet")

        self.refresh_attivo = tk.BooleanVar(value=False)
        self.tempo_refresh = tk.IntVar(value=30)

        frame = ttk.Frame(root, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)

        style = ttk.Style()
        style.theme_use('clam')

        ttk.Button(frame, text="Aggiungi", command=self.finestra_aggiungi).grid(row=0, column=0, padx=5, pady=5)
        ttk.Button(frame, text="Modifica", command=self.finestra_modifica).grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(frame, text="Elimina", command=self.elimina_articolo).grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(frame, text="Apri PDF", command=self.visualizza_pdf).grid(row=0, column=3, padx=5, pady=5)
        ttk.Button(frame, text="Visualizza Disattivati", command=self.visualizza_disattivati).grid(row=0, column=4, padx=5, pady=5)
        ttk.Button(frame, text="Visualizza", command=self.visualizza_dettagli).grid(row=0, column=5, padx=5, pady=5)
        ttk.Button(frame, text="Esci", command=root.destroy).grid(row=0, column=6, padx=5, pady=5)

        ttk.Label(frame, text="Refresh (s):").grid(row=2, column=0, sticky="w", padx=5)
        spin_refresh = ttk.Spinbox(frame, from_=5, to=3600, textvariable=self.tempo_refresh, width=6)
        spin_refresh.grid(row=2, column=1, sticky="w", padx=5)
        chk_refresh = ttk.Checkbutton(frame, text="Auto-refresh", variable=self.refresh_attivo)
        chk_refresh.grid(row=2, column=2, columnspan=2, sticky="w", padx=5)

        self.tabella = ttk.Treeview(frame, columns=("id", "id_code", "riferimento", "fornitore", "descrizione", "pdf_file", "created_at"), show="headings")
        self.intestazioni = {
            "id": "ID", "id_code": "Codice", "riferimento": "Riferimento",
            "fornitore": "Fornitore", "descrizione": "Descrizione",
            "pdf_file": "PDF", "created_at": "Data Creazione"
        }
        for col in self.tabella["columns"]:
            self.tabella.heading(col, text=self.intestazioni[col])

        self.tabella.grid(row=1, column=0, columnspan=7, sticky="nsew")
        frame.rowconfigure(1, weight=1)
        frame.columnconfigure(6, weight=1)

        self.aggiorna_tabella()
        self.schedule_refresh()

    def aggiorna_tabella(self):
        for r in self.tabella.get_children():
            self.tabella.delete(r)
        for riga in carica_articoli():
            self.tabella.insert('', tk.END, values=riga)

    def schedule_refresh(self):
        if self.refresh_attivo.get():
            self.aggiorna_tabella()
        self.root.after(self.tempo_refresh.get() * 1000, self.schedule_refresh)

    def finestra_aggiungi(self):
        win = tk.Toplevel(self.root)
        win.title("Aggiungi Articolo")
        win.grab_set()
        win.transient(self.root)

        ttk.Label(win, text="Riferimento").grid(row=0, column=0)
        rif = ttk.Entry(win)
        rif.grid(row=0, column=1)

        ttk.Label(win, text="Fornitore").grid(row=1, column=0)
        fornitore = ttk.Combobox(win, values=FORNITORI, state="readonly")
        fornitore.grid(row=1, column=1)

        ttk.Label(win, text="Descrizione").grid(row=2, column=0)
        descrizione = ttk.Entry(win)
        descrizione.grid(row=2, column=1)

        pdf_path = tk.StringVar()
        ttk.Label(win, text="PDF Allegato").grid(row=3, column=0)
        ttk.Entry(win, textvariable=pdf_path).grid(row=3, column=1)
        ttk.Button(win, text="Sfoglia", command=lambda: pdf_path.set(filedialog.askopenfilename(filetypes=[("PDF", "*.pdf")], parent=win))).grid(row=3, column=2)

        def salva():
            if not rif.get() or not fornitore.get() or not descrizione.get():
                messagebox.showwarning("Attenzione", "Compila tutti i campi obbligatori", parent=win)
                return
            if not pdf_path.get():
                if not messagebox.askyesno("Nessun PDF", "Vuoi salvare il record anche senza allegato?", parent=win):
                    return
            aggiungi_articolo(rif.get(), fornitore.get(), descrizione.get(), pdf_path.get())
            messagebox.showinfo("Salvato", "Articolo aggiunto con successo", parent=win)
            win.destroy()
            self.aggiorna_tabella()

        ttk.Button(win, text="Salva", command=salva).grid(row=4, column=0, columnspan=3, pady=10)
        centra_finestra(win, self.root)

    def finestra_modifica(self):
        item = self.tabella.selection()
        if not item:
            messagebox.showinfo("Info", "Seleziona un record da modificare", parent=self.root)
            return
        valori = self.tabella.item(item[0])["values"]
        id, _, riferimento, fornitore, descrizione = valori[:5]

        win = tk.Toplevel(self.root)
        win.title("Modifica Articolo")
        win.grab_set()
        win.transient(self.root)

        ttk.Label(win, text="Riferimento").grid(row=0, column=0)
        rif = ttk.Entry(win)
        rif.insert(0, riferimento)
        rif.grid(row=0, column=1)

        ttk.Label(win, text="Fornitore").grid(row=1, column=0)
        forn = ttk.Combobox(win, values=FORNITORI, state="readonly")
        forn.set(fornitore)
        forn.grid(row=1, column=1)

        ttk.Label(win, text="Descrizione").grid(row=2, column=0)
        desc = ttk.Entry(win)
        desc.insert(0, descrizione)
        desc.grid(row=2, column=1)

        def salva():
            modifica_articolo(id, rif.get(), forn.get(), desc.get())
            messagebox.showinfo("Modificato", "Articolo modificato correttamente", parent=win)
            win.destroy()
            self.aggiorna_tabella()

        ttk.Button(win, text="Salva", command=salva).grid(row=3, column=0, columnspan=2, pady=10)
        centra_finestra(win, self.root)

    def elimina_articolo(self):
        item = self.tabella.selection()
        if not item:
            messagebox.showinfo("Info", "Seleziona un record da eliminare", parent=self.root)
            return
        valori = self.tabella.item(item[0])["values"]
        id = valori[0]
        if messagebox.askyesno("Conferma", "Vuoi davvero disattivare questo articolo?", parent=self.root):
            elimina_logicamente(id)
            messagebox.showinfo("Eliminato", "Articolo disattivato correttamente", parent=self.root)
            self.aggiorna_tabella()

    def visualizza_pdf(self):
        item = self.tabella.selection()
        if not item:
            messagebox.showinfo("Info", "Seleziona un record con PDF da aprire", parent=self.root)
            return
        valori = self.tabella.item(item[0])["values"]
        nome_file = valori[5]
        if nome_file:
            apri_pdf(nome_file)
        else:
            messagebox.showwarning("PDF mancante", "Questo articolo non ha un PDF associato", parent=self.root)

    def visualizza_disattivati(self):
        finestra = tk.Toplevel(self.root)
        finestra.title("Articoli Disattivati")
        finestra.transient(self.root)
        finestra.grab_set()

        tabella_disattivi = ttk.Treeview(finestra, columns=("id", "id_code", "riferimento", "fornitore", "descrizione", "pdf_file", "created_at"), show="headings")
        for col in tabella_disattivi["columns"]:
            tabella_disattivi.heading(col, text=self.intestazioni[col])
        tabella_disattivi.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        for riga in carica_articoli(attivi=False):
            tabella_disattivi.insert('', tk.END, values=riga)

        def elimina_definitivamente():
            item = tabella_disattivi.selection()
            if not item:
                messagebox.showinfo("Info", "Seleziona un record da eliminare definitivamente", parent=finestra)
                return
            id = tabella_disattivi.item(item[0])["values"][0]
            if messagebox.askyesno("Conferma", "Sei sicuro di voler eliminare definitivamente questo articolo?", parent=finestra):
                conn = sqlite3.connect(DB_FILE)
                c = conn.cursor()
                c.execute("DELETE FROM inventory WHERE id=?", (id,))
                conn.commit()
                conn.close()
                messagebox.showinfo("Eliminato", "Articolo eliminato definitivamente", parent=finestra)
                tabella_disattivi.delete(item[0])

        frame_bottoni = ttk.Frame(finestra)
        frame_bottoni.pack(pady=5)

        ttk.Button(frame_bottoni, text="Recupera", command=lambda: self.recupera_selezionato(tabella_disattivi, finestra)).pack(side=tk.LEFT, padx=5)
        ttk.Button(frame_bottoni, text="Elimina Definitivamente", command=elimina_definitivamente).pack(side=tk.LEFT, padx=5)
        ttk.Button(frame_bottoni, text="Chiudi", command=finestra.destroy).pack(side=tk.LEFT, padx=5)
        centra_finestra(finestra, self.root)

    def recupera_selezionato(self, tabella_disattivi, finestra):
        item = tabella_disattivi.selection()
        if not item:
            messagebox.showinfo("Info", "Seleziona un record da recuperare", parent=finestra)
            return
        id = tabella_disattivi.item(item[0])["values"][0]
        if messagebox.askyesno("Conferma", "Vuoi davvero recuperare questo articolo?", parent=finestra):
            recupera_articolo(id)
            messagebox.showinfo("Recuperato", "Articolo riattivato correttamente", parent=finestra)
            tabella_disattivi.delete(item[0])

    def stampa_etichetta(self, codice, riferimento, fornitore):
        try:
            etichetta = f"{codice} {fornitore}"
            file_path = "/tmp/etichetta.txt"
            with open(file_path, "w") as f:
                f.write(etichetta)

            # Stampa tramite lp per sistemi Linux
            subprocess.run(["lp", "-d", "Brother_QL_700", file_path], check=True)
        except Exception as e:
            messagebox.showerror("Errore Stampa", f"Errore durante la stampa dell'etichetta: {e}", parent=self.root)

    def visualizza_dettagli(self):
        item = self.tabella.selection()
        if not item:
            messagebox.showinfo("Info", "Seleziona un record da visualizzare", parent=self.root)
            return
        valori = self.tabella.item(item[0])["values"]
        if len(valori) < 7:
            messagebox.showerror("Errore", "Il record selezionato non contiene tutti i dati richiesti", parent=self.root)
            return
        id, codice, riferimento, fornitore, descrizione, pdf_file, created_at = valori[:7]

        win = tk.Toplevel(self.root)
        win.title(f"Dettagli Articolo {codice}")
        win.transient(self.root)
        win.grab_set()

        campi = [
            ("ID", id),
            ("Codice", codice),
            ("Riferimento", riferimento),
            ("Fornitore", fornitore),
            ("Descrizione", descrizione),
            ("PDF", pdf_file if pdf_file else "Nessun PDF"),
            ("Creato il", created_at)
        ]

        for i, (label, valore) in enumerate(campi):
            ttk.Label(win, text=label + ":").grid(row=i, column=0, sticky="w", padx=5, pady=2)
            ttk.Label(win, text=valore).grid(row=i, column=1, sticky="w", padx=5, pady=2)

         ttk.Button(win, text="Stampa Etichetta", command=lambda: self.stampa_etichetta(codice, riferimento, fornitore)).grid(row=len(campi), column=0, columnspan=2, pady=5)
        ttk.Button(win, text="Chiudi", command=win.destroy).grid(row=len(campi)+1, column=0, columnspan=2, pady=10)
        centra_finestra(win, self.root)

# Avvio
if __name__ == '__main__':
    inizializza_db()
    root = tk.Tk()
    app = GestGNApp(root)
    root.mainloop()