progetto e sviluppo di un’applicazione per la gestione di un reagentario per reagenti chimici
TRANSCRIPT
Università degli Studi di Trieste
Facoltà di Ingegneria
Corso di laurea specialistica in Ingegneria Informatica
Progetto e Sviluppo di un’applicazione per la gestione di
un reagentario per reagenti chimici
Relatore : Laureando :
Chiar.mo Prof. Maurizio Fermeglia Marko Paliska
Anno accademico : 2013-2014
2
La teoria è quando si sa tutto e niente funziona. La pratica è quando tutto
funziona e nessuno sa il perchè. In questo caso abbiamo messo insieme la
teoria e la pratica: non c'è niente che funziona... e nessuno sa il perchè!
A.Einstien
3
SOMMARIO
Sommario………………………………………………………………………………………………………………………………pag. 3
Introduzione…………………………………………………………………………………………………………………………………..4
Analisi…………………………………………………………………………………………………………………………………………….6
Progettazione (concettuale del DB)………………………………………………………………………………………………..9
Progettazione (logica del DB)……………………………………………………………………………………………………….22
Progettazione (interfaccia)……………………………………………………………………………………………………………33
Realizzazione (progettazione fisica del DB)…………………………………………………………………………………..54
Realizzazione (implementazione dell’interfaccia)…………………………………………………………………………59
Conclusioni…………………………………………………………………………………………………………………………………108
Dedica……………………………………………………………………………………………………………………………………..…109
4
INTRODUZIONE
La presente tesi affronta la problematica della gestione di un reagentario. Il reagentario è il
magazzino delle sostanze chimiche che vengono utilizzate in un ente di ricerca che svolga attività
nell’ambito delle scienze chimiche, fisiche, mediche, ingegneristiche e biologiche, quindi
potenzialmente utilizzabile sia in dipartimenti universitari che in enti di ricerca.
L’analisi riportata nella tesi riguarda il reagentario del dipartimento di ingegneria ed architettura
dell’Università di Trieste, ma, poiché le norme di legge e le procedure di utilizzo dei reagenti
chimici sono relativamente standard, potrebbe essere applicato anche in altri contesti.
Le sostanza chimiche in possesso del dipartimento vengono conservate principalmente in una
stanza centrale chiamata “magazzino reagentario”. Alcune sostanze che necessitano di particolari
condizioni di stoccaggio, ad esempio temperatura costante, vengono stoccate in più frigoriferi
localizzati all'interno dei laboratori.
Il lavoro può essenzialmente dividersi in due parti. Nella prima ci siamo occupati della
progettazione dell’applicazione. Quindi il primo passo è stata la progettazione a livello puramente
teorico tenendo conto delle esigenze del utente finale, e analizzando le migliorie che si potevano
introdurre rispetto al programma già esistente. Dopo aver analizzato migliorie ed esigenze del
utente si è passati alla seconda parte, ossia lo sviluppo dell’applicazione. Qui si è realizzato tutto
ciò che era stato pianificato precedentemente: database e interfaccia.
Il risultato finale è stato un programma eseguibile (*.EXE), collegato con un DB, che gestisce tutte
le informazioni delle sostanze chimiche del reagentario. Per gestione delle sostanze chimiche si
intende la possibilità di visualizzare, inserire, modificare ed eliminare tutte le informazioni in
possesso.
La motivazione principale è stata la gestione di reagenti chimici tra più laboratori. Poter gestire in
modo intuitivo il consumo di un reagente, lo spostamento, poter ricercare in quale laboratorio si
trova un reagente, poter gestire tutte le caratteristiche che un reagente possiede e infine poter
avere un resoconto con appositi filtri di ricerca.
Il problema era già stato affrontato in via preliminare con la realizzazione di un prototipo
funzionante (“Progetto e sviluppo di un’applicazione per la gestione di un Reagentario” di Samo
Ziberna) che ha permesso ad alcuni utenti chiave di analizzare la bontà della procedura ed
evidenziare le criticità. Questa analisi ha portato alla necessità di riconsiderare la struttura della
base di dati e dell’interfaccia grafica per poter tenere conto sia delle esigenze degli utenti che delle
mutate condizioni della gestione.
Durante lo svolgimento del lavoro sono stati affrontati diversi punti essenziali. Innanzitutto il
primo punto è stato lo Studio di Tecnologie da usare. IL DIA aveva un SQL Server 2008 R2, ma è
stato aggiornato alla versione 2012. Il programma precedente era stato sviluppato in Visual Studio
2008. Quindi dati questi vincoli di progetto si è passati allo sviluppo del software mediante SQL
5
Server 2012 per il DB e Visual Studio 2013 per la GUI. Questi strumenti di sviluppo sono stati
studiati negli anni accademici in diversi corsi quindi lo studio di tecnologie usate era già noto. Il
punto successivo è stata la raccolta di requisiti. Prima si sono analizzate le migliorie da fare al
programma già esistente, e poi sono state affrontate le esigenze nuove degli utenti finali. In
particolare rispetto al programma precedente è stata introdotto il concetto di “Consumo” e
“Spostamento”(eliminati i concetti di Reso e Prelievo), introdotto il consumo di tipo decimale
(prelievo di una quantità non intera), migliorata la Ricerca che ora risulta più efficiente, introdotta
la gestione degli utenti da interfaccia e la Reportistica finale. La fase successiva è stata la
progettazione del DB e della GUI in base ai requisiti raccolti. Poi si è passati ad una fase di testing
che ha consentito all’utente finale di rendersi conto delle specifiche di progetto richieste e di
eventuali ulteriori correzioni da implementare nel progetto.
Il capitolo successivo riguarda l’Analisi dove vengono evidenziati i requisiti dell’utente finale e le
migliorie da fare al programma già esistente. Il passo successivo è la progettazione concettuale,
logica del DB e la progettazione dell’interfaccia in base ai requisiti espressi in fase di Analisi.
L’ultimo passo è stata la progettazione fisica del DB (trasformazione dei schemi logici in tabelle e
programmazione degli indici) e la realizzazione dell’interfaccia (dove sono stati usati oggetti di tipo
SQL comand e datagridview per la visualizzazione dei dati).
6
ANALISI
In questo capitolo viene svolto il lavoro di Analisi partendo dalla situazione preesistente
analizzando la documentazione disponibile.
Le informazioni principali raccolte si trovavano già nel DB del progetto sviluppato anni fa (Progetto
e sviluppo di un’applicazione per la gestione di un Reagentario).
In particolare erano state messe in evidenza le Frasi di Rischio e Sicurezza (in italiano e in inglese).
Frasi di rischio e di sicurezza(R\S):
Le frasi di rischio e sicurezza sono delle frasi standard, contraddistinte da un codice e una
frase in linguaggio naturale, che descrivono rispettivamente i rischi per la salute umana, animale o ambientale che
una sostanza chimica può provocare e i consigli di prudenza nel manipolare tale sostanza chimica. Le frasi sono
codificate dalla direttiva europea 88/379/CEE (e successive modifiche) e attualmente vige l'obbligo di specificarle
sulle etichette delle sostanze chimiche. Oggi vengono inglobate in file *.pdf chiamate Schede di Rischio\Sicurezza
dove ci sono tutte le informazioni su una determinata Sostanza Chimica.
Situazione preesistente
La situazione preesistente riguarda un progetto , tutto da migliorare, svolto negli anni precedenti.
Quest’ultimo è composto da un DB e da un’interfaccia sviluppata in Visual Studio in C#. Per il DB è
stato usato SQL Server (la versione integrata con Visual Studio). L’interfaccia è stata
completamente rifatta sempre in Visual Studio in C#. Ciò si è reso necessario per apportare le
modifiche richieste.
7
Figura 1 : Applicazione tratta dal progetto “Progetto e sviluppo di un’applicazione per la gestione di
un Reagentario” di Samo Ziberna
Requisiti Software
Il nuovo sistema sviluppato doveva essere modificato per poter avere un uso più intuitivo e
soprattutto più semplice.
Bisogna distinguere tra gli utenti che usano l’interfaccia. Quindi si deve poter gestire le
informazioni degli utenti (credenziali con username e password) che possono essere di 3 tipi :
utente non registrato, utente registrato e administrator, ognuno con diverse possibilità di gestione
di informazioni. In caso di utenti che vengono registrati si necessitano Nome e Cognome del
utente, una password e username per accedere. Inoltre ogni utente fa parte di un Laboratorio che
usa tali sostanze.
Innanzitutto il sistema deve poter visualizzare e gestire a seconda dei casi in uso, le frasi di rischio
e sicurezza che vengono memorizzate in un file *.PDF, chiamata comunemente Scheda di
Rischio/Sicurezza che contiene tutte le informazioni della sostanza (e quindi in essa le Frasi di R\S).
La sezione deve poter essere visualizzata sia in italiano che inglese. Questa funzionalità deve
essere accessibile a chiunque. Ogni Scheda di R\S è associata ad una sostanza chimica. Ogni
sostanza chimica può avere un sinonimo e può avere una classe di appartenenza. La sostanza
chimica deve poter essere trovata da un elenco in base al nome della sostanza, al CAS number e
alla formula chimica. Ogni sostanza chimica fa parte di una determinata Classe di Sostanze. I
sinonimi hanno una fonte di informazioni (vari siti internet) ed è contradistinta da un codice. Deve
8
esserci inoltre la possibilità di poter salvare in Excel tutta la lista delle sostanze chimiche. Ogni
utente può visualizzare tutte le informazioni sulle Schede di R\S, un utente User può effettuare il
Consumo e lo Spostamento della sostanza chimica ma solo l’administrator può anche gestirle
(inserimento, modifica ed eliminazione).
L’interfaccia deve poter essere in grado anche di gestire sostanze chimiche e i rispettivi reagenti
(per gestione si intende la possibilità di registrare, quindi inserire informazioni, poterle modificare
ed eventualmente eliminarle). Alcune informazioni importanti possono essere il Nome del
reagente, Capacità del contenitore, Acquirente (persona che compra la sostanza), Data di
scadenza, Data di acquisto, Quantità Residua, Stato (Disponibile, Esaurito ed Eliminato), data di
eliminazione, i dati del fornitore del sostanza chimica (Nome, Indirizzo, Fax, Telefono, E-mail) e un
Numero Progressivo (che rappresenta un’etichetta che viene messa sulla bottiglia per poterla
contradistinguere in modo univoco e semplice soprattutto nel caso ci siano inserimenti multipli).
In questo contesto distinguiamo la sezione Consumo e la sezione Spostamento. Nella sezione
Consumo si possono visualizzare le informazioni sui possibili reagenti(contenitori e contenuto
della sostanza) da consumare (si può fare una ricerca in base al Nome della sostanza, CAS number
e Formula chimica). Per eseguire il Consumo viene richiesta un’ informazione essenziale quale la
quantità da consumare (che ovviamente deve essere minore della quantità residua, si necessita di
un controllo appropiato). Le altre informazioni visualizzate sono : la capacità del contenitore, la
locazione del contenitore, l’unità di misura, l’utente che esegue l’operazione e l’acquirente (cioè la
persona che ha acquistato la sostanza).
Nella sezione Spostamento viene modificata solo la locazione. Anche quì vengono visualizzate
informazioni quali : la quantità residua, la capacità, l’unità di Misura, l’acquirente e l’utente che ha
inserito l’informazione sulla sostanza. Anche in questa sezione come nel Consumo c’è la possibilità
di ricerca, fra tutti i possibili reagenti da restituire, in base al Nome della sostanza, CAS number e
Formula Chimica. Sia l’administrator che un utente verificato possono accedere a queste
informazioni. L’administrator in più può inserire, modificare ed disabilitare i vari utenti (username
e password), può inserire, modificare ed eliminare le sostanze chimiche ed i rispettivi reagenti(per
reagenti si intendono i contenitori della sostanza e il relativo contenuto).
Inoltre deve esserci la possibilità di fare un Report, cioè un riassunto che chiamiamo Reportistica
nella quale si dove poter ricercare tutte le operazioni svolte sui reagenti. In particolare deve
esserci la possibilità di Ricercare in tutto il DB ad esempio chi ha fatto cosa, quando lo ha fatto e
che cosa ha fatto. Devono essere progettati dei opportuni filtri di ricerca in base al tipo di
operazione svolta, in base al reagente, in base al utente e in base alla data, con ovviamente
possibilità di filtri additivi.
9
PROGETTAZIONE
DATABASE-progettazione concettuale
Tenuto conto dei Requisiti espressi nel capitolo di Analisi il passo successivo è stata la
progettazione del DB. Il primo passo è stata la progettazione concettuale. Partiamo dalla
situazione già esistente che è la seguente :
Gestione Frasi di R\S
10
Gestione Reagenti
Possiamo già vedere da questi due schemi esistenti che ci sono delle entità e relazioni che
verranno sfruttate anche nel nuovo DB. Nella Gestione Frasi di R\S sfrutteremo l’entità Sostanza
Chimica e la relazione Dizionario che la lega con l’entità Sinonimo e con l’entità Fonte dei Sinonimi.
Nella parte Gestione Reagenti verrà sfruttato interamente ad eccezione della parte di Stoccaggio e
Scarto, inoltre verrà eliminata le relazioni di Prelievo e verrà introdotto il concetto di Consumo e
Spostamento.
11
GESTIONE SOSTANZA CHIMICA :
RAGRUPPAMENTO DI FRASI
FRASI RELATIVE ALLE GESTIONE FRASI R\S Innanzitutto il sistema deve poter visualizzare e gestire a seconda dei casi in uso, le frasi di rischio e sicurezza che vengono memorizzate in un file *.PDF, chiamata comunemente Scheda di Rischio/Sicurezza che contiene tutte le informazioni della sostanza (e quindi in essa le Frasi di R\S). La sezione deve poter essere visualizzata sia in italiano che inglese. Questa funzionalità deve essere accessibile a chiunque. Ogni Scheda di R\S è associata ad una sostanza chimica. Ogni sostanza chimica può avere un sinonimo e può avere una classe di appartenenza. La sostanza chimica deve poter essere trovata da un elenco in base al nome della sostanza, al CAS number e alla formula chimica. Ogni sostanza chimica fa parte di una determinata Classe di Sostanze. I sinonimi hanno una fonte di informazioni (vari siti internet) ed è contradistinta da un codice. Deve esserci inoltre la possibilità di poter salvare in Excel tutta la lista delle sostanze chimiche.
GLOSSARIO DEI TERMINI
TERMINE DESCRIZIONE SINONIMO COLLEGAMENTO Sostanza Chimica Un corpo che
possiede proprietà chimiche e fisiche ben definite
Materia Frasi di R\S
Scheda di R\S Le frasi di rischio e sicurezza sono delle frasi standard, contraddistinte da un codice e una frase in linguaggio naturale, che descrivono rispettivamente i rischi per la salute umana, animale o ambientale che una sostanza chimica può provocare e i consigli di prudenza nel manipolare tale sostanza chimica.
Pericolosità, Frasi di R\S
Sostanza Chimica
Classe L'approccio più classico allo studio della chimica organica consiste nel raggruppare i composti in classi di sostanze che presentano un medesimo gruppo funzionale,
Gruppo Sostanza Chimica
12
definendo così una serie omologa. I composti che fanno parte di una stessa classe possiedono la stessa composizione e le stesse proprietà chimiche, mentre le loro proprietà chimico-fisiche variano in funzione della massa molecolare.
Sinonimo Definisce una sostanza chimica con le stesse proprietà chimico-fisiche soltano con termine diverso.
Sostanza Chimica
ANALISI DI ENTITÀ E RELAZIONI
Per identificare lo schema scheletro vediamo che ci sono 2 enitità essenziali : la sostanza chimica e
Sinonimi, che vengono unite dalla relazione Dizionario.
SOSTANZA CHIMICA DIZIONARIO SINONIMO
Figura 2 : Schema scheletro
Analizzando attentamente i requisiti possiamo espandere varie entità e relazioni in base ai
requisiti sopra espressi.
L’entità Sinonimo a sua volta viene unita all’entità Fonte dei Sinonimi con la relazione Sorgente.
13
SOSTANZA CHIMICA DIZIONARIO SINONIMO
SORGENTE
FONTE DEI SINONIMI
Figura 3 : Aggiunta di Entità e Relazioni intorno alla Sostanza Chimica
Raggruppando il tutto si ottiene lo schema E-R :
SOSTANZA CHIMICA
DIZIONARIO
SINONIMO
SORGENTEFONTE DEI SINONIMI
Figura 4 : Schema E-R nella Gestione Frasi R\S
ANALISI DI ATTRIBUTI E CARDINALITÀ
Ora andiamo ad analizzare gli attributi in base ai requisiti espressi.
Lo schema scheletro si compone dei seguenti attributi :per la Sostanza Chimica distinugiamo
IDSostanzaChimica, CAS, NomeSostanza, MassaMolecolare, Classe e Formula. Per le cardinalità si
osserva che l’entità Sostanza Chimica e unita alla relazione Dizionario con la cardinalità uno-molti
(0-N). Mentre la cardinalità tra la relazione Dizionario e Sinonimo è uno-molti (1-N). Quindi la
14
cardinalità che legherà le 2 entità Sostanza Chimica e Sinonimo sarà del tipo 1-N, cioè per per 1
sostanza chimica possiamo avere più sinonimi.
SINONIMODIZIONARIOSOSTANZA CHIMICA
CAS
Massa Molecolare Formula
Classe NomeSostanza
Sinonimo
1-N
Figura 5 : Schema Scheletro con attributi e cardinalità
Per l’entità Fonte dei Sinonimi abbiamo NomeFonte e urlFonte. La cardinalità che unisce l’entità
Sostanza chimica alla relazione Dizionario è del tipo 0-N mentre l’entità Sinonimo con la relazione
Dizionario è unita da una cardinalità del tipo 1-1.
SINONIMO
Sinonimo
SORGENTE
FONTE DEI SONINIMI
NomeFonte
urlFonte
0-1
1-N
Figura 6 : Parte del diagramma E-R riguardante i Sinonimi e Fonte dei Sinonimi con attributi e
cardinalità
15
Alla fine così otteniamo il diagramma E-R finale con attributi e cardinalità riguardante la gestione
Sostanza Chimica.
SOSTANZA CHIMICA
CAS
Massa Molecolare
Formula
Classe NomeSostanza
FONTE DEI SINONIMISORGENTESINONIMO
0-1 1-N
urlFonte
Sinonimo
DIZIONARIO
0-N
1-1
NomeFonte
Figura 7 : Diagramma E-R finale della gestione Sostanza Chimica
GESTIONE REAGENTI (CONTENITORI e CONTENUTO)
RAGRUPPAMENTO DI FRASI
FRASI RELATIVE ALLE GESTIONE REAGENTI Ogni utente può visualizzare tutte le informazioni sulle Schede di R\S, un utente User può effettuare il Consumo e lo Spostamento della sostanza chimica ma solo l’administrator può anche gestirle (inserimento, modifica ed eliminazione). L’interfaccia deve poter essere in grado anche di gestire sostanze chimiche e i rispettivi reagenti (per gestione si intende la possibilità di registrare, quindi inserire informazioni, poterle modificare ed eventualmente eliminarle). Alcune informazioni importanti possono essere il Nome del reagente, Capacità del contenitore, Acquirente (persona che compra la sostanza), Data di scadenza, Data di acquisto, Quantità Residua, Stato (Disponibile, Esaurito ed Eliminato), data di eliminazione, i dati del fornitore del sostanza chimica (Nome, Indirizzo, Fax, Telefono, E-mail) e un Numero Progressivo (che rappresenta un’etichetta che viene messa sulla bottiglia per poterla contradistinguere in modo univoco e semplice soprattutto nel caso ci siano inserimenti multipli). In questo contesto distinguiamo la sezione Consumo e la sezione Spostamento. Nella sezione Consumo si possono visualizzare le informazioni sui possibili reagenti(contenitori e contenuto della sostanza) da consumare (si può fare una ricerca in base al Nome della sostanza, CAS number
16
e Formula chimica). Per eseguire il Consumo viene richiesta un’ informazione essenziale quale la quantità da consumare (che ovviamente deve essere minore della quantità residua, si necessita di un controllo appropiato). Le altre informazioni visualizzate sono : la capacità del contenitore, la locazione del contenitore, l’unità di misura, l’utente che esegue l’operazione e l’acquirente (cioè la persona che ha acquistato la sostanza). Nella sezione Spostamento viene modificata solo la locazione. Anche quì vengono visualizzate informazioni quali : la quantità residua, la capacità, l’unità di Misura, l’acquirente e l’utente che ha inserito l’informazione sulla sostanza. Anche in questa sezione come nel Consumo c’è la possibilità di ricerca, fra tutti i possibili reagenti da restituire, in base al Nome della sostanza, CAS number e Formula Chimica. Sia l’administrator che un utente verificato possono accedere a queste informazioni. L’administrator in più può inserire, modificare ed disabilitare i vari utenti (username e password), può inserire, modificare ed eliminare le sostanze chimiche ed i rispettivi reagenti(per reagenti si intendono i contenitori della sostanza e il relativo contenuto). Inoltre deve esserci la possibilità di fare un Report, cioè un riassunto che chiamiamo Reportistica nella quale si dove poter ricercare tutte le operazioni svolte sui reagenti. In particolare deve esserci la possibilità di Ricercare in tutto il DB ad esempio chi ha fatto cosa, quando lo ha fatto e che cosa ha fatto. Devono essere progettati dei opportuni filtri di ricerca in base al tipo di operazione svolta, in base al reagente, in base al utente e in base alla data, con ovviamente possibilità di filtri additivi.
GLOSSARIO DEI TERMINI
TERMINE DESCRIZIONE
SINONIMO COLLEGAMENTO
Utente Personaggio che usa l’interfaccia per scopi quali la gestione (osservare, modificare ed eliminare) delle informazioni. Può essere di 3 tipi : utente sconosciuto (personaggio con informazioni non memorizzate nel DB), utente di Laboratorio (personaggio provvisto di
Personaggio Consumo, Spostamento, Laboratorio,Fornitore
17
username e password che si autentica e che può gestire le informazioni rigaurdanti le Frasi di R\S e può prelevare e\o restituire un reagente-contenitore) e administrator (personaggio che può fare tutto, cioè gestire le informazioni degli utenti, dei reagenti, delle sostanze chimiche e delle frasi di R\S).
Consumo\Spostamento
Azione che un utente compie.
Consumo\Spostamento della sostanza
Utente,Reagente
Reagente In questo contesto il Reagente sta ad indicare fisicamente un contenitore nel quali vengono messi determinate sostanze chimiche.
Contenitore Utente,Spostamento,Locazione
Fornitore Altro personaggio dal quale si compra una determinata sostanza chimica. Questa stessa sostanza chimica poi viene messa in un contenitore
Venditore Utente, Reagente
18
(reagente). Laboratorio Parte
dell’università dove si eseguono vari esperimenti usando le sostanze chimiche. Ogni utente registrato fa parte di un laboratorio.
Stanze Utente
ANALISI DI ENTITÀ E RELAZIONI
Possiamo distinguere 2 entità fondamentali : l’entità utente, e l’entità reagente unite dalla
relazione Azione che poi sarà il Consumo e Spostamento rispettivamente.
UTENTE AZIONE REAGENTE
Figura 8 : Schema scheletro nella Gestione Reagenti
In base ai requisiti sopra espressi possiamo espandere ed ottenere le seguenti nuove entità e
relazioni.
L’entità Utente sarà unita da una relazione Appartenenza con l’entità Laboratorio e la stessa entità
Utente sarà unita con l’entità Fornitore dalla relazione Acquisto.
19
UTENTE APPARTENENZA LABORATORIO
ACQUISTO
FORNITORE
Figura 9 : Diagramma E-R tra Utente e Laboratorio e Fornitore
Quindi possiamo dividere la relazione Azione nel Consumo e\o Spostamento e Eliminazione che
vengono eseguite e sono operazioni lecite.
Dai requisiti possiamo notare la necessità di inserimento di una nuova entità chiamata Operazione
e di una relazione chiamata Traccia.
REAGENTE TRACCIA OPERAZIONE
Figura 10 : Diagramma E-R tra Reagente e Operazione
Alla fine abbiamo lo schema E-R finale nella Gestione Reagenti.
20
UTENTE
APPARTENENZA
LABORATORIO ACQUISTO
FORNITORE
REAGENTECONSUMO\
SPOSTAMENTO
ELIMINAZIONE
TRACCIAOPERAZIONE
Figura 11 : Diagramma E-R finale della Gestione Reagenti (Contenitori)
ANALISI DI ATTRIBUTI E CARDINALITÀ
In base ai requisiti espresso abbiamo i seguenti attributi.
Per lo schema scheletro osserviamo gli attributi : l’entità Utente ha gli attributi Username(PK),
Password, NomeUtente e CognomeUtente; l’entità Reagente ha gli attributi DataAcquisto, Data
Eliminazione, Quantità Residua, Username, Capacità, Unità di Misura, DataScadenza, Locazione e
Progressivo.
Per quel che riguarda le cardinalità l’entità Utente e la relazione Consumo e Spostamento e l’entità
Reagente hanno una cardinalità 1-1 .
UTENTE REAGENTECONSUMO\
SPOSTAMENTO
Username
Password
NomeUtente
CognomeUtente
Username
DataAcquisto DataEliminazione
DataScadenza Capacità Username
QuantitàResidua
1-1
Locazione
Unità di Misura
Abilitazione
ELIMINAZIONE
Progressivo
1-1
Acquirente
Figura 12 : Schema scheletro della Gestione Reagenti con attributi e cardinalità
Analizzando le altre entità della Gestione Reagenti possiamo notare i seguenti attributi : per
l’entità Laboratorio abbiamo NomeLaboratorio; per l’entità Fornitore abbiamo NomeFornitore,
21
Indirizzo, Telefono, Fax e Email. Per la tabella Operazione abbiamo i seguenti attributi :
Operazione, Data e Locazione Passata.
Le cardinalità invece riscontrate sono : tra l’entità Laboratorio e la relazione Appartenenza
abbiamo una 0-N, mentre tra la relazione Appartenenza e l’entità Utente 1-1; tra l’entità Utente e
quella Fornitore abbiamo una relazione Acquisto che le unisce con una cardinalità 0-N; l’entità
Reagente e al relazione Acquisto hanno una cardinalità 1-1. La cardinalità che unisce l’entità
Reagente con l’entità Operazione è 1-N.
UTENTE APPARTENENZA LABORATORIO
ACQUISTO
FORNITORE
NomeLaboratorio
Nome
Indirizzo
Telefono
Fax
1-1N-0
0-N REAGENTE
1-1
OPERAZIONE
TRACCIA
1-N
Operazione
Data
Locazione Passata
Figura 13 : Diagramma E-R tra Utente e Laboratorio e Fornitore con attributi e cardinalità
Alla fine così possiamo ottenere lo schema finale E-R con attributi e cardinalità.
22
Figura 14 : Schema finale E-R della Gestione Reagenti
DATABASE-progettazione logica
Dopo aver svolto la progettazione concettuale passiamo a quella logica. Primo passo definiamo la
Tavola dei Volumi.
GESTIONE SOSTANZA CHIMICA
TAVOLA DEI VOLUMI
Una possibile tavola dei volumi è la seguente
CONCETTO TIPO VOLUME Sostanza Chimica E 1000
Sinonimo E 106
Fonte dei Sinonimi E 15
UTENTE REAGENTECONSUMO\
SPOSTAMENTO
Username
Password
NomeUtente
CognomeUtente
DataAcquisto DataEliminazione
DataScadenzaCapacità
Username
QuantitàResidua
Locazione
Unità di Misura
APPARTENENZA
LABORATORIO ACQUISTO
FORNITORE
NomeLaboratorio
Nome
Indirizzo
Telefono
Fax
1-10-N
1-1
0-N
Abilitazione
ELIMINAZIONE
1-1
1-1
Progressivo
TRACCIA
OPERAZIONE
oPERAZIONE
dATALocazione Passata
1-N
Acquirente
23
TAVOLA DELLE OPERAZIONI E DEGLI ACCESSI
Analizzando lo schema concettuale , possiamo simulare il funzionamento del DB e quindi proporre
una possibile tavola delle operazioni e degli accessi. Ogni Sostanza Chimica può avere più di 1
Sinonimo così come la Fonte può essere più di 1.
Una possibile tavola delle operazioni può essere la seguente :
OPERAZIONE TIPO FREQUENZA Ricerca di una Sostanza Chimica
L 10 volte al giorno
Inserimento di un nuovo Sinonimo di una Sostanza
S 1 volta a settimana
Inserimento di una nuova Sostanza Chimica
S 1 volta a settimana
Inserimento nuova Fonte dei Sinonimi
S 1 volta a settimana
Per quel che riguarda la ricerca di una sostanza chimica possiamo ad esempio avere una seguente
tabella degli accessi :
CONCETTO COSTRUTTO ACCESSI TIPO Sostanza Chimica Entità 1 L
Sinonimo Entità 3 L
Fonte dei sinonimi Entità 2 L
Ovviamente la stessa tavola può essere fatta nel caso in cui inseriamo i dati, questo caso il tipo di
operazione sarebbe quello di scrittura.
Ad esempio se eseguiamo un inserimento di una nuova sostanza chimica, possiamo avere una
tabella degli accessi come la seguente :
CONCETTO COSTRUTTO ACCESSI TIPO Sostanza Chimica Entità 1 S
Sinonimo Entità 1 S
Fonte dei sinonimi Entità 2 S
24
RISTRUTTURAZIONE DELLO SCHEMA E-R
PARTIZIONAMENTO\ACCORPAMENTO DI ENTITÀ E RELAZIONI
Ora vediamo dei ulteriori possibili cambiamenti per ottimizzare lo schema E-R finale nella Gestione
Sostanza Chimica. Analizziamo l’attributo Classe. Infatti lo possiamo pensare come un’entità
separata ed unito con la relazione Classe-Sostanza con cardinalità 1-1.
Tenuto conto delle considerazioni, possiamo ottenere il seguente schema E-R ristrutturato
SOSTANZA CHIMICA
DIZIONARIO
SINONIMO
SORGENTE
FONTESinonimo
NomeFonte
urlFonte
1-N
1-N
SOSTANZA CHIMICA
CAS
Massa Molecolare
Formula
NomeSostanza
CLASSE-SOSTANZA
CLASSE
1-1
Classe
Figura 15 : Schema E-R della Gestione Sostanza Chimica ristrutturato
25
SCELTA DEGLI IDENTIFICATORI PRIMARI
Questa parte è l’ultimo ritocco allo schema E-R prima della traduzione verso il modello logico. Ogni
entità ha una sua chiave primaria che la identifica. Così facendo lo schema E-R è il seguente (le
chiavi primarie PK sono contrassegnate di rosso) :
SOSTANZA CHIMICA
DIZIONARIO
SINONIMO
SORGENTE
FONTE
Sinonimo
NomeFonte
urlFonte
1-N1-N
SOSTANZA CHIMICA
CAS
Massa Molecolare
Formula
Classe
NomeSostanzaIDSostanza Chimica
IDSinonimo
IDFonte
CLASSE-SOSTANZA
CLASSE
1-1
Classe
IDClasse
Figura 16 : Schema E-R ristrutturato con PK
SCELTA DEI VINCOLI DI INTEGRITÀ REFERENZIALE
La relazione Dizionario invece verrà eliminata e verrà inserita la chiave esterna nell’entità
Sinonimo (IDSostanzaChimica). Anche la relazione Sorgente sarà eliminata e nell’entità Sinonimo
verrà inserita la chiave esterna IDSinonimoFonte che la collegherà all’entità Fonte e la chiave
esterna IDSostanzaChimica per collegarla con l’entità SostanzaChimica.
26
LEGGENDA:
chiave primaria
chiave esterna
SOSTANZA CHIMICA
Codice Frasi di Rischio
Codice Frasi di Sicurezza
DIZIONARIO
SINONIMO
SORGENTE
FONTE
Sinonimo
NomeFonte
urlFonte
0-N
1-1
0-1
1-N
SOSTANZA CHIMICA
CAS
Massa Molecolare
Formula
NomeSostanzaIDSostanza Chimica
IDSinonimo
IDFonte
Classe-Sostanza
Classe
1-1
IDClasse
Classe
IDClasse
IDSostanza ChimicaIDSinonimoFonte
Figura 17 : Schema concettuale ristrutturato con chiavi primarie e esterne della Gestione Sostanza
Chimica
27
GESTIONE REAGENTI
TAVOLA DEI VOLUMI
Una possibile tavola dei volumi è la seguente
CONCETTO TIPO VOLUME Utente E 6
Laboratorio E 6
Fornitore E 6
Reagente E 500 Operazione E 100
TAVOLA DELLE OPERAZIONI E DEGLI ACCESSI
Analizzando lo schema concettuale , possiamo simulare il funzionamento del DB e quindi proporre
una possibile tavola delle operazioni e degli accessi. Ogni Utente con delle credenziali fa parte di
un Laboratorio (sia questo utente registrato o administrator) e può eseguire uno Spostamento e\o
Consumo. Solo l’administrator può inserire nuovi dati sul fornitore e sul reagente (nuovo
contenitore e contenuto).
Supponiamo di essere un’utente registrato con account User con delle credenziali valide (inserite
da un administrator). Una possibile tavola delle operazioni può essere la seguente :
OPERAZIONE TIPO FREQUENZA 1) Consumo :Prelievo di
una quantità da un contenitore di una sostanza chimica.
S 1 volta al mese
2) Spostamento : cambio di locazione del contenitore e contenuto.
S 1 volta alla settimana
Per quel che riguarda l’operazione n.1 possiamo ad esempio avere una seguente tabella degli
accessi :
CONCETTO COSTRUTTO ACCESSI TIPO Consumo Relazione 1 L
Reagente Entità 1 S
Sostanza Chimica Entità 1 L
Operazione Entità 1 S
28
Il reagente e la sostanza chimica servono in questa operazione come dati di supporto.
Un’altra operazione molto comune può essere l’operazione n.2 cioè lo Spostamento In questo
caso una possibile tavola degli accessi è la seguente :
CONCETTO COSTRUTTO ACCESSI TIPO Spostamento Relazione 1 L
Reagente Entità 1 S
Sostanza Chimica Entità 1 L
Operazione Entità 1 S
Nel caso del administrator (che può fare tutto) invece una possibile tavola delle operazioni è la
seguente :
OPERAZIONE TIPO FREQUENZA 1) Consumo : prelievo di
una quantità da un contenitore di una sostanza chimica.
S 1 volta al mese
2) Spostamento S 1 volta ogni 2mesi
3) Inserimento dati nuovi utenti
S 1 volta all’anno
4) Inserimento dati nuovo Reagente (Contenitore e contenuto)
S 1 volta all’anno
5) Inserimento dati nuovo Fornitore
S 1 volta all’anno
Le operazioni n.1,2 e sono state descritte sopra e sono le medesime anche nel caso administrator.
L’operazione n.3 può portare ad una tavola degli accessi come la seguente :
CONCETTO COSTRUTTO ACCESSI TIPO Utente Entità 1 S Laboratorio Entità 1 L
29
L’operazione n.4 invece può portare ad una tavola degli accessi seguente :
CONCETTO COSTRUTTO ACCESSI TIPO Reagente Entità 1 S
Locazione Entità 1 L
Fornitore Entità 1 L
Operazione Entità 1 S
L’operazione n.5 invece può avere una tavola degli accessi come la seguente :
CONCETTO COSTRUTTO ACCESSI TIPO Fornitore Entità 1 S
RISTRUTTURAZIONE DELLO SCHEMA E-R
PARTIZIONAMENTO\ACCORPAMENTO DI ENTITÀ E RELAZIONI
Come prima cosa vediamo la necessità di scomporre l’entità Reagente. Dallo schema E-R finale
della Gestione Reagenti notiamo la relazione Acquisto tra le entità Reagente e Fornitore. Dai
requisiti sopra espresso abbiamo la necessità di distinguere 2 entità separate : così otteniamo
l’entità ReagenteDF (dati fissi cioè riguarda il vero e proprio contenitore) e ReagenteDV (dati
variabili cioè il vero e proprio prodotto chimico fisicamente presente nel contenitore). All’entità
ReagenteDF verranno assegnati gli attributi di un vero e proprio contenitore (Nome Reagente,
Capacità, Data scadenza, Data eliminazione, Data acquisto) mentre all’entità ReagenteDV vengono
assegnati gli attributi seguenti : quantità residua, stato e locazione. Le due entità vengono unite
dalla relazione RDV-DF. La relazione Acquisto si collega con l’entità ReagenteDF. Tenuto conto dei
seguenti cambiamenti lo schema E-R finale ristrutturato per la Gestione Reagenti diventa il
seguente :
30
UTENTE REAGENTEDFCONSUMO\
SPOSTAMENTO
Username
Password
NomeUtente
CognomeUtente
DataAcquistoDataEliminazion
e
DataScadenza Capacità
QuantitàResidua
N-0
Unità di Misura
APPARTENENZA
LABORATORIO ACQUISTO
FORNITORE
NomeLaboratorio
Nome
Indirizzo
Telefono
Fax
1-10-N
1-1
0-N
RDF-DV
REAGENTEDV
1-1
1-1
ELIMINAZIONE
1-1
N-0
Stato
Locazione
1-1
TRACCIA
OPERAZIONEOperazione
Data Locazione passata
N-1Acquirente
Figura 18 : Schema E-R della Gestione Reagenti ristrutturato
SCELTA DEGLI IDENTIFICATORI PRIMARI
Questa parte è l’ultimo ritocco allo schema E-R prima della traduzione verso il modello logico. Ogni
entità ha una sua chiave primaria che la identifica. Così facendo lo schema E-R è il seguente (le
chiavi primarie PK sono contrassegnate di rosso) :
31
UTENTE REAGENTEDFSPOSTAMENTO\
CONSUMO
Username
Password
NomeUtente
CognomeUtente
DataAcquisto DataEliminazione
DataScadenza Capacità Username
QuantitàResidua
N-0Unità di Misura
APPARTENENZA
LABORATORIO
ACQUISTO
FORNITORE
NomeLaboratorio
Nome
Indirizzo
Telefono
Fax
1-10-N
1-1
0-N
RDF-DV
REAGENTEDV
IDReagenteDF
IDReagenteDV
IDLaboratorio
IDFornitore
1-1
1-1
ELIMINAZIONE
1-1
N-0
Stato
Locazione
1-1
Abilitazione
OPERAZIONE
IDOperazione
Operazione
Data
Locazione passata
TRACCIA
N-1
Acquirente
Figura 19 : Schema E-R della Gestione Reagenti ristrutturato con PK
SCELTA DEI VINCOLI DI INTEGRITÀ REFERENZIALE
Prima di analizzare i vincoli di integrità referenziale abbiamo la necessità di scomporre l’entità
ReagenteDV in altre 2 entità separate. Una sarà l’entità Stato (ed indicherà se la sostanza chimica
è Disponibile, Esaurita(consumata tutta la quantità) o Eliminata (smaltita per qualche motivo)) e
l’altra sarà l’entità Locazione (cioè il luogo dove si trova). La cardinalità che si trova scomponendo
questa entità è la seguente : tra l’entità Locazione e la nuova relazione Presenza (che non verrà
mentenuta) abbiamo una cardinalità di 0-N, e tra la relazione Presenza e l’entità ReagenteDV 1-1 ;
tra l’entità ReagenteDV e la relazione Stato-ReagenteDV 1-1 e tra la stessa relazione e l’entità
Stato 1-1. Con questa scomposizione possiamo assegnare le chiavi esterne. Nell’entità
ReagenteDV abbiamo 2 chiavi esterne : IDStato (per collegarla all’entità Stato) e IDLocazione(per
collegarla all’entità Locazione). Nell’entità ReagenteDF abbiamo le seguenti chiavi esterne :
IDFornitore (per collegarla all’entità Fornitore) ,IDMisura (per collegarla all’entità Unità di Misura)
e Username(per collegarla all’entità Utente-serve per tenere traccia di chi ha inserito
l’informazione). Qui la chiave primaria dell’entità ReagenteDV fa anche da chiave esterna per
collegarla all’entità ReagenteDF, e anche la chiave primaria dell’entità ReagenteDF fa da chiave
esterna per collegarla con l’entità ReagenteDV (questo a causa della scomposizione dell’entità
REAGENTE e cardinalità 1-1 presente). Un’altra scomposizione molto utile arriva nell’entità
Utente. Infatti possiamo pensare ad un’entità Abilitazione separata dall’entità Utente. Questa
entità serve per capire se un utente registrato è un administrator, un utente normale con
32
credenziali abilitato ad eseguire il consumo\spostamento. Così nell’entità Utente ci troviamo una
chiave esterna IDAbilitazione (serve per collegarsi con la tabella Abilitazione) e IDLaboratorio
(serve per collegarsi con la tabella Laboratorio). Le altre relazioni non vengono mantenute, di
conseguenza lo schema logico diventa il seguente.
LEGGENDA:
chiave primaria
chiave esterna
UTENTE REAGENTEDFCONSUMO\
SPOSTAMENTO
Username
Password
NomeUtente
CognomeUtente
DataAcquisto DataEliminazione
DataScadenza Capacità Username
QuantitàResidua
N-0APPARTENENZA
LABORATORIO
ACQUISTO
FORNITORE
NomeLaboratorio
Nome
Indirizzo
Telefono
Fax
1-10-N
1-1
0-N
RDF-DV
REAGENTEDV
IDReagenteDF
IDReagenteDV
IDLaboratorio
IDFornitore
1-1
1-1
ELIMINAZIONE
1-1
N-0
Stato
Locazione
1-1
REAGENTEDF-UNITA
UNITA DI MISURA
IDUnitàDiMisura
Misura
1-1
1-1
REAGENTEDV-STATO
STATO
IDStato
PRESENZA
1-1
LOCAZIONE
IDLocazione
0-N
UTENTE-ABILIATZIONE
ABILITAZIONE
IDAbilitazione
Abilitazione
1-1
1-1
1-1
1-1
IDAbilitazione
IDLaboratorio
IDLocazione
IDStato
IDMisura
IDFornitore
TRACCIA
OPERAZIONE
N-1
Username
IDreagenteDV
IDOperazione Data
Operazione
Locazione passata
Acquirente
Figura 20 : Schema concettale ristrutturato con chiavi primarie e esterne della Gestione Reagenti
33
INTERFACCIA
L’interfaccia è stata fatta seguendo in modo dettagliato tutti i Requisiti che sono stati espressi in
fase di Analisi. L’interfaccia è di tipo Windows Form. Di seguito viene riportato un possibile
diagramma di utilizzo dell’applicazione.
Inizializzazione Utente
Consumo\Spostamento
Reportistica
Gestione Reagente
Gestione Sostanza Chimica
Gestione Utenti
UTENTE REGISTRATO
ADMINISTRATOR
PERSONAGGIO QUALSIASI
Figura 21: Diagramma di utilizzo dell’applicazione
Inizializzazione Utenti
Intanto distinguiamo 3 tipi di personaggi : utente qualsiasi, utente registrato e administrator. Ogni
tipo di personaggio deve poter visualizzare le schede di Rischio e di Sicurezza di una sostanza
chimica (se questa sostanza in effetti ne è provvista). L’utente qualsiasi non può fare altro, mentre
per un utente registrato e l’administrator possono fare anche altre cose. Ciò comporta
l’inserimento di un sistema in grado di poter distinguere tra i diversi personaggi, cioè una
schermata di LogIn. Questa è la prima parte e possiamo chiamarla Inizializzazione Utenti cioè
distinguiamo i 3 tipi di personaggi che possono venir coinvolti.
Questa parte è formata da 2 windows form. Per permettere una migliore navigazione da un form
all’altro è stato sfruttato il meccanismo a schede chiamato TabControl. Quindi abbiamo 2 schede :
una per la visualizzazione e ricerca delle Sostanze Chimiche e delle relative Schede di
Rischio\Sicurezza e l’altra per effettuare il meccanismo di LogIn.
34
Rischio \ Sicurezza
Schede di Rischio\Sicurezza
ADMINISTRATOR
UTENTE QUALSIASI
UTENTE REGISTRATO
LogIn
Autenticazione
Save to Excel
Ricerca in base a CAS,Nome,Formula
Username & Password
UTENTE REGISTRATO
ADMINISTRATOR
Figura 22 : Inizializzazione Utenti
Consumo\Spostamento
Questa parte può essere raggiunta solo da un’utente registrato o administrator. Qui abbiamo le
seguenti schede : Rischio\Sicurezza (la stessa con vista nella visualizzazione utenti), Consumo
(dove si memorizzano i dati di consumo) e Spostamento (si memorizzano i dati di spostamento) e
LogOut.
35
Rischio \ Sicurezza
Schede di Rischio\Sicurezza
ADMINISTRATOR
UTENTE REGISTRATO
LogOut
Save to Excel
Ricerca in base a CAS,Nome,Formula
Ritorno all�Inizializzazione
Utenti
Consumo
Visualizzazione di informazioni relative al Consumo
Spostamento
Visualizzazione di informazioni relative allo Spostamento
Ricerca in base a CAS,Nome,Formula
Ricerca in base a CAS,Nome,Formula
Prelievo Reagente
Restituzione Reagente
Inserimento Dati
Inserimento Dati
Figura 23 : Consumo\Spostamento
36
Gestione Utenti
Nella Gestione Utenti, raggiungibile solo da un administrator, quest’ultimo può modificare le
credenziali di un’utente, inserire un nuovo utente ed disabilitarlo (non eliminarlo in quanto
relative azioni compiute sono state ragistrate e possono essere utili a fini di ricerche ben precise
nella Reportistica).
ADMINISTRATORGestione Utenti
Visualizzazione di tutte le informazioni degli utenti
Nuovo Utente
Modifica Utente
Disabilita Utente
Figura 24 : Gestione Utenti
37
Gestione Sostanza Chimica
Questa parte viene raggiunta solo da un administrator. Questa scheda permette di : Ricercare Una
sostanza chimica in base al CAS, Nome e Formula, Gestire una Sostanza Chimica (per gestire si
intende Aggiungere, Modificare e Eliminare) e Gestire i Sinonimi..
ADMINISTRATOR
Gestione Sostanza Chimica
Visualizzazione di tutte le informazioni delle sostanze chimiche, inclusi Sinonimi.
Nuova Sostanza Modifica Sostanza Elimina Sostanza
Nuovo Sinonimo Modifica Sinonimo Elimina Sinonimo
Ricerca in base a CAS,Nome,Formula
Save in Excel
Figura 25 : Gestione Sostanza Chimica
38
Gestione Reagente
In questa parte l’administrator può ricercare sempre la sostanza chimica desiderata in base al
Nome, CAS e Formula. Poi in base alla sostanza chimica scelta, si visualizza il contenitore con il
contenuto corrispondente e lo stato della sostanza (Disponibile, Esaurito, Eliminato). Inoltre in
questa scheda c’è la possibilità di inserire i dati di un Fornitore.
ADMINISTRATOR
Gestione Reagenti
Visualizzazione di tutte le informazioni dei Reagenti
Ricerca in base a CAS,Nome,Formula
Ricerca in base allo stato del Reagente
Nuovo Reagente
Modifica Reagente
Elimina ReagenteSave in Excel
Nuovo Fornitore
Inserimento dati
Figura 26 : Gestione Reagenti
Traduzione Italiano-Inglese
Dall’analisi dei Requisiti si deduce che c’è bisogno di una possibilità di visualizzazione in Inglese.
Quindi con un semplice click si può passare alla traduzione dell’intera interfaccia e dei dati dal DB.
Questo vale per qualsiasi personaggio stia agendo e in qualsiasi momento lo si desideri su qualsiasi
scheda si stia operando.
Diagramma UML delle classi
Se analizziamo il diagramma di utilizzo possiamo per ogni form che è stato creato analizzare il
diagramma delle classi create in UML.
39
Inizializzazione Utenti
Qui abbiamo 1 form (Form1) e 2 schede. Il Form1 è formato dai seguenti oggetti e metodi :
Figura 27
40
Consumo\Spostamento
Qui abbiamo 1 form (Form2) e fino a un massimo di 7 (in base all’utente) schede. Il Form2 è
formato dai seguenti oggetti, metodi e proprietà :
Figura 28
41
Poi in base alla figura 24 distinguiamo il Form3 necessario per l’inserimento dei dati di Consumo . Il
Form3 è formato dai seguenti oggetti, metodi e proprietà :
Figura 29
42
Il Form4 invece riguarda lo Spostamento. Il Form ha i seguenti metodi, oggetti e proprietà :
Figura 30
43
Gestione Reagente
Il form5 invece serve per modificare i dati di un reagente (contenitore). Il form ha i seguenti
metodi, proprietà ed oggetti :
Figura 31
44
Invece il Form6 serve eventualmente per inserire dati di un nuovo Fornitore. Questo Form ha i
seguenti metodi, oggetti e proprietà :
Figura 32
Mentre il seguente Form8 server per Eliminare il reagente selezionato. Oggetti, metodi e proprietà
sono i seguenti :
45
Figura 33
Il Form 7 server invece per Inserire i dati di un nuovo Reagente. Il Form ha i seguenti oggetti,
metodi e proprietà :
46
Figura 34
47
Gestione Sostanza Chimica
Il Form9 invece server per aggiungere un nuovo Sinonimo ad una sostanza chimica. Oggetti,
metodi e proprietà sono i seguenti :
Figura 35
Il Form10 invece server per inserire eventualmente i dati di una nuova Fonte. Oggetti, metodi e
proprietà sono i seguenti :
48
Figura 36
Il Form11 invece serve per Modificare i dati di un Sinonimo di una sostanza chimica. Oggetti,
metodi e proprietà sono i seguenti :
49
Figura 37
Il Form 12 invece server per l’inserimento dei dati di una nuova sostanza chimica. Oggetti, metodi
e proprietà sono i seguenti :
50
Figura 38
Salva dati in Excel
Il Form CopiaExcel è un form creato per far aspettare l’utente, in modo standard, durante un
salvataggio da una griglia con molti dati ad un foglio Excel. Ogetti e metodi sono i seguenti :
Figura 39
51
Gestione Utenti
In questa scheda invece vengono gestite e informazioni degli utenti. Oggetti, metodi e proprietà
sono i seguenti :
Figura 40
52
Informazioni
Quest’ultimo Form contiene solo le informazioni sull’autore del programma. Oggetti, metodi e
proprietà sono i seguenti :
Figura 41
Schema finale
Di conseguenza possiamo ottenere lo schema finale che è il seguente :
Gestione Sostanza Chimica
Gestione Reagenti Gestione Utenti
Consumo\Spostamento
Info
Salvataggio su Excel
53
REALIZZAZIONE
DATABASE-progettazione fisica
Dalla progettazione logica con lo schema con chiavi primarie e esterne si arriva allo schema fisico
che rappresenta in modo fedele la progettazione del DB. Prima analizziamo le 2 parti separate per
poi trovare un punto d’incontro e unire il tutto.
GESTIONE SOSTANZA CHIMICA
Dall’ultimo schema della progettazione logica possiamo ottenere questo schema della
progettazione fisica.
tblSostanzaChimica
NomeSostanza
IDSostanzaChimicaPK
IDClasseFK
CAS
MassaMolecolare
Formula
tblClasse
IDClassePK
Classe
tblSinonimo
IDSinonimoPK
IDSostanzaChimicaFK
Sinonimo
IDSinonimoFonteFK
tblFonte
IDFontePK
NomeFonte
urlFonte
Figura 42 : Schema fisico Gestione Sostanza Chimica
54
GESTIONE REAGENTI
Dall’ultimo schema della progettazione logica possiamo ottenere questo schema della
progettazione fisica.
tblUtente
NomeUtente
UsernamePK
IDLaboratorioFK
CognomeUtente
Password
IDAbilitazioneFK
tblAbilitazione
IDAbilitazionePK
Abilitazione
tblLaboratorio
IDLaboratorioPK
Nome Laboratorio
tblOperazione
Operazione
IDOperazionePK
UsernameFK
Data
IDReagenteDVFK
Locazione passata
tblReagenteDV
IDReagenteDVPKFK
IDStatoFK
Quantità Residua
IDLocazioneFK
tblStato
IDstatoPK
Stato
tblLocazione
IDLocazionePK
Locazione
tblReagenteDF
IDReagenteDFPKFK
IDFornitoreFK
DataAcquisto
IDMisuraFK
UsernameFK
Capacità
DataScadenza
tblUnitàDiMisura
IDMisuraPK
Misura
tblFornitore
Indirizzo
IDFornitorePK
NomeFornitore
Telefono
Fax
Acquirente
Figura 43 : Schema fisico della Gestione Reagenti
55
UNIONE DELLE GESTIONI E SCHEMA FINALE
Fin’ora per comodità di progettazione e analisi la Gestione Reagenti e la Gestione Sostanza
Chimica sono state analizzate separatamente. In realtà bisogna trovare ora un punto d’incontro,
cioè una relazione che lega le 2 parti in modo tale da poter considerare il tutto come un unico DB.
Partendo dalla progettazione concettuale possiamo trovare un punto d’incontro che per la parte
della Gestione Reagenti può essere l’entità ReagenteDF mentre nella parte della Gestione
Sostanza Chimica possiamo trovare un punto d’incontro nell’entità SostanzaChimica.
REAGENTEDF SOSTANZA CHIMICAReagente-Sostanza
N-1
Figura 44 : Unione a livello concettuale delle 2 entità principali delle Gestione Sostanza Chimica e Reagenti
Ora basta solo considerare tutti gli schemi fin’ora visti come validi ma tenere conto di questa unione.
Basterà inserire una chiave esterna nella tblReagenteDF di nome IDSostanzaChimica e l’unione è completa.
Quindi possiamo osservare prima lo schema E-R finale ristrutturato con attributi, chiavi primarie e esterne.
56
LEGGENDA:
chiave primaria
chiave esterna
REAGENTE-SOSTANZA
Figura 45 : Schema E-R finale con Gestione Sostanza Chimica e Gestione Reagenti
1-N
57
Di conseguenza lo schema fisico del DB risulta il seguente
tblSostanzaChimica
NomeSostanza
IDSostanzaChimicaPK
IDClasseFK
CAS
MassaMolecolare
Formula
tblClasse
IDClassePK
ClassetblSinonimo
IDSinonimoPK
IDSostanzaChimicaFK
Sinonimo
IDSinonimoFonteFK
tblFonte
IDFontePK
NomeFonte
urlFonte
tblUtente
NomeUtente
UsernamePK
IDLaboratorioFK
CognomeUtente
Password
IDAbilitazioneFK
tblAbilitazione
IDAbilitazionePK
AbilitazionetblLaboratorio
IDLaboratorioPK
Nome Laboratorio
tblOperazione
Operazione
IDOperazionePK
UsernameFK
Data
IDReagenteDVFK
Locazione passata
tblStato
IDStatoPK
Stato
ReagenteDV
IDReagenteDVPKFK
IDStatoFK
Quantità Residua
IDLocazioneFK
tblLocazione
IDLocazionePK
Locazione
tblUnitàDiMisura
IDMisuraPK
Misura
ReagenteDF
IDReagenteDFPKFK
IDFornitoreFK
Data Scadenza
IDMisuraFK
UsernameFK
IDSostanzaChimicaFK
Data Acquisto
Capacità
tblFornitore
Indirizzo
IDFornitorePK
NomeFornitore
Telefono
Fax
Figura 46 : Schema fisico con Gestione Sostanza Chimica e Gestione Reagenti
58
SCELTA DEGLI INDICI
Questa fase è di relativa importanza in quanto, SQL Server in generale permette l’indicizzazione
dei vari campi nelle tabelle. Indicizzazione viene fatte per incrementare le prestazioni durante le
varie query sul DB. Ci sono delle norme da seguire per la definizione degli indici che è bene
seguire. Queste sono :
1. Analisi delle operazioni di SELECT più importanti : in questo progetto le operazioni più
comuni, di tipo SELECT che vengono fatte sono quelle di ricerca di un record in una tabella;
2. Analizzare le tabelle con un numero di dati elevati : in questo caso la tabella
tblSostanzaChimica ha un numero di record più elevato rispetto agli altri (circa 5000).
Inoltre è la tabella sulla quale vengono fatte le SELECT più spesso;
3. Analisi delle prestazioni delle varie SELECT sui campi : le operazioni di ricerca fatte con le
SELECT riguardano principalmente il campo CAS della tabella tblSostanzaChimica. L’utente
finale infatti userà quasi sempre il codice CAS per cercare una sostanza chimica in tabella.
Gli altri campi potenziali, quali NomeSostanza e Formula non sono considerati così
fondamentali nella ricerca di una sostanza dall’utente finale, inoltre siccome sono
implicate in altre operazioni (non necessariamente SELECT) la loro indicizzazione
comporterebbe un degrado delle prestazioni;
4. Decisione sul tipo di indice da attivare nel campo selezionato : è stati scelto di usare l’indice
tipo non cluster. Diversamente da un indice cluster un indice non cluster non memorizza
direttamente una parte dei dati in tabella ma memorizza puntatori ai dati corrispondenti
alle colonne in esso definite. Questo tipi di indice viene memorizzato in una struttura
separata rispetto alla tabella. Quando si effettua una ricerca su dati memorizzati su una
tabella sulla quale è definito un indice non cluster SQL Server usa l’informazione definita
nei puntatori dell’indice per individuare le righe che corrispondono ai criteri della ricerca.
Inoltre il tipo di indice qui è stato definito come unique cioè unico. Questo tipo di indice
assicura che i valori contenuti nelle colonne definite nell’indice siano univoci all’interno
della tabella. Infatti il codice CAS è un codice univoco che identifica il record e non può
essere duplicato, per ogni sostanza esiste 1 ed 1 solo;
Tenendo conto di queste osservazioni possiamo concludere che bisogna definire un indice
UNIQUE NON CLUSTER.
Quindi nel DB è stato definito in questo modo :
GO CREATE UNIQUE NONCLUSTERED INDEX [IDX_tblSostanzaChimica] ON [dbo].[tblSostanzaChimica]([CAS] ASC) WHERE ([CAS] IS NOT NULL);
59
IMPLEMENTAZIONE DELL’INTERFACCIA
L’interfaccia utente (GUI) è di tipo Windows Form. A seconda dei casi descritti, ogni Windows
Form ha 2 o più schede. Ogni scheda ha la sua parte grafica ma il codice è sullo stesso Form. Ciò è
stato utile per semplificare la programmazione. Questo sistema è noto col nome TABCONTROL.
Per maggiori informazioni msdn.microsoft.com.
STRINGA DI CONNESSIONE
Durante la programmazione in Visual Studio, l’IDE crea un file nel quale viene memorizzata la
stringa di connessione al DB. Questo file APP.CONFIG si chiama file di configurazione. Questa
particolarità è fondamentale per il corretto funzionamento.
Il contenuto del file è il seguente :
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> </configSections> <connectionStrings> <add name="WindowsFormsApplication1.Properties.Settings.ReagentarioConnectionString"
connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Reagentario.mdf;Integrated Security=True;Connect Timeout=30"
providerName="System.Data.SqlClient" /> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
La stringa di connessione è la seguente
Le stringhe di connessione possono essere archiviate come coppie chiave/valore nella
sezione connectionStrings dell'elemento configuration di un file di configurazione
dell'applicazione. Gli elementi figlio includono add, clear e remove. L'attributo name corrisponde
al nome specificato per identificare in modo univoco una connessione, in modo che possa essere
recuperato in fase di esecuzione. L'attributo providerName è il nome invariabile del provider di
dati .NET Framework, registrato nel file machine.config. Per maggiori informazioni
msdn.microsoft.com .
60
OGGETTI DI TIPO SQLCOMMAND
Prima di iniziare a spiegare l’interfaccia come è stata realizzata, iniziamo a spiegare in quale modo
la GUI realizzata comunica con il DB. Questo avviene grazie alla classe SQLCommand. Rappresenta
un'istruzione Transact-SQL o una stored procedure da eseguire in relazione a un database SQL
Server. Questa classe non può essere ereditata. Quindi si capisce che grazie a questa classe le
query vengono scritte direttamente sul codice. Ciò comporta essenzialmente l’introduzione di uno
strato di Middleware che consente all’applicazione di eseguire sul DB tutte le operazioni
pianificate che sono essenzialmente le SELECT,INSERT,UPDATE e DELETE.
DB MIDDLEWARE GUI
Nel successivo paragrafo di spiegazione del codice vengno spiegati in dettaglio tutte le operazioni.
Gli oggetti di tipo SQLComand usati nel progetto vengono inzializzati in questo modo :
1) Si apre connessione col DB;
2) Si crea l’oggetto di tipo SQLComand;
3) Si memorizza la query (che viene vista da codice come una semplice stringa);
4) Si sfrutta il metodo ExecuteNonQuery per eseguire l’istruzione TSQL.
5) Si chiude connessione col DB;
Per maggiori informazioni e ulteriori approfondimenti sul argomento visitare il sito
msdn.microsoft.com
SQL Server
Ogetti
SQLComand:
SELECT,INSERT,UP
DATE,DELETE…
DATAGRIDVIEW
61
DESCRIZIONE DELL’INTERFACCIA
Form D’ingresso
Partiamo dal Form Principale d’ingresso. Qui possono accedere tutti. Nella Scheda
Rischio\Sicurezza è assicurata la funziona de Ricerca della sostanza chimica in base a 3 criteri di
ricerca : CAS, Nome e Formula. Quindi è stato usato una textbox, un bottone, 3 checkbox per
determinare il criterio di ricerca più adeguato e una datagridview dove vengono presentati i dati.
Basta scorrere la lista con la tastiera o mouse per visualizzare la sostanza, i relativi dati. È stato
aggiunta la possibilità di salvare la lista delle sostanze chimica visualizzata in file Excel grazie ad un
bottone apposito. In Figura vedimao uno screen shot dell’applicazione in uso.
Figura 47 : Scheda Rischio\Sicurezza del Form Iniziale
62
La ricerca della Sostanza chimica lungo il datagridview è un evento che è reso possibile da
chiunque ed è frequente in questo programma. In seguito viene presentato il relativo codice con
annessa spiegazione.
1. SqlConnection con = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Reagentario\DB\Reagentario.mdf;Integrated Security=True;Connect Timeout=30;");
a. public bool contaITA = true; b. public bool contaEN = false;
2. public Form1() a. {
i. InitializeComponent(); b. }
3. private void Form2_Load(object sender, EventArgs e) a. {
i. con.Open();
ii. buttonRicerca.Enabled = false; iii. dataGridView1.ClearSelection(); iv. SqlCommand cmd = con.CreateCommand(); v. cmd.CommandType = CommandType.Text; vi. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula FROM tblSostanzaChimica";
vii. cmd.ExecuteNonQuery(); viii. DataTable dt = new DataTable();
ix. SqlDataAdapter da = new SqlDataAdapter(cmd); x. da.Fill(dt); xi. dataGridView1.DataSource = dt;
b. }
La riga n.1 indica la stringa di connessione che in C# và indicata così ed è memorizzata nella
variabile con che è una variabile di tipo SQLConnection. Questa variabile viene aperta nella riga
3.a.i. e poi verrà ovviamente chiusa. La riga n.1.a e 1.b indicano invece 2 variabili booleane che
vengono settati così perché il loro settaggio indica se l’interfaccia deve cambiare la lingua d’uso (
da italiano a inglese e viceversa). La riga n.2 indica l’inizializzazione del Form che è standard. La
riga n.3 invece rappresenta l’evento Caricamento del Form. Dove viene aperta la connessione col
DB con con.Open e dove viene inizializzato il contenuto del primo datagridview, quello che mostra
in griglia i dati della Sostanza Chimica. Nella riga 3.a.ii. invece il bottone buttonRicerca viene
settato a false e viene cambiato in true quando viene selezionato un corretto criterio di ricerca
tramite i checkbox.
63
1. private void buttonRicerca_Click(object sender, EventArgs e) a. {
2. if (checkBox1.Checked==true && checkBox2.Checked==false && checkBox3.Checked==false) i. { ii. //ricerca per nome iii. SqlCommand cmd = con.CreateCommand(); iv. cmd.CommandType = CommandType.Text; v. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula FROM tblSostanzaChimica WHERE tblSostanzaChimica.NomeSostanza LIKE '%" + textBoxRicFormula.Text + "%'";
vi. cmd.ExecuteNonQuery(); vii. DataTable dt = new DataTable();
viii. SqlDataAdapter da = new SqlDataAdapter(cmd); ix. da.Fill(dt); x. dataGridView1.DataSource = dt; xi. }
3. else if(checkBox1.Checked==false && checkBox2.Checked==true && checkBox3.Checked==false)
i. { ii. //ricerca per CAS iii. SqlCommand cmd = con.CreateCommand(); iv. cmd.CommandType = CommandType.Text; v. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula FROM tblSostanzaChimica WHERE tblSostanzaChimica.CAS LIKE '%" + textBoxRicFormula.Text + "%'";
vi. cmd.ExecuteNonQuery(); vii. DataTable dt = new DataTable();
viii. SqlDataAdapter da = new SqlDataAdapter(cmd); ix. da.Fill(dt); x. dataGridView1.DataSource = dt; xi. }
4. else if(checkBox1.Checked==false && checkBox2.Checked==false && checkBox3.Checked==true)
i. { ii. //ricerca per formula iii. SqlCommand cmd = con.CreateCommand(); iv. cmd.CommandType = CommandType.Text; v. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula FROM tblSostanzaChimica WHERE tblSostanzaChimica.Formula LIKE '%" + textBoxRicFormula.Text + "%'";
vi. cmd.ExecuteNonQuery(); vii. DataTable dt = new DataTable();
viii. SqlDataAdapter da = new SqlDataAdapter(cmd); ix. da.Fill(dt); x. dataGridView1.DataSource = dt; xi. } xii. else MessageBox.Show("Errore criterio di ricerca");
b. }
Questo codice serve pe la Ricerca della Sostanza in base ai criteri di Ricerca selezionati. I criteri di
Ricerca sono rappresentati dai checkbox. L’evento click del bottone buttonRicerca racchiude infatti
4 possibilità :
Ricerca in base al Nome della Sostanza ( riga numero 2 )
Ricerca in base al CAS della Sostanza(riga numero 3)
64
Ricerca in base alla Formula chimica della Sostanza(riga numero 4)
Errore nel criterio di Ricerca (riga numero 4.x.ii)
Tutte e 3 hanno codice molto simile. Prima si crea l’oggetto SqlComand e poi gli viene dato il testo
della Query SQL che esegue sul DB. Poi la esegue. Adatto il risultato al datagridview.
Un altro evento che si esegue con frequenza e lo scorrere della lista nel datagridview. Questo
evento ha il seguente codice :
1. private void dataGridView1_MouseClick(object sender, MouseEventArgs e) a. {
i. textBoxCAS.Text = dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
ii. textBoxNome.Text = dataGridView1.SelectedRows[0].Cells[1].Value.ToString();
iii. textBoxFormula.Text = dataGridView1.SelectedRows[0].Cells[2].Value.ToString();
iv. button2.Enabled = true;
b. }
2. private void dataGridView1_KeyDown(object sender, KeyEventArgs e) a. {…}
3. private void dataGridView1_KeyUp(object sender, KeyEventArgs e)
a. {…}
Intanto dalla Figura 47 si può notare che per una migliore visualizzazione è stato aggiunto un
GroupBox con varie Label che mostrano il contenuto delle informazioni del datagridview. Questo
viene eseguito nella riga 1,2 o 3. L’evento che provoca tutto ciò è il click sul datagridview col
mouse. La riga 2 e 3 contiene lo stesso codice, soltanto che avviene quando viene premuto il tasto
Freccia Giù\Freccia Sù dalla tastiera.
65
Figura 48 : Scheda di LogIn
Questa invece è la schermata di LogIn per autenticarsi. Ci sono 2 textbox e un bottone.
L’evento click sul bottone ha il seguente codice C#
1. private void buttonLogIn_Click(object sender, EventArgs e) a. {
2. if (textBoxUsername.Text == "" || textBoxPassword.Text == "") 3. {
i. MessageBox.Show("Non hai inserito Username o Password,riprova"); 4. }
i. //controllo utente se admin o user
5. else { 6. SqlCommand comando = new SqlCommand("SELECT * FROM tblUtente WHERE Username ='" +
textBoxUsername.Text + "' and Password = '" + textBoxPassword.Text + "'and IDAbilitazione= 2", con);
i. SqlDataReader re = comando.ExecuteReader(); ii. bool leggo = re.Read();
7. SqlCommand comando1 = new SqlCommand("SELECT * FROM tblUtente WHERE Username ='" + textBoxUsername.Text + "' and Password = '" + textBoxPassword.Text + "'and IDAbilitazione= 1", con);
i. SqlDataReader re1 = comando1.ExecuteReader(); ii. bool leggo1 = re1.Read();
8. SqlCommand comando2 = new SqlCommand("SELECT * FROM tblUtente WHERE Username ='" + textBoxUsername.Text + "' and Password = '" + textBoxPassword.Text + "'and IDAbilitazione= 3", con);
i. SqlDataReader re2 = comando2.ExecuteReader(); ii. bool leggo2 = re2.Read();
66
iii. if (leggo && !leggo1 && !leggo2) iv. {
1. //caso user 2. this.Hide(); 3. Form2 f2 = new Form2(); 4. f2.Usr = textBoxUsername.Text; 5. f2.utente = true; 6. f2.ShowDialog();
v. } vi. if (!leggo && leggo1 && !leggo2) vii. {
1. //caso admin 2. this.Hide(); 3. Form2 f2 = new Form2(); 4. f2.Usr = textBoxUsername.Text; 5. f2.utente = false; 6. f2.ShowDialog();
viii. } ix. else { x. if (contaITA == true) xi. {
1. MessageBox.Show("Credenziali sbagliate, Riprova");
xii. } xiii. else xiv. {
1. MessageBox.Show("Credentials are wrong, try again");
xv. } xvi. }
xvii. } b. }
L’evento click del bottone è soggetto ad un controllo che è il seguente : se le textbox sono vuote
allora mostra un messaggio d’errore. Se non sono vuote passa al controllo successivo che è quello
di verificare se l’utente ha un account admin o user. Questo controllo la fanno le righe 6,7 e 8.
Viene definito un oggetto SqlComand comando nel quale scrivo la Query in Sql più gli devo dare
l’oggetto che contiene la stringa di connessione, in questo caso con definito nel evento
caricamento del Fom. Poi definisco un oggetto DataReader che esegue il comando definito prima.
Poi definisco una variabile booleana leggo nella quale memorizzo il risultato di Re.read che tornerà
un valore booleano true se trova il username e password nel DB ,false in caso contrario. Nelle
righe 8.iii e 8.vi viene controllato il valore di queste variabili booleane per capire di quale utente si
tratta. L’ultimo controllo invece è quello relativo alla lingua d’uso. Infatti già qui nel Form d’inizio
possiamo cambiare la lingua d’uso. Questo controllo lo fanno e le variabili booleane contaITA e
contaEN ed avviene nella riga 8.x e 8.xiii.
Inoltre nelle righe 8.iii e 8.vi oltre al controllo di quale utente si tratta, viene caricato il Form2 con
una proprietà che in realtà è una funzione booleana definita all’interno del Form2 che serve per
capire, in base all’utente quali schede caricare nel Form successivo.
Un evento molto frequente è anche il cambio di lingua, che come già anticipato, può avvenire in
qualsiasi momento. Il cambio di lingua è stato progettato come nel menù classico a
67
tendina(Modifica->Lingua->Italiano;English). Qui c’è anche il bottone per uscire dal
programma(File->Esci) e il bottone ?->Informazioni.
Figura 49 : Form d’Ingresso
Il codice che permette tutto ciò è il seguente :
1. private void esciToolStripMenuItem_Click(object sender, EventArgs e) a. {
i. con.Close(); ii. Environment.Exit(0);
b. }
2. private void englishToolStripMenuItem_Click(object sender, EventArgs e) a. {
i. tabPage1.Text = "Risk Security"; ii. button2.Text = "Open Data Risk-Security"; iii. checkBox1.Text = "Name"; iv. checkBox3.Text = "Chemical formula"; v. groupBox2.Text = "Selected substance"; vi. groupBox1.Text = "Search for "; vii. buttonRicerca.Text = "Search";
viii. label3.Text = "Name"; ix. groupBox3.Text = "Choice between the possible outcomes"; x. modificaToolStripMenuItem.Text = "Modification"; xi. esciToolStripMenuItem.Text = "Exit"; xii. linguaToolStripMenuItem.Text = "Language";
xiii. italianoToolStripMenuItem.Text = "Italian";
68
xiv. englishToolStripMenuItem.Text = "English"; xv. informazioniToolStripMenuItem.Text = "Information"; xvi. button1.Text = "Save in Excel";
xvii. contaEN = true; xviii. contaITA = false;
b. }
3. private void italianoToolStripMenuItem_Click(object sender, EventArgs e) a. {
i. contaITA = true; ii. contaEN = false; iii. checkBox1.Text = "Nome"; iv. checkBox3.Text = "Formula Chimica"; v. button2.Text = "Apri scheda Rischio-Sicurezza"; vi. tabPage1.Text = "Rischio Sicurezza"; vii. groupBox2.Text = "Sostanza selezionata";
viii. groupBox1.Text = "Ricerca per "; ix. buttonRicerca.Text = "Ricerca"; x. label3.Text = "Nome"; xi. groupBox3.Text = "Scelta tra i possibili risultati"; xii. modificaToolStripMenuItem.Text = "Modifica";
xiii. esciToolStripMenuItem.Text = "Esci"; xiv. linguaToolStripMenuItem.Text = "Lingua"; xv. italianoToolStripMenuItem.Text = "Italiano"; xvi. englishToolStripMenuItem.Text = "Inglese";
xvii. informazioniToolStripMenuItem.Text = "Informazioni"; xviii. button1.Text = "Salva in Excel";
b. }
La riga 1 permette la chiusura del Form e chiusura del programma. La riga n.2 e 3 invece cambiano
i valori delle label , cioè viene fatta la vera e propria traduzione in più vengono settate le variabili
booleane contaITA e contaEN necessarie per gli eventi descritti sopra.
Un’altra particolarità è il salvataggio dei dati presenti nel datagridview1 in un file Excel. Questo
evento avviene cliccando sul bottone Salva su Excel. Il codice che permette di farlo è il seguente :
1. private void button1_Click(object sender, EventArgs e) { i. saveFileDialog1.InitialDirectory = "C"; ii. saveFileDialog1.Title = "Salva su file in Excel"; iii. saveFileDialog1.FileName = ""; iv. saveFileDialog1.Filter = "Excel Files(2003)|*.xls|Excel
Files(2007)|*.xlsx";
2. if (saveFileDialog1.ShowDialog() != DialogResult.Cancel) { i. CopiaExcel copy = new CopiaExcel(); ii. copy.Show(); iii. copy.timer1.Start(); iv. Microsoft.Office.Interop.Excel.Application ExcelApp = new
Microsoft.Office.Interop.Excel.Application(); v. ExcelApp.Application.Workbooks.Add(Type.Missing);
vi. for (int i = 1; i < dataGridView1.Columns.Count + 1; i++) vii. {
1. ExcelApp.Cells[1, i] = dataGridView1.Columns[i - 1].HeaderText;
viii. } ix. for (int i = 0; i < dataGridView1.Rows.Count; i++) x. {
1. for (int j = 0; j < dataGridView1.Columns.Count; j++) 2. {
69
3. ExcelApp.Cells[i + 2, j + 1] = dataGridView1.Rows[i].Cells[j].Value.ToString();
4. } xi. } xii. ExcelApp.Columns.AutoFit();
xiii. ExcelApp.ActiveWorkbook.SaveCopyAs(saveFileDialog1.FileName.ToString());
xiv. ExcelApp.ActiveWorkbook.Saved = true; xv. ExcelApp.Quit(); xvi. }
b. }
La scrittura fondamentale avviene nella riga 1.a avviene il caricamento della finestra standard di
Salvataggio di un file in Excel, dove viene richiesto il nome del file. La formazione del vero e
proprio file avviene dalla riga 2. Vengono contate le righe e colonne del datagridview (attraverso i
cicli for) e il loro contenuto copiato nel nuovo file. Fintantochè vengono contati i cicli for viene
caricato un altro Form chiamato CopiaExcel , che è una schermata standard di caricamento di un
file, come in figura seguente.
Figura 50 : Salvataggio sul file Excel dei dati presenti in griglia
70
Le Schede di Rischio\Sicurezza venivano nel programma precedente visualizzate come singole frasi
collegate rispettivamente alla sostanza chimica selezionata. Ora invece , come espressamente
richiesto, le schede di Rischio\Sicurezza sono dei file con estensione PDF nei quali ci sono tutte le
informazioni correlate su quella sostanza. Ogni sostanza ha un file nominato in questo modo :
CAS.PDF. Il CAS è un codice univoco quindi non ci possono essere 2 sostanze con codice CAS
identico, da questo punto di vista è simile ad una chiave primaria. Essendo quindi gestito così, il
bottone “Apri Scheda Rischio\Sicurezza” ha il seguente codice :
1. private void button2_Click(object sender, EventArgs e) a. {
i. string file = "C:\\Reagentario\\CAS-PDF\\" + textBoxCAS.Text + ".pdf"; ii. // iii. if (File.Exists(file)) System.Diagnostics.Process.Start(file); iv. else MessageBox.Show("Attenzione, attualmente non presente");
b. }
Quindi in base alla sostanza chimica che in quel momento è selezionata , premendo sul bottone
appare il file PDF della sostanza chimica corrispondente. Da questo punto in avanti l’utente può ad
esempio stampare tutte le informazioni (con le relative Frasi di Rischio\Sciurezza e molto
altro…)sulla sostanza chimica selezionata.
Scheda Consumo\Spostamento
Figura 51 : Scheda di Consumo
71
La figura 51 mostra la schermata inziale della scheda di Consumo. L’utente che si è autenticato ha
un account User cioè può fare solo Consumo e\o Spostamento, consultare la Reportistica e
nient’altro.
Il codice per la visualizzazione nella griglia principale è lo stesso, della Scheda R\S vista prima (cioè
si visualizzano tutte le possibili sostanze chimiche presenti nel DB), quello che cambia è la
visualizzazione della griglia sottostante. Qui vengono infatti visualizzati i contenitori e il loro
relativo contenuto, con tutte le informazioni. Il codice che permette ciò è il seguente :
1. public void dataGridView4_MouseClick(object sender, MouseEventArgs e) 2. {
a. textBoxPCAS.Text = dataGridView4.SelectedRows[0].Cells[0].Value.ToString(); b. textBoxPID.Text = Convert.ToString(leggiIDSostanzaChimica(textBoxPCAS.Text)); c. textBoxPNome.Text = dataGridView4.SelectedRows[0].Cells[1].Value.ToString(); d. textBoxPFormula.Text =
dataGridView4.SelectedRows[0].Cells[2].Value.ToString(); i. con.Open(); ii. SqlCommand cmd = con.CreateCommand(); iii. cmd.CommandType = CommandType.Text;
1. cmd.CommandText = "SELECT tblReagenteDF.IDreagente, tblSostanzaChimica.CAS, tblReagenteDF.DataAcquisto, tblReagenteDF.DataScadenza, tblReagenteDF.Capacità, tblReagenteDV.QuantitàResidua, tblReagenteDV.Acquirente, tblLocazione.NomeLocazione, tblUnitàDiMisura.Misura FROM tblReagenteDF INNER JOIN tblSostanzaChimica ON tblSostanzaChimica.IDSostanzaChimica = tblReagenteDF.IDSostanzaChimica INNER JOIN tblReagenteDV ON tblReagenteDF.IDreagente = tblReagenteDV.IDReagente INNER JOIN tblUnitàDiMisura ON tblReagenteDF.IDMisura = tblUnitàDiMisura.IDMisura INNER JOIN tblLocazione ON tblReagenteDV.IDLocazione = tblLocazione.IDLocazione WHERE tblReagenteDF.IDSostanzaChimica ='" + textBoxPID.Text + "'";
iv. cmd.ExecuteNonQuery(); v. con.Close(); vi. DataTable dt = new DataTable(); vii. SqlDataAdapter da = new SqlDataAdapter(cmd);
viii. da.Fill(dt); ix. dataGridView5.DataSource = dt; x. dataGridView5.Columns[0].Visible = false;
1. if (dataGridView5.Rows.Count == 0) 2. { 3. DataTable dt5 = new DataTable(); 4. dataGridView5.DataSource = dt5; 5. buttonPrelievoReagente.Enabled = false; 6. } 7. else {
8. quantità =
dataGridView5.SelectedRows[0].Cells[5].Value.ToString(); 9. quantità1 = Convert.ToDouble(quantità); 10. if (quantità1 > 0) buttonPrelievoReagente.Enabled = true; 11. else buttonPrelievoReagente.Enabled = false; 12. }
xi. f = e; 3. }
4. public void dataGridView4_KeyUp(object sender, KeyEventArgs e) {…}
5. public void dataGridView4_KeyDown(object sender, KeyEventArgs e) {…}
72
Oltre da notare la Query complessa (2.iii.1), và notato anche il controllo che viene fatto sulla
quantità (righe 2.x1÷2.x.12).Infatti se il reagente selezionato ha quantità pari a zero (cioè boccione
vuoto) allora non si può eseguire nessun consumo, ed il relativo bottone che permette di fare ciò
ha la proprietà Enabled settato a false.
Da notare è la funzione leggiIDSostanzaChimica che dando come parametro d’ingresso una stringa
(il CAS in questo caso) ritorna come valore ID corrispondente al CAS. Il codice è il seguente :
1. public int leggiIDSostanzaChimica(string CAS) a. {
i. int ID = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblSostanzaChimica WHERE
tblSostanzaChimica.CAS = '" + CAS + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) ID = (int)leggi["IDSostanzaChimica"]; vii. con.Close();
viii. return ID;
b. }
Il bottone blu in alto a destra del datagridview invece serve da refresh dei dati. Il codice è :
1. con.Open(); i. SqlCommand cmd = con.CreateCommand(); ii. cmd.CommandType = CommandType.Text; iii. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula FROM tblSostanzaChimica";
iv. cmd.ExecuteNonQuery(); v. DataTable dt = new DataTable(); vi. SqlDataAdapter da = new SqlDataAdapter(cmd); vii. da.Fill(dt);
viii. dataGridView1.DataSource = dt;
2. con.Close(); Viene aperta la connessione con DB, creo un oggetto SqlComand cmd nel quale scrivo la Query in Sql, la adatto e la visualizzo nel datagridview corrispondente. Il codice evento del bottone PrelievoReagente è il seguente :
1. private void buttonPrelievoReagente_Click(object sender, EventArgs e) a. {
i. Form3 f3 = new Form3(); ii. f3.portoID = leggiIDSostanzaChimica(textBoxPCAS.Text); iii. f3.portoCAS = textBoxPCAS.Text; iv. f3.portoNome = textBoxPNome.Text; v. f3.portoMassa = textBoxPMassa.Text; vi. f3.portoFormula = textBoxPFormula.Text; vii. f3.portoIDR = dataGridView5.SelectedRows[0].Cells[0].Value.ToString();
viii. f3.portoCap = dataGridView5.SelectedRows[0].Cells[4].Value.ToString(); ix. f3.portoQuantitàR =
dataGridView5.SelectedRows[0].Cells[5].Value.ToString(); x. f3.portoLocazione =
dataGridView5.SelectedRows[0].Cells[7].Value.ToString();
73
xi. f3.portoDataAcquisto = dataGridView5.SelectedRows[0].Cells[2].Value.ToString();
xii. f3.portoDataScadenza = dataGridView5.SelectedRows[0].Cells[3].Value.ToString();
xiii. f3.portoUnità = dataGridView5.SelectedRows[0].Cells[8].Value.ToString();
xiv. f3.Acquirente = dataGridView5.SelectedRows[0].Cells[6].Value.ToString();
xv. f3.portoUsr = provUsr; xvi. f3.criga =
dataGridView4.Rows.GetFirstRow(DataGridViewElementStates.Selected); xvii. f3.f2 = this; xviii. f3.Show();
b. }
Questo codice è legato alle funzioni che sono state scritte appositamente nel Form3(quello del
Consumo). Ogni volta che si preme il bottone e che si apre il Form3 le informazioni vengono
spedite al Form del consumo grazie alle seguenti funzioni :
private string tempCAS = string.Empty; private string tempNome = string.Empty; private string tempFormula = string.Empty; private string tempIDR = string.Empty; private string tempCap = string.Empty; private string tempQuantitàR = string.Empty; private string tempLocazione = string.Empty; private string tempDataAcquisto = string.Empty; private string tempDataScadenza = string.Empty; private string tempQuantitàP = string.Empty; private string tempUnità = string.Empty; private string tempUsr = string.Empty; private string Acq = string.Empty; private int riga = 0; private int tempID = 0;
public int criga { get { return riga; } set { riga = value; } } public int portoID { get { return tempID; } set{tempID = value;} } public string portoCAS { get { return tempCAS; } set { tempCAS = value; } } public string portoNome { get { return tempNome; } set { tempNome = value; } } public string portoFormula { get { return tempFormula; } set { tempFormula = value; } } public string portoIDR {
74
get { return tempIDR; } set { tempIDR = value; } } public string portoCap { get { return tempCap; } set { tempCap = value; } } public string portoQuantitàR { get { return tempQuantitàR; } set { tempQuantitàR = value; } } public string portoLocazione { get { return tempLocazione; } set { tempLocazione = value; } } public string portoDataAcquisto { get { return tempDataAcquisto; } set { tempDataAcquisto = value; } } public string portoDataScadenza { get { return tempDataScadenza; } set { tempDataScadenza = value; } } public string portoQuantitàP { get { return tempQuantitàP; } set { tempQuantitàP = value; } } public string portoUnità { get { return tempUnità; } set { tempUnità = value; } } public string portoUsr { get { return tempUsr; } set { tempUsr = value; } } public string Acquirente { get { return Acq; } set { Acq = value; } }
Una volta che si preme il bottone del prelievo compare la seguente schermata :
75
Figura 52 : Form Consumo Reagente
Questo Form esegue il consumo vero e proprio. Intanto tutte le label che non si possono
modificare prendono le informazioni dal Form precedente, più precisamente dai datagridview
selezionati.
Le informazioni variabili, cioè quelle che l’utente può modificare è la Quantità prelevata. Come si
vede è stata introdotta la possibilità di consumo totale della quantità e del consumo parziale. Per i
numeri decimali è stato usato il punto “.” come separatore.
Il codice che permette di avere solo numeri decimali col punto “.” come separatore è il seguente :
i. private void textBoxQuantitàpre_KeyPress(object sender, KeyPressEventArgs e) a. {
i. char ch = e.KeyChar;
ii. if (ch == 46 && textBoxQuantitàpre.Text.IndexOf('.') != -1) { iii. e.Handled = true; iv. return; v. } vi. if(!Char.IsDigit(ch) && ch!= 8 && ch!=46){ vii. e.Handled = true;
viii. }
b. }
In pratica questo codice permette di inserire solo i numeri da tastiera(se si premono i tasti delle
lettere non li visualizza), anche numeri decimali ma col punto come separatore (se si preme la
virgola non la visualizza come gli altri caratteri che non siano numeri).
76
A parte il Bottone Annulla di funzionamento ovvio, chi desta senz’latro interesse è il bottone
Registra. Il codice che ne descrive il funzionamento è il seguente :
1. private void buttonReg_Click(object sender, EventArgs e) a. {
i. int IDR = Int32.Parse(textBoxIDR.Text); ii. string Usr = tempUsr; iii. double ris;
iv. double IDRF = Convert.ToDouble(textBoxQuantitàR.Text);//quantità
residua v. if (textBoxQuantitàpre.Enabled == true && ( textBoxQuantitàpre.Text ==
"" || textBoxQuantitàpre.Text == "0")) MessageBox.Show("Errore nella quantità prelevata,ricontrolla");
vi. else vii. {
viii. if (checkBox1.Checked == true && checkBox2.Checked == false) ix. {//prelevo tutto
1. SqlCommand update = new SqlCommand(); 2. update.CommandType = CommandType.Text; 3. update.CommandText = "UPDATE tblReagenteDV SET
[QuantitàResidua]=@QR, [IDStato]=@IDS WHERE IDReagente = '" + textBoxIDR.Text + "'";
4. //update.Parameters.AddWithValue("@QuantitàResidua", somma); 5. update.Parameters.AddWithValue("@QR", 0); 6. update.Parameters.AddWithValue("@IDS", 3); 7. update.Connection = con; 8. con.Open(); 9. update.ExecuteNonQuery(); 10. con.Close();
x. } xi. else if (checkBox1.Checked == false && checkBox2.Checked == true) xii. {
1. NumberFormatInfo provider = new NumberFormatInfo(); 2. provider.NumberDecimalSeparator = "."; 3. ris = Convert.ToDouble(textBoxQuantitàpre.Text,
provider);//quantità prelevata col punto dalla textbox 4. if (ris > IDRF) MessageBox.Show("Non puoi prelevare più di
quanto c'è, controlla la quantità prelevata"); 5. else
a. { b. double nd1 = Math.Round(IDRF - ris, 2); c. SqlCommand update = new SqlCommand(); d. update.CommandType = CommandType.Text; e. update.CommandText = "UPDATE tblReagenteDV SET
[QuantitàResidua]=@QR, [IDStato]=@IDS WHERE IDReagente = '" + textBoxIDR.Text + "'";
f. update.Parameters.AddWithValue("@QR", nd1); g. update.Parameters.AddWithValue("@IDS", 1); h. update.Connection = con; i. con.Open(); j. update.ExecuteNonQuery(); k. con.Close();
l. }
6. }
7. f2.dataGridView4.Rows[riga].Selected = true; 8. f2.dataGridView4_MouseClick(f2.dataGridView4, f2.f); 9. //aggiorna la tblOperazione
77
10. AggiornaOp(); 11. DataTable dt = new DataTable(); 12. f2.dataGridView6.ClearSelection(); 13. f2.dataGridView7.DataSource = dt; 14. f2.dataGridView8.ClearSelection(); 15. f2.dataGridView9.DataSource = dt; 16. f2.buttonDatiReso.Enabled = false; 17. f2.bottoni();
xiii. MessageBox.Show("Consumo registrato con successo"); xiv. Close(); xv. }
b. }
2. private void AggiornaOp() {
i. string query = "INSERT INTO tblOperazione (Operazione, Username, Data, Locazionepassata, IDreagente) VALUES (@Op, @User, @Data, @Locpas, @IDR)";
ii. SqlCommand insert = new SqlCommand(query, con); iii. insert.Parameters.AddWithValue("@Op", "Consumo"); iv. insert.Parameters.AddWithValue("@User", tempUsr); v. insert.Parameters.AddWithValue("@Data", DateTime.Now.Date); vi. insert.Parameters.AddWithValue("@Locpas", DBNull.Value); vii. insert.Parameters.AddWithValue("@IDR", tempIDR);
viii. con.Open(); ix. insert.ExecuteNonQuery(); x. con.Close();
b. }
Come prima cosa vengono inizializzate le variabili. Ci sono 2 checkbox, 1 che indica il prelievo
totale e una che indica il prelievo parziale. Nel caso di prelievo totale (1.a.viii÷1.a.x) la quantità
residua viene posto a 0, e lo stato del reagente viene posto a 3 (che rappresenta a chiave primaria
dello stato “Esaurito”). Quando invece eseguiamo un prelievo parziale (cioè diamo un input da
tastiera sulla quantità da prelevare) allora per prima cosa alla riga 1.a.v viene eseguito un test (per
non inserire quantità che non hanno senso come il campi vuoto oppure 0) e poi dalla riga 1.a.xi
viene eseguito il prelievo parziale. Nel caso si inserisca una quantità maggiore di quella disponibile
compare un messaggio d’errore. Poi nelle righe successive viene fatto il calcolo di quello che
rimane nel boccione una volta prelevata la quantità (riga 1.a.xii.4), e poi viene aggiornata la
quantità nella tabella del reagente, e viene settato a 1 lo stato del reagente che vuol dire
“Disponibile” (questa particolarità si poteva anche omettere in quanto la sostanza se viene
eseguito un prelievo parziale rimane comunque ancora disponibile).
Fatto questo le righe successive 1.a.xii.7÷1.a.xii.17 servono per fare un reset di tutte le griglie che
si trovano nel form principale. Ciò è importante per veder in modo automatico i cambiamenti
effettuati dopo un consumo.
Nella riga n.2 invece viene eseguita una procedura che è standard in questo programma. Dopo
ogni consumo o prelievo o modifica o inserimento di un reagente questa procedura viene eseguita
in quanto è quella che scrive nella tabella tblOperazione i dati necessari per la Reportistica. I dati
fondamentali sono :
Il tipo di Operazione (in questo caso consumo)
78
L’utente che esegue questa operazione (la sua chiave primaria Username)
La data di esecuzione dell’operazione
Una locazione passata (campo non obligatorio in questo caso ma fondamentale nel
operazione di spostamento)
La chiave primaria del reagente coinvolto
Il form relativo allo Spostamento invece compare così:
Figura 53 : Scheda di Spostamento
Questa schermata server per lo Spostamento della sostanza. In questa scheda la locazione
l’informazione fondamentale è la locazione. Il codice per la visualizzazione è lo stesso del form
Consumo. L’unica cosa sono le informazioni quelle che cambiano. Nella griglia sottostante a quella
principale le informazioni che compaiono sono infatti il CAS, la capacità del boccione, la quantità
residua, la misura, la locazione corrente e nome e cognome del utente che lo ha inserito.
Il bottone Sposta Reagente Selezionato invece apre il Form come in figura :
79
Figura 54 : Form Spostamento
Tutti i dati presenti nelle varie label a sinistra sono non modificabili dall’utente, in quanto presi dal
Form precedente, col meccanismo delle funzioni, già enunciato prima. L’unico dato modificabile è
la Nuova Destinazione. Qui abbiamo una combobox dove si seleziona la nuova locazione. Questa
combobox viene caricata con i dati presenti nella tblLocazione. Il codice di carimento della
combobox è il seguente (codice tratto dalla procedura di Load del Form) :
i. con.Open(); ii. SqlDataReader leggi = null; iii. SqlCommand cmd = new SqlCommand("SELECT * FROM tblLocazione", con); iv. leggi = cmd.ExecuteReader(); v. while (leggi.Read()) comboBoxDest.Items.Add(leggi["NomeLocazione"]); vi. comboBoxDest.SelectedText = textBoxLocazioneARR.Text; vii. con.Close();
Il bottone Registra all’inizio è settato a false. Infatti c’è un codice di controllo sulla combobox. Se
infatti si cambia la locazione allora il bottone viene settato a true, altrimenti rimane a false. Il
codice che permette di fare ciò è il seguente :
1. private void comboBoxDest_SelectedIndexChanged(object sender, EventArgs e) a. {
i. buttonRR.Enabled = true;
b. }
Il bottone Registra invece ha il seguente codice :
80
1. private void buttonRR_Click(object sender, EventArgs e) 2. {
i. SqlCommand update = new SqlCommand(); ii. update.CommandType = CommandType.Text; iii. update.CommandText = "UPDATE tblReagenteDV SET [IDLocazione]=@IDLoc
WHERE IDReagente = '" + tempID + "'";
iv. update.Parameters.AddWithValue("@IDLoc", Locazione(comboBoxDest.Text)); v. update.Connection = con; vi. con.Open(); vii. update.ExecuteNonQuery();
viii. con.Close(); ix. MessageBox.Show("Locazione cambiata con successo"); x. //aggiornare la tblOperazione xi. AggiornaOp(); xii. f2.dataGridView6.Rows[riga].Selected = true;
xiii. f2.dataGridView6_MouseClick(f2.dataGridView6, f2.k); xiv. DataTable dt = new DataTable(); xv. f2.dataGridView8.ClearSelection(); xvi. f2.dataGridView9.DataSource = dt;
xvii. f2.dataGridView4.ClearSelection(); xviii. f2.dataGridView5.DataSource = dt;
xix. f2.buttonPrelievoReagente.Enabled = false; xx. f2.bottoni(); xxi. //f2.buttonDatiReso.Enabled = false;
xxii. Close(); 3. }
4. private int Locazione(string Loc) { 5. con.Open(); 6. int ID = 0; 7. SqlDataReader leggi = null; 8. SqlCommand cmd = new SqlCommand("SELECT * FROM tblLocazione WHERE
tblLocazione.NomeLocazione = '" + Loc + "'", con); 9. leggi = cmd.ExecuteReader(); 10. while (leggi.Read()) ID = (int)leggi["IDLocazione"]; 11. con.Close(); 12. return ID;
13. }
Questo codice esegue lo Spostamento. Lo spostamento però, per il DB, non è nient’altro che un
update della tabella tblLocazione. Nella variabile tempID c’è l’ID del reagente coninvolto. Viene
cambiata l’ID della tblLocazione che è chiave esterna. Per fare questo viene chiamata una funzione
Locazione che data una stringa come parametro d’ingresso (la stringa che compare nella
combobox ) restituisce la relativa chiave primaria della tabella tblLocazione (riga 2.iv).
L’ultima scheda è quella di LogOut che è quella dove ci riporta al Form d’Inizio.
81
Figura 55 : Scheda di LogOut
Scheda di Gestione reagenti
La scheda Gestione reagenti è raggiugibile soltanto da un utente di tipo administrator. Si presenta
così :
82
Figura 56 : Scheda Gestione reagenti
Da notare è la griglia inferiore. Qui infatti vengono visualizzati tutti i reagenti di quella particolare
sostanza chimica. Il codice che ne permette la visualizzazione è il seguente :
1. public void dataGridView8_MouseClick(object sender, MouseEventArgs e) a. {
i. textBox4.Text = dataGridView8.SelectedRows[0].Cells[0].Value.ToString();
ii. textBox5.Text = Convert.ToString(leggiIDSostanzaChimica(textBox4.Text));
iii. textBox3.Text = dataGridView8.SelectedRows[0].Cells[1].Value.ToString();
iv. textBox1.Text = dataGridView8.SelectedRows[0].Cells[2].Value.ToString();
v. button4.Enabled = true; vi. con.Open(); vii. SqlCommand cmd = con.CreateCommand();
viii. cmd.CommandType = CommandType.Text; ix. cmd.CommandText = "SELECT tblReagenteDF.IDreagente,
tblSostanzaChimica.CAS, tblSostanzaChimica.NomeSostanza, tblReagenteDF.Progressivo, tblStato.Stato, tblReagenteDV.QuantitàResidua, tblReagenteDV.Acquirente, tblReagenteDF.Capacità, tblUnitàDiMisura.Misura, tblLocazione.NomeLocazione, tblReagenteDF.DataAcquisto, tblReagenteDF.DataScadenza, tblFornitore.NomeFornitore, tblUtente.CognomeUtente,tblUtente.NomeUtente, tblReagenteDF.Username FROM tblReagenteDF INNER JOIN tblReagenteDV ON tblReagenteDF.IDreagente = tblReagenteDV.IDReagente INNER JOIN tblSostanzaChimica ON tblReagenteDF.IDSostanzaChimica = tblSostanzaChimica.IDSostanzaChimica INNER JOIN tblLocazione ON tblReagenteDV.IDLocazione =
83
tblLocazione.IDLocazione INNER JOIN tblUnitàDiMisura ON tblReagenteDF.IDMisura = tblUnitàDiMisura.IDMisura INNER JOIN tblFornitore ON tblReagenteDF.IDFornitore = tblFornitore.IDFornitore INNER JOIN tblUtente ON tblReagenteDF.Username = tblUtente.Username INNER JOIN tblStato ON tblReagenteDV.IDStato = tblStato.IDStato WHERE tblReagenteDF.IDSostanzaChimica = '" + textBox5.Text + "'";
x. cmd.ExecuteNonQuery(); xi. con.Close(); xii. DataTable dt = new DataTable();
xiii. SqlDataAdapter da = new SqlDataAdapter(cmd); xiv. da.Fill(dt); xv. dataGridView9.DataSource = dt; xvi. dataGridView9.Columns[0].Visible = false;
xvii. if (dataGridView9.Rows.Count == 0) button2.Enabled = button3.Enabled =
button5.Enabled = false; xviii. else button2.Enabled = button3.Enabled = button5.Enabled = true;
xix. g = e;
b. } 2. private void dataGridView8_KeyDown(object sender, KeyEventArgs e) {…}
3. private void dataGridView8_KeyUp(object sender, KeyEventArgs e) 4. {…}
Di interesse sono i bottoni sottostanti. In particolare i bottoni Aggiungi,Modifica ed Elimina.
Il bottone Modifica ape il seguente Form :
Figura 57 : Form Modifica Reagente
84
Qui in questo form vengono riportati i dati del reagente selezionato nel form precedente. Per
trasportare i dati si usa come sempre il meccanismo setter-getter. Si possono cambiare la Data
Acquisto, Capacità, Unità di Misura, Fornitore, Locazione, Disponibilità (Username compare ma è
non modificabile). Fornitore e Locazione sono combobox. Il codice del bottone Modifica è il
seguente :
1. private void button2_Click(object sender, EventArgs e) a. {
i. NumberFormatInfo provider1 = new NumberFormatInfo(); ii. provider1.NumberDecimalSeparator = "."; iii. double C = Double.Parse(textBox8.Text,provider1);//disponibilità iv. double C1 = Double.Parse (textBox4.Text,provider1);//capacità v. double C2; vi. if (textBox3.Text == "") C2 = 0; vii. else C2 = Double.Parse(textBox3.Text,provider1);
viii. if (textBox2.Text == "" || textBox2.Text == "0" || textBox4.Text == "" || textBox8.Text == "" || comboBox1.Text == "" || comboBox2.Text == "" || comboBox4.Text == "") MessageBox.Show("Errore, hai lasciato campi vuoti");
ix. else if (C+C2 > C1) MessageBox.Show("Errore controlla le quantità"); x. else
1. { xi. updateReagenteDV(); xii. updateReagenteDF();
xiii. AggiornaOp(); xiv. MessageBox.Show("Dati aggiornati con successo"); xv. f2.dataGridView8.Rows[riga].Selected = true; xvi. f2.dataGridView8_MouseClick(f2.dataGridView8, f2.g);
xvii. DataTable dt = new DataTable(); xviii. f2.dataGridView6.ClearSelection();
xix. f2.dataGridView7.DataSource = dt;
xx. f2.dataGridView4.ClearSelection(); xxi. f2.dataGridView5.DataSource = dt;
xxii. f2.buttonPrelievoReagente.Enabled = false; xxiii. f2.buttonDatiReso.Enabled = false; xxiv. Close();
1. } b. }
2. private void AggiornaOp() {…}
3. private void updateReagenteDV() {
i. NumberFormatInfo provider = new NumberFormatInfo(); ii. provider.NumberDecimalSeparator = "."; iii. double QR = Convert.ToDouble(textBox8.Text, provider); iv. SqlCommand update = new SqlCommand(); v. update.CommandType = CommandType.Text; vi. update.CommandText = "UPDATE tblReagenteDV SET [IDStato]=@IDS,
[QuantitàResidua]= @QuantitàResidua, [IDLocazione]=@IDLocazione, [Acquirente]= @Acq WHERE IDReagente = '" + textBox1.Text + "'";
1. if (QR > 0) update.Parameters.AddWithValue("@IDS", 1); 2. else update.Parameters.AddWithValue("@IDS", 2); 3. if (checkBox1.Checked == true) {
a. double QR1 = Convert.ToDouble(textBox3.Text, provider); b. double tot = Math.Round(QR + QR1, 2); c. update.Parameters.AddWithValue("@QuantitàResidua", tot); d. }
4. else update.Parameters.AddWithValue("@QuantitàResidua", QR);
85
5. update.Parameters.AddWithValue("@IDLocazione", leggiIDLocazione(comboBox2.Text));
6. update.Parameters.AddWithValue("@Acq", textBox2.Text); 7. update.Connection = con; 8. con.Open(); 9. update.ExecuteNonQuery(); 10. con.Close();
i. } 4. private int leggiIDLocazione(string Locazione) {
i. int IDL=0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblLocazione WHERE
tblLocazione.NomeLocazione = '"+Locazione+"'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDL = (int)leggi["IDLocazione"]; vii. con.Close();
viii. return IDL; a. }
5. private void updateReagenteDF() {
i. NumberFormatInfo provider = new NumberFormatInfo(); ii. provider.NumberDecimalSeparator = "."; iii. double QR = Convert.ToDouble(textBox4.Text, provider); iv. DateTime data1 = dateTimePicker1.Value; v. SqlCommand update1 = new SqlCommand(); vi. update1.CommandType = CommandType.Text; vii. update1.CommandText = "UPDATE tblReagenteDF SET [DataAcquisto]= @DA,
[Capacità]= @Cap, [Username]=@User, [IDFornitore]=@IDF, [IDMisura]=@IDMisura, [Progressivo]=@Prog WHERE IDreagente = '" + textBox1.Text + "'";
viii. update1.Parameters.AddWithValue("@DA", data1); ix. update1.Parameters.AddWithValue("@Cap", QR); x. update1.Parameters.AddWithValue("@User", Username); xi. update1.Parameters.AddWithValue("@IDF",
leggiIDFornitore(comboBox1.Text)); xii. update1.Parameters.AddWithValue("@IDMisura",leggiIDMisura(comboBox4.Tex
t) ); xiii. update1.Parameters.AddWithValue("@Prog", textBox5.Text); xiv. update1.Connection = con; xv. con.Open(); xvi. update1.ExecuteNonQuery();
xvii. con.Close(); i. }
6. private int leggiIDFornitore(string Fornitore) {
i. int IDF = 0; ii. con.Open(); iii. SqlDataReader leggi1 = null; iv. SqlCommand cmd1 = new SqlCommand("SELECT * FROM tblFornitore WHERE
tblFornitore.NomeFornitore = '" + Fornitore + "'", con); v. leggi1 = cmd1.ExecuteReader(); vi. while (leggi1.Read()) IDF = (int)leggi1["IDFornitore"]; vii. con.Close();
viii. return IDF; b. }
7. private int leggiIDMisura (string Misura){
i. int IDM=0; ii. con.Open(); iii. SqlDataReader leggi2 = null; iv. SqlCommand cmd2 = new SqlCommand("SELECT * FROM tblUnitàDiMisura WHERE
tblUnitàDiMisura.Misura = '" + Misura + "'", con);
86
v. leggi2 = cmd2.ExecuteReader(); vi. while (leggi2.Read()) IDM = (int)leggi2["IDMisura"]; vii. con.Close();
viii. return IDM; ix. }
Vediamo dal codice che bisogna agire su due tabelle per fare le modifiche richieste ossia
tblReagenteDF e tblReagenteDV. E infatti sono state create 2 procedure updateReagenteDF e
updateReagenteDV che eseguono l’update dei dati del reagente.
La procedura private int leggiIDLocazione(string Locazione) dà come risultato la chiave
primaria (un intero) , dando come parametro d’ingresso una stringa chiamata locazione che non è
altro che la stringa di testo selezionata nella combobox. Grazie alla stringa locazione, trova il
valore corrispondente della chiave primaria e la usa per modificare quella esterna nella
tblReagenteDV. Tutta la procedura private void updateReagenteDV() della riga n.3 è simile a tutte
le update descritte fin’ora.
Lo stesso discorso vale per la procedura private void updateReagenteDF() della riga n.5. Avendo
però qui due chiavi esterne serviranno 2 nuove procedure che come nel caso precedente hanno
dei parametri d’ingresso, grazie ad essi trovano nelle rispettive tabelle le chiavi primarie associate
a quei record e poi modificano la chiave esterna nella propria tabella(riga n.6 e 7).
Per il Fornitore c’è la possibilità di inserire immediatamente un nuovo Fornitore. Si apre il
seguente Form
Figura 58 : Scheda Nuovo Fornitore
87
Qui un l’administrator può inserire i dati del nuovo Fornitore e memorizzarli. Il codice del bottone
Registra è il seguente :
1. private void button1_Click(object sender, EventArgs e) a. {
i. if (textBox1.Text == "" || textBox2.Text == "") MessageBox.Show("Nome Fornitore e Indirizzo sono campi obbligatori");
ii. else { iii. string query = "INSERT INTO tblFornitore (NomeFornitore, Indirizzo,
Telefono, Fax, Email ) VALUES (@NF, @Ind, @Tel, @Fax, @Email)"; iv. SqlCommand insert = new SqlCommand(query, con); v. insert.Parameters.AddWithValue("@NF", textBox1.Text); vi. insert.Parameters.AddWithValue("@Ind", textBox2.Text); vii. insert.Parameters.AddWithValue("@Tel", textBox3.Text);
viii. insert.Parameters.AddWithValue("@Fax", textBox4.Text); ix. insert.Parameters.AddWithValue("@Email", textBox5.Text); x. con.Open(); xi. insert.ExecuteNonQuery(); xii. con.Close();
xiii. MessageBox.Show("Dati fornitore inseriti con successo"); xiv. Close(); xv. }
b. }
I campi Fornitore e Indirizzo sono campi obbligatori (riga 1.a.i). La procedura d’inserimento è
quella standard.
Ora vediamo il bottone Aggiungi sempre nella Scheda Gestione Reagenti. Il Form che si apre è il
seguente :
Figura 59 : Form Inserisci Reagente
88
I Campi della date sono Oggetti DateTimePicker. Data scadenza ed eliminazione non sono dati
necessari. I restanti sono tutti campi da popolare. Il bottone Registra ha il seguente codice :
1. private void button1_Click(object sender, EventArgs e) {
2. if (textBox1.Text == "" || textBox1.Text == "0" || textBox2.Text == "" || textBox3.Text == "" || comboBox1.Text == "" || comboBox2.Text == "" || comboBox4.Text == "") MessageBox.Show("Errore,ci sono campi vuoti, ricontrolla");
3. else { i. if (checkBox3.Checked == false) ii. {//inserimento singolo
1. inserisciReagenteDF(); 2. inserisciReagenteDV(); 3. AggiornaOp(); 4. MessageBox.Show("Nuovo reagente inserito con successo");
iii. } iv. else if (checkBox3.Checked == true){
1. //inserimento multiplo 2. int n = Int32.Parse( textBox5.Text); 3. for (int i = 0; i < n; i++) { 4. inserisciReagenteDF(); 5. inserisciReagenteDV(); 6. AggiornaOp(); 7. } 8. MessageBox.Show("Nuovi reagenti inseriti con successo");
v. } vi. f2.dataGridView8.Rows[riga].Selected = true; vii. f2.dataGridView8_MouseClick(f2.dataGridView8, f2.g);
viii. DataTable dt = new DataTable(); ix. f2.dataGridView6.ClearSelection(); x. f2.dataGridView7.DataSource = dt; xi. f2.dataGridView4.ClearSelection(); xii. f2.dataGridView5.DataSource = dt;
xiii. f2.buttonPrelievoReagente.Enabled = false; xiv. f2.buttonDatiReso.Enabled = false; xv. Close(); xvi. }
b. }
4. private void AggiornaOp() {…}
5. private void inserisciReagenteDF() {
i. DateTime dt1= dateTimePicker1.Value;//data acquisto ii. DateTime dt2 = dateTimePicker2.Value;//data scadenza iii. DateTime dt3 = dateTimePicker3.Value;//data eliminazione iv. NumberFormatInfo provider = new NumberFormatInfo(); v. provider.NumberDecimalSeparator = "."; vi. double cap = Convert.ToDouble(textBox1.Text, provider); vii. //mi serve l'IDFornitore
viii. if (checkBox1.Checked == false && checkBox2.Checked == true) {//ho la data scadenza ma non la eliminazione
ix. string query = "INSERT INTO tblReagenteDF (IDSostanzaChimica, DataAcquisto, DataScadenza, Capacità, IDFornitore, Username, IDMisura ) VALUES (@IDS, @DA, @DS, @Cap, @IDF, @Usr, @IDM)";
x. SqlCommand insert = new SqlCommand(query, con); xi. insert.Parameters.AddWithValue("@IDS", IDS); xii. insert.Parameters.AddWithValue("@DA", dt1);
xiii. insert.Parameters.AddWithValue("@DS", dt2); xiv. insert.Parameters.AddWithValue("@Cap", cap); xv. insert.Parameters.AddWithValue("@IDF", IDFornitore(comboBox1.Text)); xvi. insert.Parameters.AddWithValue("@Usr", textBox4.Text);
89
xvii. insert.Parameters.AddWithValue("@IDM", IDMisura(comboBox4.Text)); xviii. con.Open();
xix. insert.ExecuteNonQuery(); xx. con.Close(); xxi. }
xxii. else if (checkBox1.Checked == true && checkBox2.Checked == false) xxiii. {//ho la data di eliminazione ma non la scadenza xxiv. string query = "INSERT INTO tblReagenteDF (IDSostanzaChimica,
DataAcquisto, DataEliminazione, Capacità, IDFornitore, Username, IDMisura ) VALUES (@IDS, @DA, @DE, @Cap, @IDF, @Usr, @IDM)";
xxv. SqlCommand insert = new SqlCommand(query, con); xxvi. insert.Parameters.AddWithValue("@IDS", IDS); xxvii. insert.Parameters.AddWithValue("@DA", dt1); xxviii. insert.Parameters.AddWithValue("@DE", dt3); xxix. insert.Parameters.AddWithValue("@Cap", cap); xxx. insert.Parameters.AddWithValue("@IDF", IDFornitore(comboBox1.Text));
xxxi. insert.Parameters.AddWithValue("@Usr", textBox4.Text); xxxii. insert.Parameters.AddWithValue("@IDM", IDMisura(comboBox4.Text)); xxxiii. con.Open(); xxxiv. insert.ExecuteNonQuery(); xxxv. con.Close(); xxxvi. } xxxvii. else { // nè data scadenza nè eliminazione
1. string query = "INSERT INTO tblReagenteDF (IDSostanzaChimica, DataAcquisto, Capacità, IDFornitore, Username, IDMisura ) VALUES (@IDS, @DA, @Cap, @IDF, @Usr, @IDM)";
2. SqlCommand insert = new SqlCommand(query, con); 3. insert.Parameters.AddWithValue("@IDS", IDS); 4. insert.Parameters.AddWithValue("@DA", dt1); 5. insert.Parameters.AddWithValue("@Cap", cap); 6. insert.Parameters.AddWithValue("@IDF",
IDFornitore(comboBox1.Text)); 7. insert.Parameters.AddWithValue("@Usr", textBox4.Text); 8. insert.Parameters.AddWithValue("@IDM",
IDMisura(comboBox4.Text)); 9. con.Open(); 10. insert.ExecuteNonQuery(); 11. con.Close();
xxxviii. } b. }
6. private void inserisciReagenteDV() { i. NumberFormatInfo provider = new NumberFormatInfo(); ii. provider.NumberDecimalSeparator = "."; iii. int stato = 1; iv. double QR = Convert.ToDouble(textBox2.Text, provider); v. string query = "INSERT INTO tblReagenteDV (IDReagente, IDStato,
QuantitàResidua, IDLocazione, Acquirente) VALUES (@IDR, @IDStato, @QR, @IDL, @Acq)";
vi. SqlCommand insert1 = new SqlCommand(query, con); vii. insert1.Parameters.AddWithValue("@IDR",leggiIDReagenteDF());
viii. insert1.Parameters.AddWithValue("@IDStato", stato); ix. insert1.Parameters.AddWithValue("@QR", QR); x. insert1.Parameters.AddWithValue("@IDL", IDLocazione(comboBox2.Text)); xi. insert1.Parameters.AddWithValue("@Acq", textBox3.Text); xii. con.Open();
xiii. insert1.ExecuteNonQuery(); xiv. con.Close();
b. }
7. private int leggiIDReagenteDF() { i. int IDR = 0; ii. con.Open(); iii. SqlDataReader leggi = null;
90
iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblReagenteDF WHERE tblReagenteDF.IDSostanzaChimica = '" + IDS + "'", con);
v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDR = (int)leggi["IDreagente"]; vii. con.Close();
viii. return IDR; b. }
8. private int IDLocazione(string Locazione) {
i. int ITL = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblLocazione WHERE
tblLocazione.NomeLocazione = '" + Locazione + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) ITL = (int)leggi["IDLocazione"]; vii. con.Close();
viii. return ITL; b. }
9. private int IDFornitore(string Fornitore) { i. int IDF = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblFornitore WHERE
tblFornitore.NomeFornitore = '" + Fornitore + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDF = (int)leggi["IDFornitore"]; vii. con.Close();
viii. return IDF; b. }
10. private int IDMisura(string Misura) { i. int IDM = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblUnitàDiMisura WHERE
tblUnitàDiMisura.Misura = '" + Misura + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDM = (int)leggi["IDMisura"]; vii. con.Close();
viii. return IDM; ix. }
Qui notiamo però le seguenti funzioni private int leggiIDReagenteDF(),private int
IDLocazione(string Locazione), private int IDFornitore(string Fornitore) e private int
IDMisura(string Misura). Infatti qui inseriamo dei nuovi dati riguardanti il contenitore e il
reagente (la vera e propria sostanza). Come già anticipato le tblReagenteDF e tblReagenteDV
hanno chiavi primaria che è anche chiave esterna. Ciò significa che quando inseriamo un nuovo
record devo inserirlo contemporaneamente anche nell’altra tabella, dove le chiavi primarie
devono coincidere.
La funzione leggiIDReagenteDF restituisce la chiave primaria della tblReagenteDF in base alla
chiave esterna della sostanza chimica. Le altre funzioni sono di ovvio significato : devono in
base alla stringa data recuperare il valore della corrispondente chiave primaria.
Quando invece selezioniamo un reagente e premiamo il bottone Elimina allora quello che
accade e che compare un nuovo Form, più piccolo, che chiede la conferma dell’Eliminazione.
Se si preme il tasto “Si”allora il codice che viene eseguito è il seguente :
91
1. private void button1_Click(object sender, EventArgs e) a. {
i. // premuto il tatso SI ii. SqlCommand update = new SqlCommand(); iii. update.CommandType = CommandType.Text; iv. update.CommandText = "UPDATE tblReagenteDV SET [IDStato]= @IDS,
[QuantitàResidua]=@QS WHERE IDReagente = '" + ID + "'"; v. update.Parameters.AddWithValue("@IDS", 2); vi. update.Parameters.AddWithValue("@QS", 0); vii. update.Connection = con;
viii. con.Open(); ix. update.ExecuteNonQuery(); x. con.Close(); xi. MessageBox.Show("Reagente eliminato con succeesso!"); xii. AggiornaOp();
xiii. f2.dataGridView8.Rows[riga].Selected = true; xiv. f2.dataGridView8_MouseClick(f2.dataGridView8, f2.g); xv. DataTable dt = new DataTable(); xvi. f2.dataGridView6.ClearSelection();
xvii. f2.dataGridView7.DataSource = dt; xviii. f2.dataGridView4.ClearSelection();
xix. f2.dataGridView5.DataSource = dt; xx. f2.buttonPrelievoReagente.Enabled = false; xxi. f2.buttonDatiReso.Enabled = false;
xxii. Close(); b. }
private void AggiornaOp() {…}
L’eliminazione di una sostanza equivale da un’ Update nella tabella tblReagenteDV dove lo stato
della sostanza viene modificato e portato a 2 (chiave esterna che indica nella tblStato lo stato
Eliminato).
Gestione Sostanza Chimica
Figura 60 : Scheda Gestione Sostanza Chimica
92
In questa scheda oltre che alla classica Ricerca della sostanza e del relativo salvataggio di dati in
Excel, possiamo Gestire i Sinonimi nonché Gestire le Sostanze vere e proprie. Questa parte è
raggiungibile solo dal administrator.
Premendo il bottone Inserisci sotto il primo datagridview si apre il seguente Form :
Figura 61 : Form Nuova Sostanza
Ci sono 4 textbox e 1 combobox. Il bottone Registra usa il seguente codice :
1. private void button1_Click(object sender, EventArgs e) a. {
i. if (textBox1.Text == "" || textBox2.Text == "" || textBox4.Text == "") MessageBox.Show("CAS,Nome e Formula sono campi obbligatori");
ii. else { 1. NumberFormatInfo provider = new NumberFormatInfo(); 2. provider.NumberDecimalSeparator = "."; 3. double massa = Convert.ToDouble(textBox3.Text, provider); 4. string query = "INSERT INTO tblSostanzaChimica (CAS,
NomeSostanza, MassaMolecolare, Formula, IDClasse) VALUES (@CAS, @NS, @Massa, @Form, @IDC)";
5. SqlCommand insert = new SqlCommand(query, con);
6. insert.Parameters.AddWithValue("@CAS", textBox1.Text); 7. insert.Parameters.AddWithValue("@NS", textBox2.Text); 8. insert.Parameters.AddWithValue("@Massa", massa); 9. insert.Parameters.AddWithValue("@Form", textBox4.Text); 10. insert.Parameters.AddWithValue("@IDC",
leggiIDClasse(comboBox1.Text));
11. con.Open(); 12. insert.ExecuteNonQuery(); 13. con.Close(); 14. MessageBox.Show("Sostanza chimica inserita con successo");
93
15. Close(); 16. f2.caricagriglia10(); 17. f2.caricatutteg(); 18. }
b. }
2. private int leggiIDClasse(string Classe) {
i. int IDC = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblClasse WHERE
tblClasse.Classe = '" + Classe + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDC = (int)leggi["IDClasse"]; vii. con.Close();
viii. return IDC; ix. }
L’inserimento dati da oggetti SqlComand è standard. Notiamo però la funzione (riga 2) private
int leggiIDClasse(string Classe) . Questa funzione prende come parametro d’ingresso una
stringa , chiamate Classe, e ritorna la chiave primaria del record in cui quella stringa Classe è
registrata. La stringa Classe rappresenta la stringa visualizzata nella ComboBox.
Invece premendo il bottone Elimina viene eseguito il seguente codice :
1. private void button29_Click(object sender, EventArgs e) a. {
i. SqlCommand cmd = new SqlCommand(); ii. cmd.CommandType = CommandType.Text; iii. cmd.CommandText = "DELETE tblSostanzaChimica WHERE IDSostanzaChimica
='" + dataGridView10.SelectedRows[0].Cells[0].Value.ToString() + "'"; iv. cmd.Connection = con; v. con.Open(); vi. cmd.ExecuteNonQuery(); vii. con.Close();
viii. MessageBox.Show("Sostanza chimica eliminata con successo"); ix. }
In questo caso abbiamo un DELETE, ma come per l’insert e l’update la sintassi è molto simile.
Se invece si preme il bottone Aggiungi sotto il datagridview dei Sinonimi appare questo Form :
94
Figura 62 : Form Aggiungi Sinonimo
Abbiamo 1 textbox e 1 ComboBox. Inoltre abbiamo il bottone Nuova Fonte per inserire i dati
della nuova Fonte. Il Form che appare cliccando su Nuova Fonte è il seguente :
Figura 63 : Form Nuova Fonte
95
Abbiamo 2 texbox. Il bottone Registra ha il seguente codice :
2. private void button1_Click(object sender, EventArgs e) 3. { 4. reg = true; 5. string query = "INSERT INTO tblFonte (NomeFonte, urlFonte) VALUES (@NF, @url)"; 6. SqlCommand insert = new SqlCommand(query, con);
7. insert.Parameters.AddWithValue("@NF", textBox1.Text); 8. insert.Parameters.AddWithValue("@url", textBox2.Text); 9. con.Open(); 10. insert.ExecuteNonQuery(); 11. con.Close(); 12. MessageBox.Show("Nuova fonte inserita con successo");
13. Close();
14. }
Mentre il bottone Registra del Form Aggiungi Sinonimo ha il seguente codice
1. private void button2_Click(object sender, EventArgs e)//registra nuovo sinonimo a. {
i. string query = "INSERT INTO tblSinonimo (IDSostanzaChimica, Sinonimo, IDSinonimoFonte) VALUES (@IDS, @Sin, @IDSinF)";
ii. SqlCommand insert = new SqlCommand(query, con);
iii. insert.Parameters.AddWithValue("@IDS", Int32.Parse(tempID)); iv. insert.Parameters.AddWithValue("@Sin", textBox1.Text); v. insert.Parameters.AddWithValue("@IDSinF", leggiID(comboBox1.Text)); vi. con.Open(); vii. insert.ExecuteNonQuery();
viii. con.Close(); ix. MessageBox.Show("Dati di sinonimo inseriti con successo"); x. Close();
b. }
2. private int leggiID(string Fonte) {
i. int ID = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblFonte WHERE
tblFonte.NomeFonte = '" + Fonte + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) ID = (int)leggi["IDFonte"]; vii. con.Close();
viii. return ID;
b. }
Codice molto simile a quello visto fin’ora dalla spiegazione ovvia.
Il bottone Modifica , modifica il sinonimo selezionato e fa aprire il form come in figura :
96
Figura 64 : Form Modifica Sinonimo
Il bottone Modifica ha il seguente codice :
1. private void button1_Click(object sender, EventArgs e) a. {
i. if (textBox1.Text == "") MessageBox.Show("Non puoi inserire un sinonimo vuoto");
ii. else { iii. SqlCommand update = new SqlCommand(); iv. update.CommandType = CommandType.Text; v. update.CommandText = "UPDATE tblSinonimo SET [Sinonimo]=@Sin,
[IDSinonimoFonte]=@IDSF WHERE IDSinonimo = '" + leggiIDSin(tempSin) + "'";
vi. update.Parameters.AddWithValue("@Sin", textBox1.Text); vii. update.Parameters.AddWithValue("@IDSF",
leggiIDSinFont(comboBox1.Text)); viii. update.Connection = con;
ix. con.Open(); x. update.ExecuteNonQuery(); xi. con.Close(); xii. MessageBox.Show("Sinonimo modificato con successo");
xiii. f2.dataGridView10.Rows[riga].Selected = true; xiv. f2.dataGridView10_MouseClick(f2.dataGridView10, f2.l); xv. Close(); xvi. }
b. }
2. private int leggiIDSin(string Sinonimo) { i. int ID = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblSinonimo WHERE
tblSinonimo.Sinonimo = '" + Sinonimo + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) ID = (int)leggi["IDSinonimo"];
97
vii. con.Close(); viii. return ID;
b. } 3. private int leggiIDSinFont(string Fonte) {
i. int IDSF = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblFonte WHERE
tblFonte.NomeFonte = '" + Fonte + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDSF = (int)leggi["IDFonte"]; vii. con.Close();
viii. return IDSF;
b. }
Quindi nella riga viene fatto l’update della tabella tblSinonimo in base ai dati recuperati nel form.
La funzione di riga2, private int leggiIDSin(string Sinonimo) recupera ID del Sinonimo dato
come parametro d’ingresso che serve come condizione per vedere in quale record della tabella
andare ad aggiornare il dato in tabella, mentre la funzione private int leggiIDSinFont(string
Fonte) recupera l’ID della tabella tblFonte in base alla stringa data come parametro d’ingresso
(che è il testo della combobox in quel istante) necessario quando si modifica la Fonte nella
combobox. Infatti l’ID recuperato da quest’ultma funzione serve per andare ad aggiornare la
chiave esterna nella tabella tblSinonimo (riga 1.a.vii).
Invece il bottone Elimina fa comparire questa messagebox :
Figura 65 : Eliminazione Sinonimo
98
Il codice che esegue l’eliminazione è il seguente :
1. private void button18_Click(object sender, EventArgs e)
a. { i. SqlCommand cmd = new SqlCommand(); ii. cmd.CommandType = CommandType.Text; iii. cmd.CommandText = "DELETE tblSinonimo WHERE IDSinonimo ='" +
leggiIDSin(dataGridView11.SelectedRows[0].Cells[1].Value.ToString())+"'";
iv. cmd.Connection = con; v. con.Open(); vi. cmd.ExecuteNonQuery(); vii. con.Close();
viii. MessageBox.Show("Sinonimo eliminato con successo"); ix. int riga
=dataGridView10.Rows.GetFirstRow(DataGridViewElementStates.Selected); x. dataGridView10.Rows[riga].Selected = true; xi. dataGridView10_MouseClick(dataGridView10,l);
b. }
Vediamo che l’elimnazione vera e propria viene eseguita nella riga 1.a.iii nella quale viene
eliminato il record avente ID uguale a quello dato dalla funzione leggiIDSin che come parametro
d’ingresso accetta una stringa , in questo caso data dalla seconda cella del datagridview11 della
riga selezionata. Quello che segue poi è il MessageBox , come si vede in figura, e l’aggiornamento
del datagridview corrispondente.
Come si vede da tutte le figure di ogni scheda, c’è la possibilità di Ricerca di una Sostanza in base al
CAS, Nome e Formula. I checkbox sono mutualmente esclusivi. Ma in questa scheda è data la
possibilità di Ricerca anche in base al Sinonimo. Infatti può capitare spesso che si inserisca
correttamente il nome della sostanza , poi si inserisca anche qualche sinonimo. Quando si fa una
ricerca molte volte non ci si ricorda il nome della sostanza correttamente e si fa uso dei sinonimi
proprio per questo. La possibilità di ricerca per sinonimo è stata aggiunta in modo automatico
durante ad esempio la selezione del criterio di ricerca per Nome e nel caso in cui non ci siano
risultati nella griglia principale. Il codicedel bottone Ricerca in questo caso è il seguente :
1. private void button9_Click(object sender, EventArgs e)
a. { i. if (checkBox20.Checked == true && checkBox19.Checked == false &&
checkBox18.Checked == false && checkBox33.Checked ==false) ii. { iii. //ric per CAS iv. con.Open(); v. SqlCommand cmd = con.CreateCommand(); vi. cmd.CommandType = CommandType.Text; vii. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula, tblClasse.Classe FROM tblSostanzaChimica LEFT JOIN tblClasse ON tblClasse.IDClasse = tblSostanzaChimica.IDClasse WHERE tblSostanzaChimica.CAS LIKE '%" + textBox13.Text + "%'";
viii. cmd.ExecuteNonQuery();
99
ix. con.Close(); x. DataTable dt = new DataTable(); xi. SqlDataAdapter da = new SqlDataAdapter(cmd); xii. da.Fill(dt);
xiii. dataGridView10.DataSource = dt; xiv. } xv. else if (checkBox20.Checked == false && checkBox19.Checked == true &&
checkBox18.Checked == false && checkBox33.Checked == false) xvi. {//ricerca per Nome
xvii. con.Open(); xviii. SqlCommand cmd = con.CreateCommand();
xix. cmd.CommandType = CommandType.Text; xx. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula, tblClasse.Classe FROM tblSostanzaChimica LEFT JOIN tblClasse ON tblClasse.IDClasse = tblSostanzaChimica.IDClasse WHERE tblSostanzaChimica.NomeSostanza LIKE '%" + textBox13.Text + "%'";
xxi. cmd.ExecuteNonQuery(); xxii. con.Close(); xxiii. DataTable dt = new DataTable(); xxiv. SqlDataAdapter da = new SqlDataAdapter(cmd); xxv. da.Fill(dt);
xxvi. dataGridView10.DataSource = dt; xxvii. if (dataGridView10.RowCount == 0) {
1. con.Open(); 2. SqlCommand cmd1 = con.CreateCommand(); 3. cmd1.CommandType = CommandType.Text; 4. cmd1.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula, tblClasse.Classe FROM tblSostanzaChimica LEFT JOIN tblClasse ON tblClasse.IDClasse = tblSostanzaChimica.IDClasse LEFT JOIN tblSinonimo ON tblSinonimo.IDSostanzaChimica = tblSostanzaChimica.IDSostanzaChimica WHERE tblSinonimo.Sinonimo LIKE '%" + textBox13.Text + "%'";
5. cmd1.ExecuteNonQuery(); 6. con.Close(); 7. DataTable dt1 = new DataTable(); 8. SqlDataAdapter da1 = new SqlDataAdapter(cmd1); 9. da1.Fill(dt1); 10. dataGridView10.DataSource = dt1; 11. if (dataGridView10.RowCount == 0) MessageBox.Show("Nessun
risultato nè per Nome nè per Sinonimo"); 12. }
xxviii. } xxix. else if (checkBox20.Checked == false && checkBox19.Checked == false &&
checkBox18.Checked == true && checkBox33.Checked == false) xxx. {//ric per formula
xxxi. con.Open(); xxxii. SqlCommand cmd = con.CreateCommand(); xxxiii. cmd.CommandType = CommandType.Text; xxxiv. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula, tblClasse.Classe FROM tblSostanzaChimica LEFT JOIN tblClasse ON tblClasse.IDClasse = tblSostanzaChimica.IDClasse WHERE tblSostanzaChimica.Formula LIKE '%" + textBox13.Text + "%'";
xxxv. cmd.ExecuteNonQuery(); xxxvi. con.Close(); xxxvii. DataTable dt = new DataTable(); xxxviii. SqlDataAdapter da = new SqlDataAdapter(cmd); xxxix. da.Fill(dt);
xl. dataGridView10.DataSource = dt; xli. }
100
xlii. else if (checkBox20.Checked == false && checkBox19.Checked == false && checkBox18.Checked == false && checkBox33.Checked == true) {
xliii. //ric per sinonimo xliv. con.Open(); xlv. SqlCommand cmd = con.CreateCommand();
xlvi. cmd.CommandType = CommandType.Text; xlvii. cmd.CommandText = "SELECT tblSostanzaChimica.CAS,
tblSostanzaChimica.NomeSostanza, tblSostanzaChimica.Formula, tblClasse.Classe FROM tblSostanzaChimica LEFT JOIN tblClasse ON tblClasse.IDClasse = tblSostanzaChimica.IDClasse LEFT JOIN tblSinonimo ON tblSinonimo.IDSostanzaChimica = tblSostanzaChimica.IDSostanzaChimica WHERE tblSinonimo.Sinonimo LIKE '%" + textBox13.Text + "%'";
xlviii. cmd.ExecuteNonQuery(); xlix. con.Close();
l. DataTable dt = new DataTable(); li. SqlDataAdapter da = new SqlDataAdapter(cmd); lii. da.Fill(dt);
liii. dataGridView10.DataSource = dt; liv. } lv. else MessageBox.Show("Errore criterio di ricerca");
b. }
Analizziamo la riga n.1.a.xxvii. Quando il datagridview10 non produce alcun risultato (in questo
caso nessun risultato per Nome) allora ricerca per Sinonimo. In caso di nessun risultato neanche
per Sinonimo, avvisa con un MessageBox.
Gestione Utenti
La scheda di Gestione Utenti invece è la seguente :
Figura 66 : Gestione Utenti
101
I bottoni Modifica o Nuovo aprono il seguente Form :
Figura 67 : Form Utente
Vediamo prima l’evento Load del Form :
1. private void Utente_Load(object sender, EventArgs e){
i. con.Open(); ii. SqlDataReader leggi = null; iii. SqlCommand cmd1 = new SqlCommand("SELECT * FROM tblLaboratorio", con); iv. leggi = cmd1.ExecuteReader(); v. while (leggi.Read()) comboBox1.Items.Add(leggi["NomeLaboratorio"]); vi. con.Close(); vii. if (ut == true) {
viii. //nuovo utente ix. comboBox1.SelectedText = ""; x. } xi. else { xii. //modifica utente
xiii. comboBox1.SelectedText = laboratorio; xiv. textBox1.Text = nome; xv. textBox1.ReadOnly = true; xvi. textBox2.Text = cognome;
xvii. textBox2.ReadOnly = true; xviii. textBox3.Text = username;
xix. textBox4.Text = password; xx. if (abilitazione == "Administrator") checkBox2.Checked = true; xxi. else if (abilitazione == "User") checkBox1.Checked = true;
xxii. else checkBox3.Checked = true; xxiii. }
b. }
102
Dal evento Load vediamo subito che viene fatto test sulla variabile ut. Questa variabile proviene
dal Form principale e viene passata attraverso i metodi getter-setter. Infatti se questa variabile
vale true (quando si preme il bottone Aggiungi del Form principale)allora bisogna aggiungere un
nuovo utente, altrimenti bisogna modificarlo (riga 1.vii e 1.xi).
Il bottone Registra ha il seguente codice :
1. private void button1_Click(object sender, EventArgs e) {
i. if (checkBox1.Checked == true && checkBox2.Checked == true ||
checkBox2.Checked == true && checkBox3.Checked == true || checkBox1.Checked == true checkBox3.Checked == true) MessageBox.Show("Utente, non correttamente");
ii. else { iii. int abilita; iv. if (checkBox2.Checked == true) abilita = 1;//admin v. else if (checkBox1.Checked == true) abilita = 2; //user vi. else abilita = 3;//non abilitato
1. if (ut == true) 2. {//nuovo utente
3. string query = "INSERT INTO tblUtente (Username, Password,
NomeUtente, CognomeUtente, IDAbilitazione, IDLaboratorio, IDStato ) VALUES (@User, @Passwd, @Nome, @Cognome, @IDA, @IDL, @IDS)";
4. SqlCommand insert = new SqlCommand(query, con); 5. insert.Parameters.AddWithValue("@User", textBox3.Text); 6. insert.Parameters.AddWithValue("@Passwd", textBox4.Text); 7. insert.Parameters.AddWithValue("@Nome", textBox1.Text); 8. insert.Parameters.AddWithValue("@Cognome", textBox2.Text); 9. insert.Parameters.AddWithValue("@IDA", abilita); 10. insert.Parameters.AddWithValue("@IDL,
leggiIDLaboratorio(comboBox1.Text)); 11. insert.Parameters.AddWithValue("@IDS", 1); 12. con.Open(); 13. insert.ExecuteNonQuery(); 14. con.Close(); 15. MessageBox.Show("Nuovo utente registrato con successo"); 16. } 17. else {//modifica utente 18. SqlCommand update = new SqlCommand(); 19. update.CommandType = CommandType.Text; 20. update.CommandText = "UPDATE tblUtente SET [Username]= @User,
[Password]=@Passwd, [IDAbilitazione]=@IDA, [IDLaboratorio]=@IDL, [IDStato]=@IDS WHERE NomeUtente = '" + textBox1.Text + "' AND CognomeUtente = '"+textBox2.Text+"'";
21. update.Parameters.AddWithValue("@User", textBox3.Text); 22. update.Parameters.AddWithValue("@Passwd", textBox4.Text); 23. update.Parameters.AddWithValue("@IDA", abilita); 24. update.Parameters.AddWithValue("@IDL",
leggiIDLaboratorio(comboBox1.Text)); 25. update.Parameters.AddWithValue("@IDS", 1); 26. update.Connection = con; 27. con.Open(); 28. update.ExecuteNonQuery(); 29. con.Close(); 30. MessageBox.Show("Dati utente modificati con successo");
103
31. } vii. f2.button33_Click(f2.button33, f2.r);
viii. Close(); ix. }
b. } 2. private int leggiIDLaboratorio(string Laboratorio) {
i. int IDL = 0; ii. con.Open(); iii. SqlDataReader leggi = null; iv. SqlCommand cmd = new SqlCommand("SELECT * FROM tblLaboratorio WHERE
tblLaboratorio.NomeLaboratorio = '" + Laboratorio + "'", con); v. leggi = cmd.ExecuteReader(); vi. while (leggi.Read()) IDL = (int)leggi["IDLaboratorio"]; vii. con.Close();
viii. return IDL;
ix. }
Anche qui come in precedenza notiamo che in base al valore della variabile booleana ut o
inseriamo un nuovo utente, oppure lo modifichiamo. La funzione leggiIDLaboratorio riceve la
stringa di parametro che è il nome di laboratorio (riga 1.vi.10 e riga 1.vi.24) e nella tabella
tblLaboratorio recupera l’ID corrispondente.
Reportistica
Questa parte è quella più importante per un’utente. Infatti qui l’utente può attraverso filtri additivi
ricercare tutte le informazioni possibili. I criteri di ricerca sono così definiti : in base al operazione,
in base alla sostanza chimica, in base al utente e in base alla data.
Figura 68 : Scheda Reportistica
104
Il bottone Ricerca ha il seguente codice :
1. private void button35_Click(object sender, EventArgs e) { 2. if (checkBox26.Checked == true && checkBox27.Checked == false && checkBox28.Checked
== false && checkBox29.Checked == false) { 3. //ricerca solo per Operazione 4. con.Open(); 5. SqlCommand cmd = con.CreateCommand(); 6. cmd.CommandType = CommandType.Text; 7. cmd.CommandText = "SELECT tblOperazione.Operazione, tblOperazione.Data,
tblSostanzaChimica.CAS, tblSostanzaChimica.NomeSostanza, tblReagenteDV.Acquirente, tblReagenteDV.QuantitàResidua, tblUnitàDiMisura.Misura,tblLocazione.NomeLocazione, tblOperazione.Locazionepassata,tblUtente.NomeUtente, tblUtente.CognomeUtente FROM tblOperazione INNER JOIN tblReagenteDF ON tblReagenteDF.IDreagente = tblOperazione.IDreagente INNER JOIN tblSostanzaChimica ON tblSostanzaChimica.IDSostanzaChimica = tblReagenteDF.IDreagente INNER JOIN tblUtente ON tblOperazione.Username = tblUtente.Username INNER JOIN tblReagenteDV ON tblReagenteDV.IDReagente = tblOperazione.IDreagente INNER JOIN tblUnitàDiMisura ON tblUnitàDiMisura.IDMisura = tblReagenteDF.IDMisura INNER JOIN tblLocazione ON tblLocazione.IDLocazione = tblReagenteDV.IDlocazione WHERE tblOperazione.Operazione = '" + comboBox2.Text + "' ";
8. cmd.ExecuteNonQuery(); 9. DataTable dt = new DataTable(); 10. SqlDataAdapter da = new SqlDataAdapter(cmd); 11. da.Fill(dt); 12. con.Close(); 13. dataGridView17.DataSource = dt; 14. dataGridView17.Columns[7].HeaderText = "Locazione attuale";
i. if (dataGridView17.Rows.Count == 0) { ii. DataTable dt1 = new DataTable(); iii. dataGridView17.DataSource = dt1; iv. MessageBox.Show("Nessun risultato!"); v. } vi. else {
1. dataGridView17.Enabled = true; 2. dataGridView17.Rows[0].Selected =true; 3. button36.Enabled = true; 4. }
vii. } viii. else if (checkBox26.Checked == false && checkBox27.Checked == true &&
checkBox28.Checked == false && checkBox29.Checked == false) { ix. //ricerca solo in base alla sostanza selezionata x. con.Open(); xi. SqlCommand cmd = con.CreateCommand(); xii. cmd.CommandType = CommandType.Text;
xiii. cmd.CommandText = "SELECT tblOperazione.Operazione, tblOperazione.Data, tblSostanzaChimica.CAS, tblSostanzaChimica.NomeSostanza, tblReagenteDV.Acquirente, tblReagenteDV.QuantitàResidua, tblUnitàDiMisura.Misura, tblLocazione.NomeLocazione, tblOperazione.Locazionepassata, tblUtente.NomeUtente, tblUtente.CognomeUtente FROM tblOperazione INNER JOIN tblReagenteDF ON tblReagenteDF.IDreagente = tblOperazione.IDreagente INNER JOIN tblReagenteDV ON tblReagenteDV.IDReagente = tblReagenteDF.IDreagente INNER JOIN tblSostanzaChimica ON tblSostanzaChimica.IDSostanzaChimica = tblReagenteDF.IDSostanzaChimica INNER JOIN tblUtente ON tblOperazione.Username = tblUtente.Username INNER JOIN tblUnitàDiMisura ON tblUnitàDiMisura.IDMisura = tblReagenteDF.IDMisura INNER JOIN tblLocazione ON tblLocazione.IDLocazione = tblReagenteDV.IDLocazione WHERE tblOperazione.IDreagente = '" + dataGridView18.SelectedRows[0].Cells[0].Value.ToString() + "' ";
xiv. cmd.ExecuteNonQuery(); xv. DataTable dt = new DataTable();
105
xvi. SqlDataAdapter da = new SqlDataAdapter(cmd); xvii. da.Fill(dt); xviii. con.Close();
xix. dataGridView17.DataSource = dt; xx. dataGridView17.Columns[7].HeaderText = "Locazione attuale"; xxi. if (dataGridView17.Rows.Count == 0){
1. DataTable dt1 = new DataTable(); 2. dataGridView17.DataSource = dt1; 3. MessageBox.Show("Nessun risultato!");
xxii. } xxiii. else{
1. dataGridView17.Enabled = true; 2. dataGridView17.Rows[0].Selected = true; 3. button36.Enabled = true; 4. }
xxiv. } xxv. else if (checkBox26.Checked == false && checkBox27.Checked == false &&
checkBox28.Checked == true && checkBox29.Checked == false) { xxvi. //ricerca solo per utente xxvii. int i; xxviii. i = dataGridView16.SelectedRows.Count; xxix. int j = 0; xxx. DataTable dt1 = new DataTable();
xxxi. while(j<i) { xxxii. con.Open(); xxxiii. SqlCommand cmd = con.CreateCommand(); xxxiv. cmd.CommandType = CommandType.Text; xxxv. cmd.CommandText = "SELECT tblOperazione.Operazione, tblOperazione.Data,
tblSostanzaChimica.CAS, tblSostanzaChimica.NomeSostanza, tblReagenteDV.Acquirente, tblReagenteDV.QuantitàResidua, tblUnitàDiMisura.Misura, tblLocazione.NomeLocazione , tblOperazione.Locazionepassata, tblUtente.NomeUtente, tblUtente.CognomeUtente FROM tblOperazione INNER JOIN tblReagenteDF ON tblReagenteDF.IDreagente = tblOperazione.IDreagente INNER JOIN tblSostanzaChimica ON tblReagenteDF.IDSostanzaChimica = tblSostanzaChimica.IDSostanzaChimica INNER JOIN tblReagenteDV ON tblReagenteDV.IDReagente = tblReagenteDF.IDreagente INNER JOIN tblUnitàDiMisura ON tblUnitàDiMisura.IDMisura = tblReagenteDF.IDMisura INNER JOIN tblLocazione ON tblLocazione.IDLocazione = tblReagenteDV.IDLocazione INNER JOIN tblUtente ON tblOperazione.Username = tblUtente.Username WHERE tblOperazione.Username = '" + dataGridView16.SelectedRows[j].Cells[2].Value.ToString() + "' ";
xxxvi. cmd.ExecuteNonQuery(); xxxvii. DataTable dt = new DataTable(); xxxviii. SqlDataAdapter da = new SqlDataAdapter(cmd); xxxix. da.Fill(dt);
xl. con.Close(); xli. dt1.Merge(dt);
xlii. dataGridView17.DataSource = dt1; xliii. dt.Clear(); xliv. j++; xlv. }
xlvi. dataGridView17.Columns[7].HeaderText = "Locazione attuale"; xlvii. if (dataGridView17.Rows.Count == 0){
1. DataTable dt2 = new DataTable(); 2. dataGridView17.DataSource = dt2; 3. MessageBox.Show("Nessun risultato!");
xlviii. } xlix. else
l. { 1. dataGridView17.Enabled = true;
106
2. dataGridView17.Rows[0].Selected = true; 3. button36.Enabled = true;
li. } lii. }
liii. else if (checkBox26.Checked == false && checkBox27.Checked == false && checkBox28.Checked == false && checkBox29.Checked == true)
liv. {//ricerca solo per data lv. DateTime d1 = dateTimePicker1.Value; lvi. DateTime d2 = dateTimePicker2.Value;
lvii. con.Open(); lviii. SqlCommand cmd = con.CreateCommand();
lix. cmd.CommandType = CommandType.Text; lx. cmd.CommandText = "SELECT tblOperazione.Operazione, tblOperazione.Data,
tblSostanzaChimica.CAS, tblSostanzaChimica.NomeSostanza, tblReagenteDV.Acquirente, tblReagenteDV.QuantitàResidua, tblUnitàDiMisura.Misura, tblLocazione.NomeLocazione , tblOperazione.Locazionepassata, tblUtente.NomeUtente, tblUtente.CognomeUtente FROM tblOperazione INNER JOIN tblReagenteDF ON tblOperazione.IDreagente = tblReagenteDF.IDreagente INNER JOIN tblSostanzaChimica ON tblSostanzaChimica.IDSostanzaChimica = tblReagenteDF.IDSostanzaChimica INNER JOIN tblReagenteDV ON tblReagenteDV.IDReagente = tblReagenteDF.IDreagente INNER JOIN tblUnitàDiMisura ON tblUnitàDiMisura.IDMisura = tblReagenteDF.IDMisura INNER JOIN tblLocazione ON tblLocazione.IDLocazione = tblReagenteDV.IDLocazione INNER JOIN tblUtente ON tblOperazione.Username = tblUtente.Username WHERE ( tblOperazione.Data BETWEEN @DA AND @A ) ";
lxi. cmd.Parameters.Add("@DA", d1); lxii. cmd.Parameters.Add("@A", d2); lxiii. cmd.ExecuteNonQuery(); lxiv. DataTable dt = new DataTable(); lxv. SqlDataAdapter da = new SqlDataAdapter(cmd);
lxvi. da.Fill(dt); lxvii. con.Close(); lxviii. dataGridView17.DataSource = dt; lxix. dataGridView17.Columns[7].HeaderText = "Locazione attuale"; lxx. if (dataGridView17.Rows.Count == 0){
1. DataTable dt1 = new DataTable(); 2. dataGridView17.DataSource = dt1; 3. MessageBox.Show("Nessun risultato!");
lxxi. } lxxii. else lxxiii. {
1. dataGridView17.Enabled = true; 2. dataGridView17.Rows[0].Selected = true; 3. button36.Enabled = true;
lxxiv. } lxxv. } lxxvi. }
Qui viene riportato per semplicità soltanto il codice per ricerca singola di filtri. Quando invece c’è
una ricerca con filtri additivi, allora l’unica cosa che cambia sostanzialmente sono le condizioni
della query dove conle parole chiavi WHERE e AND si aggiungono condizioni. Quello che viene
fatto è questo : prima viene eseguita la query in base alla condizioni poste dei singoli filtri. Poi nel
caso non ci siano risulati, compare un messaggio che indica che non ci sono risulati, altrimenti
viene popolata la griglia sottostante con i risultati. L’Header della cella numero 7 viene chiamata
come “Locazione attuale” (per distinguerla dalla “Locazione passata” che può essere anche nulla
107
in certi casi), questo è fondamentale per distinguerla durante le operazioni di Spostamento. Se ci
sono dei risultati allora si attiva la possibilità di clicclare sul bottone Salva in Excel.
108
CONCLUSIONI
Gli obbiettivi che il progetto si era posto all’inizio sono stati raggiunti. Si è partiti dalla analisi delle
prestazioni di un prototipo e, in base alle considerazioni e suggerimenti degli utenti, si è addivenuti
ad una rivisitazione del progetto originale per tenere conto delle mutate condizioni sia normative
che di procedure, anche dovute alla fusione di diversi dipartimenti in un unico dipartimento.
L’applicazione sviluppata consiste in un data base, gestito da un DBMS MS-SQL server 2012 e da
un applicativo Windows Forms sviluppato in Visual studio.
In questo progetto sono state sviluppate 14 tabelle. Nella tabella principale tblSostanzaChimica
sono presenti 5000 record. Sono stati creati 15 Form. Sono presenti 11536 righe di codice.
Sono soddisfatto dell’esperienza che ho svolto. Mi ha permesso di capire intanto quanto sia
complicato gestire ambienti con tante informazioni e quanto sia complicato analizzare progetti da
migliorare, tenendo conto che l’utente finale ha bisogno continuamente di nuove specifiche anche
dettate dal cambiamento del mercato.
Il progetto non è attualmente ancora operativo, ma è in fase di testing. Superata questa fase
andrà sicuramente ad aiutare a gestire il personale del dipartimento nella gestione delle
informazioni delle sostanze chimiche. Inoltre, il prodotto potrebbe essere adottato da altri
dipartimenti dell’Ateneo per facilitare la gestione dei reagentari.
109
DEDICA
Mesi di lavoro per portare a termine questa tesi di laurea. Dedicarla a solo una persona o a due,
non avrebbe senso. Dedicarla a chi mi vuole bene, a chi mi ha voluto bene e ad esempio non c’è più
e a chi mi vorrà bene invece è sensato e giusto, ma senza fare nomi. Accettare le ingiustizie e le
avversità come se fossero una sfida per cercare di superarle.
Nei miei anni universitari ho conosciuto tante persone nuove, la mia conoscenza si è ampliata sia in
termini di amicizie che in termini di sapienza. Questo lo devo all’unviersità.
Grazie genitori, grazie amici e grazie università. Se oggi sono quello che sono lo devo soprattutto a
voi! Questa tesi ed li mio lavoro lo dedico a voi!!!