GestGazziNet: differenze tra le versioni

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


<p>
 
✅ 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
 
 
 
 
 
 
<pre>
# === MIGLIORAMENTI APPLICATI A GESTGN ===
# === MIGLIORAMENTI APPLICATI A GESTGN ===
# Ristrutturazione completa con GUI completa e migliorata.
# Ristrutturazione completa con GUI completa e migliorata.
Riga 375: Riga 438:
     app = GestGNApp(root)
     app = GestGNApp(root)
     root.mainloop()
     root.mainloop()
</p>
</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()