GestGazziNet
Versione 31/5/2025
- === 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()