• Non ci sono risultati.

IMPLEMENTAZIONE DELL’AUTOMA DEI PRELIEVI In questo capitolo è illustrata l’implementazione dell’automa dei prelievi

N/A
N/A
Protected

Academic year: 2021

Condividi "IMPLEMENTAZIONE DELL’AUTOMA DEI PRELIEVI In questo capitolo è illustrata l’implementazione dell’automa dei prelievi"

Copied!
47
0
0

Testo completo

(1)

68 6. IMPLEMENTAZIONE DELL’AUTOMA DEI PRELIEVI

In questo capitolo è illustrata l’implementazione dell’automa dei prelievi. La presentazione è organizzata secondo il flusso logico seguito dall’operatore, andando di volta in volta a presentare il codice sviluppato. Il flusso logico è stato diviso in tre fasi in base all’ambiente di lavoro che l’operatore si trova davanti. Le fasi sono descritte in dettaglio all’interno del paragrafo 6.1. Si è preferito utilizzare questo approccio a causa dalla varietà dei linguaggi di programmazione usati. Si è ritenuto, infatti, che una trattazione disgiunta delle varie parti dell’architettura fosse poco chiara.

Tutto il codice sviluppato è stato commentato il più possibile. Perciò in questa sede saranno esposte solo le porzioni di codice più interessanti. Inoltre, le linee a cui è dedicato un commento speciale dopo il codice, sono evidenziate di blu.

In particolare il codice PL/SQL delle store procedure è stato riportato raramente in quanto molto lungo, spesso a causa delle dichiarazioni dei cursori, e di difficile consultazione in formato cartaceo. Di conseguenza si è deciso di esporre solamente le azioni compiute dalle varie store.

Gli oggetti Java più complessi, poi, che saranno incontrati durante l’illustrazione del codice, saranno descritti in paragrafi dedicati.

L’ambiente di sviluppo utilizzato per implementare l’interfaccia grafica è Netbeans. É un IDE gratuito e supportato direttamente da Sun Microsystems.

6.1 Fasi dell’evoluzione dell’automa dei prelievi

L’evoluzione dell’automa dei prelievi può essere suddivisa in tre fasi principali:

1. Impostazione dei parametri e inizializzazione (ambiente Java)

2. Elaborazione e generazione delle liste di prelievo (ambiente rete di Petri) 3. Visualizzazione e analisi dei risultati (ambiente Java)

Queste suddivisione, come già detto, è definita dal cambio di ambiente di lavoro al quale l’operatore è sottoposto. Nel prossimi paragrafi sono illustrati i dettagli di ognuna di queste fasi.

6.2 Fase 1: Impostazione dei parametri e inizializzazione

In questa fase l’utente avvia l’automa dei prelievi. Questo si presenta sottoforma di un’interfaccia Java nella quale l’utente può avviare la procedura di generazione delle liste di prelievo andando a inserire alcuni parametri. Una volta innescata tale procedura, questa va a inizializzare tutte le strutture dati necessarie alla reti di Petri.

6.2.1 Panoramica interfaccia Java

Nello sviluppo dell’interfaccia grafica dell’automa si è cercato di creare un involucro modulare. Non si voleva creare un ambiente che potesse contenere solamente l’automa dei prelievi. L’intento, infatti, era quello di creare un pacchetto software all’interno del quale l’automa dei prelievi fosse un modulo in modo da permettere, in futuro la realizzazione di altri moduli. Tale approccio è stato aiutato dalla natura modulare del linguaggio Java.

La tipologia di interfaccia scelta è MDI (MultiDocument Interface) cioè composta da una finestra di lavoro principale all’interno della quale si possono aprire più finestre contemporaneamente.

(2)

69 6.2.1.1 Convenzioni per il codice Java

L’interfaccia Java è composta da una serie di classi Java. Ognuna è racchiusa all’interno di un file con estensione .java. Tali file sono stati suddivisi in tanti package in base alle loro funzionalità.

La suddivisione in package è gestita direttamente dal linguaggio Java. I nomi dei pacchetti rispettano lo standard di nomenclatura Java. Lo standard afferma che affinché il nome del pacchetto risulti univoco sia composto da una struttura analoga a quella dei domini internet. Il nome, infatti, scelto per i pacchetti è:

suffisso.nomemodulo.area

Il suffisso è sempre lo stesso per tutti i moduli ed è la parte che garantisce univocità del nome essendo un dominio internet. Il valore del suffisso è : eu.iacopo.

Il nomemodulo, nel caso dell’automa dei prelievi, è il seguente acronimo: galpesp (Generazione Automatica Liste di Prelievo e Spedizioni)

L’area identifica il compito del pacchetto. Alcuni esempi sono:

• Database: include tutte le classi Java inerenti allo scambio dati fra Java-Oracle come la creazione e la cancellazione della connessione al database stesso, la creazione di query ecc…

• File: include tutte le classi Java inerenti alla lettura e scrittura da file di testo.

Il pacchetto associato ad ogni funzione o oggetto presente nel codice sarà riportato all’interno di parentesi quadre.

6.2.1.2 Struttura base della interfaccia grafica

Come è stato già detto la struttura dell’interfaccia grafica è di tipo MDI.

In Fig. 54 sono evidenziati gli oggetti Java che compongono un applicazione MDI. La finestra principale è composta da un oggetto JFrame all’interno del quale viene aggiunto

Fig. 54 Struttura di una applicazione MDI

(3)

70 l’oggetto JDesktopPane. Questo funziona come il desktop di Windows: ci si possono posizione finestre multiple e ridurre ad icona. Nell’esempio vediamo due finestra aperte e una ridotta ad icona. Le finestra interne sono istanze dell’oggetto JInternalFrame e devono essere associate al JDesktopPane una volta create per essere visualizzate.

Ogni JInternalFrame può essere composto da semplici pannelli o da pannelli a schede come nell’esempio in Fig. 54. I pannelli a schede sono realizzati attraverso l’oggetto JTabbedPane mentre i singoli panelli contenuti nella schede con l’oggetto JPanel.

Nel progetto è stata sfruttata una delle caratteristiche più potenti di un linguaggio di programmazione ad oggetti: l’ereditarietà. Abbiamo, infatti, di volta in volta creato, attraverso la definizioni di classi apposite, nuovi oggetti personalizzati estensioni di questi oggetti base.

In particolare alcuni degli oggetti creati sono (fra parentesi indico l’oggetto base associato):

• GalpespMDI (JFrame): è la finestra principale dell’interfaccia e appartiene al package: eu.iacopo.galpesp.run

• IFrameRisultatoGalp (JInternalFrame): è la finestra interna per il lancio della generazione delle liste di prelievo che sarà esaminata in 6.2.2. Questo appartiene al package eu.iacopo.galpesp.frame.

• LancioGalpPanel (JPanel): è il pannello della scheda Parametri della finestra Generazione liste di prelievo visibile in Fig. 54.

Le classi dei vari oggetti personalizzati definiti nel progetto sono racchiusi in package in base al tipo: tutti i JInternalFrame sono memorizzati nel package eu.iacopo.galpesp.frame, tutti i JPanel in eu.iacopo.galpesp.panel ecc…

6.2.1.3 Documentazione interattiva

Tutto il codice Java, come già detto, è stato commentato. È stata creata, inoltre, una documentazione interattiva in formato HTML che documenta in maniera completa la struttura e i dettagli delle varie classi.

La documentazione è stata realizzata secondo lo standard di commento Javadoc utilizzando il tool AutoComment presente all’interno dell’ambiente di sviluppo Netbeans.

Questo tool crea una serie di pagine HTML nella sottodirectory del progetto

Fig. 55 Documentazione interattiva del codice Java

(4)

71 /dist/javadoc/. La documentazione così creata contiene sia una panoramica di tutti i pacchetti del progetto sia i dettagli di ogni singola funzione e classe all’interno dei pacchetti.

Un esempio della documentazione di una classe è riportato nella Fig. 55.

6.2.2 Schermata inserimento dei parametri

L’operatore come primo passo per la generazione della lista dei prelievi, dopo aver aperto l’automa dei prelievi, deve fare click sul menu Liste di PrelievoÆ Generazione liste di prelievo per accedere alla finestra di inserimento dei parametri. (Fig. 56) Questa finestra è implementata dall’oggetto IFrameRisultatoGalp (eu.iacopo.galpesp.frame).

È stata inserita una scorciatoia da tastiera: CTRL+W per aprirla.

La finestra è stata disegnata in modo tale che rispecchiasse il più possibile quella del portafoglio ordini, presente all’interno del gestionale BMS per facilitare l’utilizzo agli utenti.

L’operatore deve specificare la stagione di spedizione e l’intervallo di date di consegna degli ordini clienti di cui vuole generare le liste di prelievo.

Una volta che l’operatore ha inserito questi parametri ha due possibilità: specificare un particolare codice cliente o lasciare vuoto il campo della schermata di Fig. 56.

Nel primo caso una volta che avrà premuto sul pulsante Genera il programma andrà a cercare solo ordini clienti associati a quel cliente.

Nel secondo, invece, andrà a cercare gli ordini di tutti i clienti.

Il menu a discesa Stagione di spedizione è caricato direttamente dal database aziendale attraverso la funzione iniz_jcb_pcstag() che appartiene al package eu.iacopo.galpesp.panel.

* Inizializza la combobox con i valori della stagione presenti nel database

*/

private void iniz_jcb_pcstag() { String la_stag;

int i;

String ls_query = "SELECT CSTAG,\n" + " BDELE,\n" + " TSTAG,\n" + " SSTAG,\n" + " FSTAG,\n" + " CALLE,\n" + " CSOCI\n" +

" FROM CONT_AMM.BLSTAGIO\n" + "WHERE\n" +

"BSTOR = 'A' ” + "ORDER BY SSTAG DESC";

Fig. 56 Finestra inserimento parametri

(5)

72

//creo la query

Query lo_query = new Query();

//recupero righe e colonne

ArrayList lo_ris_query = lo_query.eseguiQuery(ls_query);

//Vector delle righe a sua volta ogni riga è un vector Vector lo_rigm = (Vector) lo_ris_query.get(1);

int li_num_rig = lo_rigm.size();

//ciclo sulle righe e riempio la casella combinata con il valore

//della terza colonna for(i=0;i<li_num_rig;i++){

Vector lo_rig = (Vector) lo_rigm.get(i);

la_stag =(String) lo_rig.get(2);

jcb_pcstag.addItem(la_stag);

}

//chiudo la connessione lo_query.chiudiQuery();

}

Nel Listato 11 il recupero dal database della descrizione delle stagioni di spedizioni è realizzata attraverso la query evidenziata. La descrizione della stagione è contenuta all’interno del campo TSTAG della tabella BLSTAGIO. La tabella ha la gestione della storicità, infatti, troviamo nella clausola where il campo BSTOR = ‘A’. Per effettuare la query all’interno del database Oracle si usa l’oggetto Query [eu.iacopo.galpesp.database] e le relative funzioni membro come eseguiQuery e chiudiQuery.per un approfondimento sull’oggetto query vedi il paragrafo 6.2.2.1.

Una volta inseriti i parametri l’operatore preme sul pulsante Genera per dare inizio all’inizializzazione delle strutture dati. Le attività associate alla pressione del pulsante Genera sono descritte nel paragrafo 6.2.2.4.

6.2.2.1 Classe Query

L’oggetto Query ha come compiti quello di collegarsi al database, se la connessione non è già stata effettuata, e recuperare i dati richiesti dalla query passata.

Il recupero dei dati avviene attraverso la funzione eseguiQuery che ritorna le colonne e le righe della query all’interno di un ArrayList.

Sia i costruttori che le funzioni membro qui sotto elencate sono descritte nei paragrafi successivi.

Oggetti membro

• private ResultSet r: contiene i valori ritornati dalla query

• private Statement s: struttura necessaria per l’esecuzione della query

• private Connect connessione: connessione al database Costruttori

• Query()

• Query(Connect p_conn): inizializza solo lo statement con la connessione passata Listato 11 Funzione iniz_jcb_pcstag()

(6)

73 Funzioni membro

• chiudiQuery(): chiude la connessione della query

• ArrayList eseguiQuery(String ps_query)

• Vector getNextRow( ResultSet rs,ResultSetMetaData rsmd )

• ArrayList preparaRisultato(ResultSet rs)

• ResultSet ritornaRs(): ritorna l’oggetto membro ResultSet 6.2.2.1.1 Query()

public Query(){

try{

Main go_main = new Main();

//mi connetto al database connessione = new

Connect(go_main.gs_connessione,go_main.gs_user,go_main.gs_passwod);

//inizializzo lo statement

s=connessione.rendiConnessione().createStatement();

}

catch (Exception e){

new Eccezione(e);

} }

Query() è il costruttore di default. Questo crea la connessione andando a recuperare i parametri di configurazione del database dalle variabili globali dell’oggetto Main. La connessione viene effettuata tramite l’oggetto Connect.

6.2.2.1.2 ArrayList eseguiQuery(String ps_query)

public ArrayList eseguiQuery(String ps_query){

ArrayList lo_al = null;

try{

//eseguo la query

r= s.executeQuery(ps_query);

lo_al = this.preparaRisultato(r);

}

catch(Exception e){

new Eccezione(e);

}

return lo_al;

}

I dati sono recuperati attraverso il metodo executeQuery dello statement e sono appoggiati all’interno di un ResultSet. La funzione preparaRisultato divide i dati del ResultSet passato in due vettori, uno contenente le righe e l’altro le colonne. Questi due vettori sono ritornati incapsulati all’interno di un ArrayList.

Listato 12 Query()

Listato 13 ArrayList eseguiQuery(String ps_query)

(7)

74 6.2.2.1.3 ArrayList preparaRisultato(ResultSet rs)

public ArrayList preparaRisultato(ResultSet rs) throws SQLException {

boolean moreRecords = rs.next();

//definisco due vettori uno per le colonne e uno per le righe del

//result set

Vector nomecol = new Vector();

Vector rows = new Vector();

ArrayList col_row = new ArrayList();

//recupero i metadata dal resultSet

ResultSetMetaData rsmd = rs.getMetaData();

//popolo il vettore con i nomi delle colonne for (int i = 1; i <= rsmd.getColumnCount(); ++i ) nomecol.addElement(rsmd.getColumnName(i));

//popolo il vettore delle righe do {

rows.addElement( getNextRow(rs,rsmd));

} while (rs.next());

//preparo il vettore risultato col_row.add(nomecol);

col_row.add(rows);

return col_row;

}

Questa funzione dato il ResultSet passato crea due Vector nomecol e rows nei quale inserisce rispettivamente i valori delle colonne e i valori delle righe. Una particolare attenzione va posta intorno alla righe. È stato necessario creare la funzione getNextRow per convertire in maniera corretta i tipi dei dati contenuti all’interno delle tabelle Oracle nei corrispondenti tipi Java.

6.2.2.1.4 Vector getNextRow( ResultSet rs,ResultSetMetaData rsmd )

private Vector getNextRow( ResultSet rs,

ResultSetMetaData rsmd ) throws SQLException

{

Vector currentRow = new Vector();

for ( int i = 1; i <= rsmd.getColumnCount(); ++i ) switch( rsmd.getColumnType( i ) ) {

case Types.VARCHAR:

currentRow.addElement( rs.getString( i ) );

break;

case Types.INTEGER:

currentRow.addElement(

new Long( rs.getLong( i ) ) );

break;

// case tipo NUMBER case 2:

currentRow.addElement(

new Double( rs.getDouble( i ) ) );

Listato 14 ArrayList preparaRisultato(ResultSet rs)

(8)

75

break;

//caso della data case 91:

currentRow.addElement(rs.getDate(i));

break;

default:

System.out.println( "Type was: " + rsmd.getColumnTypeName( i ) );

}

return currentRow;

}

Questa funzione cicla sulla riga corrente del ResulSet passato sfruttando le informazioni contenute all’interno dei metadati quali il numero delle colonne. Durante il ciclo aggiunge gli elementi della riga corrente con il giusto tipo al Vector currentRow. Ad esempio se il tipo del campo della tabella è Number verrà inserito all’interno del vettore come un double.

6.2.2.2 Classe Connect

L’oggetto Connect realizza la connessione al database sfruttando i driver JDBC (Java DataBase Connectivity) disponibili per Oracle. Questo oggetto fa parte del package eu.iacopo.galpesp.database.

Oggetti membro

• private Connection c: connessione al database Costruttori

• Connect(String ps_dbUrl,String ps_User, String ps_Passwd) Funzioni membro

• Connection rendiConnessione(): restituisce l’oggetto membro connection

• chiudiConnessione(): chiude la connessione al database 6.2.2.2.1 Connect(String ps_dbUrl,String ps_User, String ps_Passwd)

Questa funzione crea la connessione con il database in base ai parametri passati.

public Connect(String ps_dbUrl,String ps_User, String ps_Passwd) throws Eccezione, ClassNotFoundException {

try{

//Carico il driver

Class.forName("oracle.jdbc.driver.OracleDriver");

c = DriverManager.getConnection(ps_dbUrl,ps_User,ps_Passwd);

}

catch (Exception e){

throw new Eccezione(e);

} }

6.2.2.3 Gestione dell’eccezioni

In molti dei listati analizzati fino ad ora troviamo la gestione dell’eccezioni attraverso l’utilizzo del costrutto try…catch. La gestione di eventuali eccezioni che si vengono a

Listato 15 Vector getNextRow( ResultSet rs,ResultSetMetaData rsmd)

Listato 16 Connect(String ps_dbUrl,String ps_User, String ps_Passwd)

(9)

76 formare durante l’esecuzione del codice è fondamentale sia in fase di sviluppo che in quella di messa in produzione: in quanto è molto più semplice andare a rintracciare l’errore di programmazione. Affinché le informazioni sullo stack dell’eccezione fossero disponibile in maniera chiara nell’interfaccia grafica è stato creato un package dedicato proprio all’eccezioni: eu.iacopo.galpesp.eccezioni.

All’interno di questo package troviamo due classi:

• Eccezione

• FormEccezione 6.2.2.3.1 Eccezione

public class Eccezione extends Exception {

public Eccezione(Throwable e) { //crea l'oggeto padre' super(e);

new FormEccezione((Exception) e);

} }

Questa classe sfrutta una delle potenzialità maggiori di un linguaggio ad oggetti come Java: l’ereditarietà. Infatti il nostro obbiettivo è quello di potenziare l’oggetto eccezione già presente all’interno di Java andando ad aggiungere un form che ci riporti in un formato grafico piacevole le informazioni sull’eccezione sollevata.

Nel Listato 17 è definita la classe Eccezione con la clausola extends che permette di ereditare tutti i metodi e gli oggetti membro dell’oggetto Exception. Il costruttore, inoltre, richiama il costruttore della classe “madre” attraverso la chiamata super().

Il form per la presentazione delle informazione è l’oggetto FormEccezione.

6.2.2.3.2 FormEccezione

public FormEccezione(Exception e) { initComponents();

//aggiorno il nome della eccezione

jt_Eccezione.setText(e.toString()+e.getLocalizedMessage());

//stampo la traccia sullo stderro e.printStackTrace();

//aggiorno la descrizione della pila dell'eccezione' StackTraceElement[] stack = e.getStackTrace();

String ls_testo = null;

for(int i=0; i < stack.length; i++){

ls_testo = ls_testo + stack[i].toString() + "\n";

}

// jta_Stack.setText(stack[0].toString());

jta_Stack.setText(ls_testo);

// rende visibile la schermata this.setVisible(true);

}

Il costruttore dell’oggetto FormEccezione è riportato in Listato 18. In tutti gli oggetti che fanno parte dell’interfaccia grafica è presente il metodo initComponents(). Questo è

Listato 17 Classe Eccezione

Listato 18 Costruttore FormEccezione

(10)

77 automaticamente generato dall’ambiente di sviluppo NetBeans e si occupa di inizializzare tutti i componenti grafici dell’oggetto in questione. All’interno del form è riportato sia il nome dell’eccezione sollevata sia l’intero stack dell’eccezione. Un esempio di tale form è visibile in Fig. 57; l’eccezione sollevata in questo caso è relativa al tentativo di accedere ad un puntatore con valore null. Il punto da dove l’eccezione si è sollevata è stato all’interno del metodo eseguiQuery alla linea 78 come riportato nello stack in figura.

6.2.2.4 Evento click sul pulsante Genera

Alla pressione del pulsante Genera è associato il codice nel Listato 19.

private void jb_generaActionPerformed(java.awt.event.ActionEvent evt) {

CallableStatement lo_proc;

Connect lo_conn;

int li_oelen;

int li_nprog_ielen;

File lo_file;

//recupero le variabili globali Main go_main = new Main();

try {

//recupero il codice della stagione String ls_tstag =

jcb_pcstag.getSelectedItem().toString();

String ls_pcstag = this.recuperaPc_stag(ls_tstag);

//controllo che la stagione sia diversa da default if (ls_pcstag.equals("0")){

//creo la finestra di dialogo come frame interno

JOptionPane.showInternalConfirmDialog(this.getParent(), "Selezionare una stagione di spedizione",

"Stagione di spedizione", JOptionPane.DEFAULT_OPTION,

JOptionPane.WARNING_MESSAGE);

return;

}

//creo la connessione lo_conn = new

Connect(go_main.gs_connessione,go_main.gs_user,go_main.gs_passwod);

//preparo i parametri per la chiamata della SP

lo_proc = lo_conn.rendiConnessione().prepareCall("{ call pkg_galpesp_run.sp_main(?, ?, ?, ?, ?, ?, ?) }");

Fig. 57 Esempio di un form per le eccezioni

(11)

78

//controllo che l'inpiut dell'utente sia corretto'

//se non spefica nessun cliente metto a null String lo_ncocg = jt_ncocg.getText();

if (lo_ncocg == "" ){

lo_proc.setNull(2,Types.NUMERIC);

} else{

lo_proc.setString(2, jt_ncocg.getText());

}

//setto i parametri

lo_proc.setString(1, jt_pc_soci.getText());

lo_proc.setString(3,ls_pcstag);

lo_proc.setString(4,jt_dscad_i.getText());

lo_proc.setString(5,jt_dscad_f.getText());

//setto i parametri in uscita //oelen

lo_proc.registerOutParameter(6,java.sql.Types.INTEGER);

//nprog_ielen

lo_proc.registerOutParameter(7,java.sql.Types.INTEGER);

//eseguo la store procedure

//gli statement sono autocommitanti lo_proc.execute();

//recupero i due parametri in uscita li_oelen = lo_proc.getInt(6);

li_nprog_ielen = lo_proc.getInt(7);

//chiudo lo statement lo_proc.close();

//chiudo la connessione lo_conn.chiudiConnessione();

//salvo i parametri su un file per il passaggio alla rete di petri

//recupero il nome del file come variabile globale del main

go_main = new Main();

lo_file = new File();

lo_file.apriScrittura(go_main.gs_file_path_name);

lo_file.scriviLinea(String.valueOf(li_oelen));

lo_file.scriviLinea(String.valueOf(li_nprog_ielen-1));

//chiudo il file

lo_file.chiudiFileScrittura();

//lancio l'ambiente Renew'

String[] ls_lanciaRdp = new String[1];

ls_lanciaRdp[0]= new Main().gs_renew;

new GoodWindowsExec(ls_lanciaRdp);

//creo la finestra di dialogo come frame interno

JOptionPane.showInternalConfirmDialog(this.getParent(), "Elaborazione Terminata con successo","Elaborazione Terminata",

JOptionPane.DEFAULT_OPTION,

JOptionPane.INFORMATION_MESSAGE);

(12)

79

} catch (ClassNotFoundException ex) { ex.printStackTrace();

new Eccezione(ex);

} catch (Eccezione ex) { ex.printStackTrace();

}

catch (Exception ex){

new Eccezione(ex);

}

Questo codice compie le seguenti azioni:

• Recupera l’input dell’operatore e ne fa un controllo di correttezza

• Chiama la store procedure che inizializza le strutture dati necessarie per la fase successiva

• Salva i parametri di dialogo fra Java e la rete di Petri all’interno di un file di testo

• Lancia l’ambiente di simulazione Renew

Tutti le azioni sopra elencate vengono commentate nei dettagli nei paragrafi successivi.

6.2.2.4.1 Recupero input utente e controllo di correttezza

Per primo è recuperato il codice della stagione associato alla descrizione selezionata dall’utente.

Questo avviene attraverso la funzione recuperaPc_stag (ls_tstag).

Una volta recuperato tale codice, controlla se il valore selezionato dall’utente corrisponde alla stagione Default (‘0’); se è così, presenta una finestra di dialogo che avverte l’utente di selezionare una stagione corretta.

6.2.2.4.2 Inizializzazione delle struttura dati

L’inizializzazione delle strutture dati necessarie alla reti di Petri per potersi evolvere avviene attraverso l’utilizzo di una store procedure. Le strutture dati e le azioni compiute dalla store procedure SP_MAIN sono analizzate in dettaglio all’interno del paragrafo 6.2.2.5.

La chiamata di una store procedure da codice Java è composta dalle seguenti fasi:

a) Preparazione della chiamata della store b) Valorizzazione dei parametri con modalità IN c) Registrazione dei parametri con modalità OUT d) Chiamata vera e propria della store procedure e) Recupero dei parametri con modalità OUT

a) avviene utilizzando la funzione membro dell’oggetto Connection prepareCall . Questa prende come parametro una stringa di testo racchiusa tra parentesi graffe. La stringa è composta dal nome della store procedure da chiamare comprensivo del package che

Listato 19 Pressione sul pulsante genera: jb_generaActionPerformed

Fig. 58 Controllo correttezza parametri

(13)

80 eventualmente la contenga. Al posto dei parametri della store si mettono dei punti interrogativi. Ad esempio prepareCall("{ call pkg_galpesp_run.

sp_main(?, ?, ?, ?, ?, ?, ?) }"); prepara la chiamata della store sp_main contenuta nel package pkg_galpesp_run ed ha 7 parametri. La prepareCall crea e ritorna un CallableStatement al quale devo essere configurati i parametri. La posizione di ogni parametro all’interno della chiamata è identificata da un indice numerico.

Questo indice ha come valore base 1.

b) Devono essere specificati i valori con il relativo tipo di tutti i parametri con modalità IN.

Per fare ciò si utilizza la funzione setTIPO(indice_parametro, valore_parametro); dove TIPO è sostituito dal tipo del parametro. Nel Listato 19 i parametri da passare sono di tipo stringa. Se il valore del parametro è NULL allora si utilizza la funzione setNull.

c) I parametri con modalità OUT si registrano attraverso la funzione registerOutParameter(indice_parametro, tipo parametro). Come tipo del parametro posso sfruttare la costante adeguata già predisposta in Java per il linguaggio sql contenute in java.sql.Types.*.

d) La chiamata che esegue il codice della store procedure avviene con la funzione execute()

e) Una volta che la store è stata eseguita i valori dei parametri con modalità OUT sono recuperati attraverso l’utilizzo della funzione getTIPO(indice_parametro) dove TIPO ha lo stesso comportamento visto nel punto c.

6.2.2.4.3 Salvataggio dei parametri di dialogo

Le uniche informazioni necessarie alla reti di Petri per l’avvio della sua evoluzione sono:

• Il numero della elaborazione (oelen)

• Numero totale degli ordini da elaborare

Il primo è un numero progressivo che identifica l’elaborazione corrente dell’automa dei prelievi. Il numero è calcolato all’interno della store procedure sp_main sfruttando un sequel Oracle. Questo numero è stato inserito per assicurare l’integrità dei dati all’interno del database e la possibilità di lanciare l’automa dei prelievi da più postazioni contemporaneamente.

Il numero totale degli ordini da elaborare sarà utilizzato dalla rete di petri per determinare la fine della sua elaborazione.

Visti i problemi riscontrati nell’instanziare una rete di Petri da codice Java illustrati nel paragrafo 5.3.5.7, il metodo più semplice,per far sì che queste informazioni, calcolate dalla store procedure, fossero disponibili alla rete è stato quello di memorizzarle all’interno di un file di testo. E’ da notare che il path deve essere scritto con doppi backslash in quanto il backslash è considerato carattere di escape. Ad esempio “C:\\java\\code\\Tesi\\appoggio.txt”.

È stata creata, quindi, una classe ad hoc per la gestione della lettura e scrittura in un file di testo contenuta nel package eu.iacopo.galpesp.file e denominata File. Nel Listato 19 si vede l’utilizzo di questa classe per l’apertura e la scrittura dei due parametri suddetti.

Il path del file è memorizzato all’interno di una variabile globale di tipo stringa all’interno della classe Java Main (eu.iacopo.galpesp.run).

(14)

81 6.2.2.4.4 Lancio dell’ambiente Renew

Il lancio dell’ambiente di simulazione della rete di petri è eseguito tramite l’utilizzo della classe GoodWindowsExec il cui costruttore accetta come parametro il path dell’applicazione da lanciare. Il path del Renew è memorizzato all’interno della variabile globale di tipo stringa all’interno della classe Main (eu.iacopo.galpesp.run) e ha la stessa modalità di scrittura vista per il path del file di appoggio.

La classe GoodWindowsExec permette il lancio corretto su tutte le piattaforme Windows con versione Windows 95 o superiore.

6.2.2.5 Dettagli sull’inizializzazione delle strutture dati

L’automa dei prelievi ha necessità, per poter iniziare la sua elaborazione, dell’elenco degli ordini cliente da processare. Fra gli ordini di questo elenco l’automa dovrà valutare quali siano idonei per l’imballaggio e quali no.

Il recupero di tali informazioni è effettuato all’interno della store procedure sp_main contenuta nel package PKG_GALPESP_RUN il cui codice è riportato nel Listato 20.

procedure SP_MAIN(PC_CSOCI in VARCHAR2, pi_ncocg in number,

pc_cstag in varchar2, pd_dscad_i in date, pd_dscad_f in date, pi_oelen out integer,

pi_nprog_ielen out integer) is

--dicharazione variabili locali li_oelen number;

li_prog_ielen number;

ls_csoci varchar2(6);

li_nprog_ielen number;

ln_ncocg number;

ln_cindi number;

ln_canag_sclie number;

--flag di test

b_test_cap_ubi number;

--- begin

--prendo un nuovo oelen SELECT SQ_OELEN.NEXTVAL INTO li_oelen

FROM DUAL;

--assegno il parametro in uscita pi_oelen:= li_oelen;

--setto a null nel caso in cui non venga passato nessun codice cliente

ln_canag_sclie := null;

if (pi_ncocg is not null) then

--calcolo il codice cliente bms da quello bl select ac.canag_sclie

into ln_canag_sclie from anclient ac

(15)

82

where ac.ncocg = pi_ncocg and ac.bdele = 'S'

and ac.bstor = 'A';

end if;

--lancio la store per la situazione attuale e che popola la tabella A_LGPOORCL

bl_pkg_lista_prel.spu_int_cliente(pc_csoci, li_oelen,

ln_canag_sclie, pc_cstag,

pd_dscad_i, pd_dscad_f);

--inizializzo la tabella LGGALP

SP_INIZ_LGGALP(li_oelen,li_nprog_ielen,pc_cstag);

--assegno il numeratore in uscita delle elaborazioni pi_nprog_ielen := li_nprog_ielen;

end;

In particolare all’interno di questa store ne viene sfruttata un'altra già presente all’interno del gestionale BMS la SPU_INT_CLIENTE del package BL_PKG_LISTA_PREL. Questa store ha il compito di popolare la tabella A_LGPOORCL con tutte le informazioni del portafoglio clienti presentato nel paragrafo 1.5.3.1.La store prende come parametri:

• Codice della società

• Numero dell’elaborazione

• Eventuale codice cliente

• Codice della stagione di spedizione

• L’intervallo delle date di consegna degli ordini clienti da elaborare

Tutte queste informazioni sono state fornite dall’operatore nella schermata d’inserimento dei parametri descritta nel paragrafo 6.2.2.

L’automa dei prelievi memorizza i risultati della sua elaborazione all’interno di una tabella LGGALP appositamente creata nel database Oracle. La struttura della tabella è riportata in Fig. 59.

Name Type Nullable Default Descrizione

CSOCI VARCHAR2(24) N Codice della società

OELEN INTEGER N Numero di elaborazione

NCOCG INTEGER N Codice del cliente

NPROG_IELEN INTEGER N Progressivo dell’elaborazione CCAUS_SDOCU VARCHAR2(24) N Causale dell’ordine cliente

YCALE INTEGER N Anno dell’ordine cliente

NPROT_DDOCU INTEGER N Numero ordine cliente

DSCAD_DRIGD DATE Y Data di consegna dell’ordine

CSTAG VARCHAR2(10) Y stagione di vendita

DISP_P2 NUMBER Y disponibilità nel magazzino p2

DISP_P3 NUMBER Y disponibilità nel magazzino p3

Listato 20 Store procedure sp_main

(16)

83

BSALD INTEGER Y 0 flag ordine a saldo

BUBI INTEGER N 0 flag quota ubicazione

DELAB DATE N Data dell’elaborazione

Questa tabella deve essere inizializzata con alcuni dei valori presenti all’interno della tabella A_LGPOORCL. Questo compito è svolto dalla store procedure SP_INIZ_LGGALP.

Questa non carica tutti gli ordini cliente ma solo quelli appartenenti a clienti che sono solvibili. Quindi, il controllo di morosità del cliente viene fatto direttamente in fase di inizializzazione dell’insieme degli ordini che devono essere passati alla rete di Petri.

L’informazione sullo stato di solvibilità del cliente è conservata all’interno del campo tnota_snume_0 della tabella A_LGPOORCL. Se tale campo assume valore uno significa che il cliente è moroso. Se, invece, vale zero il cliente è in regola con i pagamenti.

La store, inoltre ha come parametro con modalità OUT li_nprog_ielen che è il numero di ordini totali che l’automa deve processare. Questo valore verrà poi scritto nel file di appoggio per la rete di Petri.

Gli altri controlli effettuati sugli ordini dall’automa dei prelievi saranno approfonditi nel paragrafo 6.3.

6.3 Fase 2: Elaborazione e generazione delle liste di prelievo

L’operatore, come abbiamo visto descrivendo la fase 1, ha inserito i parametri e premuto il pulsante Genera. Dopo ciò, avviene un cambiamento di ambiente di lavoro. È lanciato in automatico, infatti, Renew,l’ambiente di simulazione delle reti di Petri.

L’operatore, allora, deve aprire la rete di Petri contenuta nel file rete.rnw usando il menu FileÆOpen Drawing. Le rete di Petri è visibile nella sua interezza in Fig. 60.

La rete di Petri è il motore logico dell’automa dei prelievi, cioè, ne scandisce l’evoluzione e l’azioni intraprese. Uno dei motivi per cui si è scelto la rete di Petri è la dinamicità che dà al flusso logico di un programma. Infatti se in qualsiasi momento si decidesse di modificare la sequenza di azioni che l’automa deve compiere basterebbe andare a cambiare la rete. La rete non fa altro, infatti, che richiamare una serie di funzioni che compiono azioni elementari.

Queste possono, quindi, essere richiamate in ordine diverso secondo necessità. Cambiare una rete di Petri è molto più intuitivo e veloce che andare ad modificare la procedura principale di un codice tradizionale.

Inoltre, attraverso una rete di Petri, si riescono a gestire in maniera agevole azioni che devono avvenire in parallelo e la loro eventuale sincronizzazione.

La rete dell’automa è stata suddivisa per la sua descrizione in varie sezioni visibili all’interno della Fig. 60. Ad ogni sezione è associato un paragrafo all’interno del quale sono analizzate le parti più interessanti della rete e la logica del codice associato.

L’operatore per attivare la rete di petri deve premere CTRL+R. Questo dà inizio alla simulazione durante la quale è possibile vedere in tempo reale l’evoluzione dei token all’interno della rete.

6.3.1 Sezione di inizializzazione

All’interno della rete circolano token. Per mantenere la rete leggibile si è cercato di far circolare token con dimensioni più piccole possibili. Il tipo scelto per i token è la tupla.

Ogni token rappresenta un ordine cliente da processare e lo definiamo “token base”. In ogni token base sono presenti le seguenti informazioni (fra parentesi tonde indico il nome della variabile associata):

Fig. 59 Tabella LGGALP

(17)

84 Fig. 60 Rete di Petri dell’automa dei prelievi

(18)

85

• Oggetto di collegamento tra Java e la rete di Petri (lo_l2j): Java in questa fase, come già detto, funge da tramite fra le rete di Petri e Oracle per la chiamate delle store procedure.

• Numero dell’elaborazione corrente (oelen)

• Numero dell’ordine cliente all’interno dell’elaborazione (i)

• Eventuali flag di controllo

Le informazioni suddette per costruire il token base sono recuperate all’interno di questa sezione della rete.

L’oggetto di collegamento è un oggetto Java della classe Root del package eu.iacopo.galpesp.rdp. Questa classe contiene, al suo interno, tutte le funzioni Java che sono richiamate dalla rete di Petri. È necessario, quindi, inserirlo nella tupla base e trasportarlo all’interno della rete. La creazione dell’oggetto avviene all’interno della prima transizione della rete.

Successivamente sono recuperati, leggendoli dal file di appoggio, il numero di elaborazione e il numero totale degli ordini da elaborare. Tutto ciò è realizzato con la funzione prendiParametri(indice_del_parametro). L’indice del parametro corrisponde alla riga del file di testo nella quale è memorizzato il parametro da recuperare.

Adesso che abbiamo il numero totale degli ordini lo sfruttiamo per creare tutti i token base attraverso un ciclo. Nel posto Ordini in elaborazione troveremo, quindi, tanti token base quanti sono gli ordini da elaborare.

6.3.2 Sezione logiche di controllo

Tutti gli ordini sotto forma di token sono sottoposti ai seguenti controlli per verificare o meno la loro idoneità all’imballaggio:

• Controllo della saturazione della capienza dell’ubicazione del cliente

• Controllo se l’ordine è a saldo

6.3.2.1 Controllo della saturazione della capienza dell’ubicazione del cliente

Si ricorda che gli articoli immagazzinati nel magazzino P2 sono quelli che sono prodotti su richiesta del cliente e che ad ogni riga di questi ordini è associato un ordine di produzione come illustrato in 2.4.7. Il concetto di ubicazione di magazzino e i relativi problemi sono stati presentati all’interno del paragrafo 1.4.1.2 e 1.5.3.1.1.2.

Si deve, quindi, andare a controllare se gli ordini hanno un numero di paia disponibili all’interno del magazzino P2, tali da superare la soglia di capienza dell’ubicazione.

I token, infatti, attraversano la transizione Calcolo disponibilità paia su richiesta maga P2. Questa è un transizione di azione che richiama la funzione Java controllaDisponibilitàP2(numero_elaborazione, numero_ordine) la quale a sua volta richiama, con gli stessi parametri, la store procedure SP_DISP_P2 contenuta nel package PKG_GALPESP_RUN. Questa store ha il compito di calcolare il numero totale di paia presenti all’interno dell’ubicazione del cliente. Tale numero sarà memorizzato all’interno del campo DISP_P2 della tabella LGGALP, descritta in precedenza.

La store, quindi, compie i seguenti passi:

• Ricava le coordinate dell’ordine dalla tabella LGGALP:

o Causale dell’ordine o Anno dell’ordine

(19)

86 o Numero dell’ordine

a partire dai parametri passati.

• Ricava le righe dell’ordine ancora “aperte” (paia non ancora spedite) che contengono articoli stoccati nel magazzino P2, e scorre su queste con un ciclo. Per ogni riga esegue le seguenti azioni:

o Recupera le coordinate del movimento di magazzino associato alla riga di ordine corrente. Queste sono composte da: causale di magazzino, anno, numero movimento magazzino. Queste coordinate le chiamiamo moma_OC. (ulteriori informazioni riguardo la gestione degli ordini cliente si trovano nel paragrafo 2.4.6)

o Attraverso le moma_OC recupera dalla tabella PROPXORC le coordinate del movimento di magazzino dell’ordine di produzione associato che definiamo moma_OP. Sono recuperate, però, solo le moma_OP degli ordini di produzioni “chiusi”. Si ricorda che un ordine di produzione è “chiuso” se le paia in esso contenute sono state versate a magazzino. Se non ci sono ordini di produzione “chiusi”, significa che le paia dell’ordine non sono presenti nel maga P2 e si salta alla riga successiva. (ulteriori informazioni riguardo la gestione degli ordini produzione si trovano nel paragrafo 2.4.7).

o Una volta note le moma_OP e le moma_OC recupera l’oid dell’articolo da LGMOVMAG e passa a ciclare sulle singole taglie

• Per ogni taglia esegue le seguenti azioni:

o Recupera attraverso le moma_OC dalla tabella BLMOVMAG la quantità ORDINATA per la taglia corrente che chiamiamo qta_oc

o Recupera attraverso le moma_OP dalla tabella BLMOVMAG la quantità PRODOTTA per la taglia corrente che chiamiamo qta_op

o Calcola la quantità di paia ancora da evadere per la taglia corrente attraverso la formula (1).

_ _ _ _ _ 01 _

qta da evadere qta oc qta ldp= − −qta spedita (1)

Dove qta_ldp_01 è la quantità di paia della taglia corrente presenti in liste di prelievo ancora “aperte”. Questa quantità è ricavata dalla tabella T_BLEVAO01 andando a cercare le liste di prelievo che hanno BSTAM pari a 0 o 1. Per i dettagli sulle liste di prelievo si rimanda al paragrafo 2.4.4.

Qta_spedita è la quantità di paia della taglia corrente già spedite. Questa quantità è inclusa nella formula (1) in quanto si deve tenere di conto della possibilità che una riga d’ordine possa essere stata evasa parzialmente. Tale quantità è recuperata sommando le quantità di tutti i movimenti di magazzino moma_OC associati all’ordine cliente corrente vincolando l’articolo, la combinazione colore ecc. a quelli attuali e il campo BCHIUD_DRIGD a ‘S’.

o Calcola le paia disponibili sullo scaffale per la taglia corrente attraverso la formula (2).

_ _ _ _ 01_ 02

qta scaffale qta op qta ldp= − (2)

Dove qta_ldp_01_02 è la quantità di paia presenti in liste di prelievo “aperte”

decurtata delle paia che eventualmente sono state già imballate. (caso di lista di prelievo evasa parzialmente). Questa quantità è ricavata attraverso una query non banale che lavora sulla tabella T_BLEVAO01 e sulla T_BLEVAO02 con

(20)

87 join esterno. Per i dettagli sulle liste di prelievo evase parzialmente si rimanda al paragrafo 2.4.4.2.

o Adesso che abbiamo sia la quantità ordinata che quella presente sugli scaffali si possono verificare due casi: copertura totale dell’ordinato o copertura parziale.

Se la qta_scaffale è maggiore o uguale alla qta_da_evadere siamo nel caso di copertura totale e la store aggiunge alla somma della paia disponibili per l’ordine corrente qta_ordinata. Altrimenti siamo nel caso di copertura parziale e la store aggiunge qta_scaffale

Una volta che il ciclo su tutte le righe dell’ordine è terminato, la store controlla che la disponibilità di paia sia positiva se non lo fosse la pone a zero. Poi, memorizza tale quantità nel campo DISP_P2 della tabella LGGALP per l’ordine cliente corrente.

Una volta calcolata la disponibilità per il magazzino P2 il token avanza nella rete e passa alla transizione Controllo ubicazione nella quale viene verificato se la paia presenti all’interno dell’ubicazione del cliente superino o meno la soglia prefissata. Tutto ciò è realizzato attraverso la funzione controlloUbi(numero_elaborazione, numero_ordine). Questa a sua volta richiama la store procedure SP_FILTRO_CAP_UBI del package PKG_GALPESP_FILTRI. La soglia di saturazione è impostata all’interno della costante soglia_ubi nella store. Il valore impostato è 50 paia.

La store se le paia nell’ubicazione superano la soglia mette a 1 il campo BUBI della tabella LGGALP altrimenti lo mette a 0. Infine, ritorna come parametro OUT il valore di BUBI alla funzione controlloUbi. A sua volta la funzione ritorna tale valore alla rete di Petri la quale lo memorizza all’interno della variabile li_ubi.

I token, poi, sono divisi in base all’esito del test sulla saturazione dell’ubicazione: infatti sono presenti due transizioni di guardia sul valore assunto da li_ubi. Troveremo, quindi, i token degli ordini che devono essere imballati, a causa ubicazione saturata, nel posto Ubicazione piena mentre gli altri nel posto Ubicazione non piena.

I token in Ubicazione non piena continuano il loro percorso verso il secondo controllo:

verifica se l’ordine è a saldo. Gli altri passano direttamente alla generazione delle lista di prelievo.

6.3.2.1.1 Native Dynamic SQL

All’interno della reti di Petri è necessario, molto spesso, andare a leggere o aggiornare il valore di una particolare colonna della tabella LGGALP. Poiché mi sembrava poco efficiente e tedioso andare a creare tante funzioni di lettura e tante di aggiornamento quante sono le colonne di LGGALP ho preferito andare a sfruttare una delle caratteristiche più recenti di Oracle: le Native Dynamic SQL (NDS).

Le NDS sono istruzioni dinamiche cioè il cui valore è valutato a tempo di esecuzione. Una NDS è costituita da una stringa di tipo varchar2, come nel caso delle query standard, ma all’interno di questa stringa si possono inserire delle variabili. Queste variabili sono più potenti dei parametri di una query standard: infatti, nel caso della query standard, un parametro può prendere solo il valore di un “right value”. Per “right value” si intende un valore che compare a destra dell’operatore =. Invece, queste variabili si possono sostituire a qualunque parte di un comando SQL.

Nell’automa di prelievi si è fatto uso delle NDS per passare alla query come variabile il nome della colonna che deve essere letta o aggiornata. Un esempio di NDS è proprio la store spu_update_col_flag_lggalp che è stata utilizzata nel paragrafo precedente per aggiornare il valore del campo DISP_P2 della tabella LGGALP. Il codice è riportato nel Listato 21.

(21)

88

create or replace procedure spu_update_col_flag_lggalp(

pi_oelen in number, ps_csoci in varchar2,

ps_ccaus_sdocu in varchar2, pi_ycale in number,

pi_nprot_ddocu in number, ps_col in varchar2,

pi_flag in integer) is nds_update varchar2(500);

begin

nds_update:= 'update lggalp lp set '|| ps_col ||'='|| pi_flag ||

' where lp.csoci= ''' ||ps_csoci|| ''' ' ||

' and lp.oelen='||pi_oelen||

' and lp.nprot_ddocu='||pi_nprot_ddocu||

' and lp.ccaus_sdocu=''' ||ps_ccaus_sdocu|| ''' ' ||

' and lp.ycale='||pi_ycale;

execute immediate nds_update;

end spu_update_col_flag_lggalp;

Nel codice la variabile nds_update contiene la NDS. La variabile ps_col è la colonna da aggiornare e pi_flag il valore da inserire. I simboli || sono l’operatore in PL/SQL per la concatenazione delle stringhe.

Un esempio di chiamata di tale store è il seguente:

spu_update_col_flag_lggalp(pi_oelen,ls_csoci,ls_ccaus_sdocu,li_ycale, li_nprot_ddocu,'BUBI',1);

nel quale viene messo a 1 il campo della colonna BUBI.

6.3.2.2 Controllo se l’ordine è a saldo

Se un ordine non ha passato il controllo sulla saturazione dell’ubicazione è sottoposto ad un’ulteriore verifica. Questa consiste nel controllare se, prese tutte le paia attualmente disponibili nei vari magazzini, si riesce con queste a saldare l’ordine. Se ciò è possibile l’ordine è idoneo all’imballaggio e sarà generata la relativa lista di prelievo.

Una informazione necessaria, quindi, per questo controllo è la disponibilità delle paia, per un certo ordine, su tutti e tre i magazzini del prodotto finito P1, P2 e P3.

Per quanto riguarda il magazzino P2 la disponibilità è stata già calcolata durante il controllo sulla saturazione dell’ubicazione e memorizzata nel campo DISP_P2 della tabella LGGALP.

Rimangono, quindi, da calcolare le altre due. La descrizione delle azioni necessarie al calcolo sono illustrate nei paragrafi successivi.

In virtù della necessità di queste informazioni nella transizione Controllo se ordine è saldato sono state inserite due inscrizioni: una di azione e una di guardia.

La transizione di azione ha il compito di richiamare la funzione controlloSaldo(numero_di_elaborazione, numero_di_ordine) che esegue il controllo vero e proprio.

Listato 21 Esempio di una Native Dynamic SQL

(22)

89 L’inscrizione di guardia, invece, blocca lo scatto della transizione e quindi l’esecuzione della funzione fino a quando non è disponibile l’informazione sulla disponibilità del magazzino P1.

La funzione controllaSaldo, una volta abilitata, chiama la store procedure SP_FILTRO_SALD del package PKG_GALPESP_FILTRI.

La store ha il compito di controllare se l’ordine è a saldo. Un ordine è a saldo se la disponibilità totale coincide con il numero di paia che devono essere ancora evase. Per fare ciò , quindi, la store compie le seguenti azioni:

• Ricava le coordinate dell’ordine dalla tabella LGGALP:

o Causale dell’ordine o Anno dell’ordine o Numero dell’ordine a partire dai parametri passati

• Calcola la disponibilità totale sommando quelle dei singoli magazzini prodotti finiti così ricavate:

o Magazzino P1: calcola le paia di “classico” che sono state impegnate per l’ordine corrente. Cioè, va a sommare tutte le paia attribuite nella tabella LGGALPIMPE filtrando sull’ordine corrente. (per i dettagli sull’impegno delle paia “classiche” si rimanda al 6.3.2.2.1).

o Magazzino P2: recupera dalla tabella LGGALP il valore del campo DISP_P2 per l’ordine corrente

o Magazzino P3: recupera dalla tabella LGGALP il valore del campo DISP_P3 per l’ordine corrente (per i dettagli sul calcolo di DISP_P3 si rimanda a 6.3.2.2.2).

• Calcola il numero di paia ancora da evadere secondo la formula (5):

_ _ _ _ ( _ _ _ _ ) ( _ _ _ _ ) _

paia da evadere=pa tot ord pa sp cla pa sp fant+ pa sc cla pa sc fant+ pa ldp (5) Dove pa_tot_ord è il numero di paia totale presenti nell’ordine corrente. Questa quantità è ricavata dalla tabella A_LGPOORCL dal campo QMOMA. Si ricorda che la tabella A_LGPOORCL racchiude i dati del portafoglio ordini clienti.

Pa_sp_cla e pa_sped_fant sono il numero di paia spedite rispettivamente di articoli classici e fantasia. Queste informazioni sono recuperate dalla tabella A_LGPOORCL dai campi QCONS_DDOCU (paia classiche) e QSTRN_DDOCU (paia fantasia).

Pa_sc_cla e pa_sc_fant sono il numero di paia già imballate ma non ancora spedite rispettivamente di articoli classici e fantasia. Queste informazioni sono recuperate dalla tabella A_LGPOORCL dai campi QRICE (paia classiche) e QFATT (paia fantasia).

Pa_ldp è il numero di paia presenti in liste di prelievo. Questa quantità è recuperata dalla tabella A_LGPOORCL dal campo QLANC.

• Verifica se paia_da_evadere è uguale alla disponibilità totale. Se è così mette a 1 il campo BSALD della tabella LGGALP. Altrimenti lo mette a 0. Se la disponibilità totale fosse nulla mette il flag BSALD a zero. Questo ulteriore controllo è necessario in tutti quei casi che non c’è niente da evadere e la disponibilità è nulla.

• Ritorna alla funzione Java il valore BSALD memorizzato.

(23)

90 La fuzione Java ricevuto il valore di BSALD dalla store lo ritorna alla rete di Petri la quale lo memorizza all’interno della variabile li_sald. I token, quindi, sono smistati attraverso due transizioni di guardia in base al valore di li_sald: se li_sald è 1 allora il token sarà posizionato nel posto Ordini saldati se invece li_sald è 0 sarà posizionato nel posto Ordini non saldati.

6.3.2.2.1 Disponibilità magazzino P1

Il magazzino P1, si ricorda, ha una politica di stoccaggio ad articolo e non a ubicazione cliente come il magazzino P2. Il magazzino P1 contiene gli articoli “classici”. Per una panoramica più esauriente su questi magazzini si rimanda al paragrafo 1.4.

La disponibilità del magazzino P1 necessita di un approccio completamente diverso rispetto al calcolo relativo ai magazzini P2 e P3. Questo perchè le paia di articoli “classici”, come già detto, sono prodotti a “scorta” o su proiezione di vendita e perciò non sono assegnate ad un particolare cliente. Attualmente, in azienda, non è presente un meccanismo di assegnazione di queste paia. Infatti si procede all’attribuzione nel momento in cui è generata la lista di prelievo del cliente. Quindi le paia di “classico” sono assegnate cliente per cliente e non con una logica “globale”.

Si è ritenuto che questo processo non fosse corretto e che portasse alla generazione di errori nell’attribuzione di queste paia con conseguenze sulla produttività del reparto magazzino. Si è deciso quindi di creare una modalità di attribuzione che rispetti le direttive commerciali dell’azienda.

L’attribuzione deve avvenire secondo i seguenti criteri:

• data di consegna crescente

• A parità di data di data di consegna si segue l’ordine di priorità del cliente I dettagli dell’attribuzione sono descritti all’interno dei prossimi paragrafi.

6.3.2.2.1.1 Gestione delle priorità delle spedizioni

Attualmente in azienda non vi è alcuna gestione delle priorità delle spedizioni. Tutti i clienti, infatti, hanno la stessa priorità in fase di imballaggio e spedizione. Si è ritenuto opportuno,invece, creare delle categorie di priorità per le varie tipologie di clienti.

Sono state definite sei classi di priorità contraddistinte da un valore intero compreso tra 0 e 5. Il valore 0 contraddistingue la priorità minima che è quella predefinita per tutti i clienti. Il valore 5 è assegnata ai clienti più importanti e che hanno quindi priorità massima in fase di spedizione.

La classe di priorità è stata implementata all’interno del database andando a sfruttare un campo attualmente inutilizzato dell’anagrafica clienti. In questo modo l’assegnazione delle priorità da parte del reparto commerciale sarà gestita all’interno dell’anagrafica cliente già esistente nel BMS.

Il campo scelto è CCATE_SCLIE_I ed appartiene alla tabella ANINDCLI. Questa è la tabella dove sono memorizzate tutte le informazioni sugli indirizzi di spedizione. È un tabella che ammette la gestione della storicità di un dato quindi, in tutte le query su questa tabella, si deve fare attenzione all’utilizzo del campo BSTOR.

Il gestionale ammette la presenza di indirizzi di spedizione multipli per un singolo cliente:

questo causa la presenza di più record all’interno della tabella ANINDICLI. Tali record hanno tutti lo stesso codice cliente (C_ANAG_SCLIE). Ogni indirizzo di spedizione è identificato dal campo CINDI_DCLIE che è un contatore progressivo. Per convenzione si è scelto di assegnare la priorità di spedizione del cliente al primo indirizzo cioè quello con CINDI_DCLIE uguale a 1.

(24)

91 6.3.2.2.1.2 Implementazione dell’attribuzione articoli “classici”

All’interno della rete di Petri l’attribuzione degli articoli classici è lanciata dalla transizione Attribuzione “classico”. Questa transizione è messa in parallelo al resto della rete in quanto è un operazione che rimane svincolata dai rimanenti controlli che la rete deve fare. In questo modo sfruttiamo la potenzialità delle reti di Petri di gestire attività in parallelo: infatti, mentre i token base dei vari ordini sono sottoposti al controllo di disponibilità e a quello dell’ubicazione, la rete lancia l’operazione di attribuzione del classico. Tutto ciò avvantaggia i tempi di elaborazione.

La transizione lancia la funzione attribuzioneComm(numero_elaborazione) la quale restituisce il valore 1 quando l’attribuzione è terminata.

Tale funzione contiene, come parametro, solo il numero di elaborazione: infatti l’assegnazione del “classico” coinvolge tutti gli ordini presenti all’interno dell’elaborazione corrente. La funzione richiama la store procedure SP_ATT_COMM del package PKG_GALPESP_ATT_COMM. Tale store compie i seguenti passi:

• Ricava, attraverso il cursore riportato nel Listato 22, l’elenco di tutti gli ordini disposti secondo i criteri sovra esposti. Ogni ordine dell’elenco è composto dalle due coordinate del token base: numero_di_elaborazione (li_oelen) e dal numero_di_ordine (nprog_ielen).

cursor lc_oc_elab is select lg.oelen, lg.nprog_ielen from lggalp lg,

anclient an, anindcli indi

where lg.oelen=pi_oelen and --join con anclient

an.csoci = lg.csoci and an.ncocg = lg.ncocg and an.bstor = 'A' and --join con anindiclie

indi.csoci = an.csoci and indi.canag_sclie = an.canag_sclie and

--nel caso di multi indirizzo il flag viene messo sul primo indirizzo

indi.cindi_dclie = 1 and indi.bstor = 'A'

order by lg.dscad_drigd asc, indi.ccate_sclie_i desc; -- priorità

• Per ogni ordine così ricavato lancia la store procedure SP_ATT_COMM_OC che ha il compito di attribuire le paia di classico all’ordine passato. La store prende i seguenti parametri:

o Numero di elaborazione o Numero di ordine

o Progressivo di attribuzione: è un contatore progressivo che identifica l’ordine con cui viene effettuata l’attribuzione. Questo è utile per la fase di analisi dei dati. A posteriori, infatti, possiamo sapere quale ordine cliente ha avuto l’attribuzione prima degli altri.

Listato 22 Cursore degli ordini per l’attribuzione del classico

(25)

92 o Flag di attribuzione (parametro con modalità OUT): tale flag è messo a 1 se

sono state attribuite paia all’ordine corrente altrimenti è messo a 0

I passi compiuti dalla store procedure SP_ATT_COMM_OC sono:

• Ricava le coordinate dell’ordine dalla tabella LGGALP:

o Causale dell’ordine o Anno dell’ordine o Numero dell’ordine a partire dai parametri passati.

• Ricava le righe dell’ordine ancora “aperte” (paia non ancora spedite) che contengono articoli stoccati nel magazzino P1, e scorre su queste con un ciclo. Per ogni riga esegue le seguenti azioni:

o Recupera le coordinate del movimento di magazzino associato alla riga di ordine corrente. Queste sono composte da: causale di magazzino, anno, numero movimento magazzino. Queste coordinate le chiamiamo moma_OC. (ulteriori informazioni riguardo la gestione degli ordini cliente si trovano nel paragrafo 2.4.6)

o Note le moma_OC recupera l’oid dell’articolo da LGMOVMAG e la quantità ordinata per ogni taglia, che chiamiamo qta_ord_taglia e passa a ciclare sulle singole taglie

• Per ogni taglia esegue le seguenti azioni:

o Chiama la store procedure SP_DISP_P1_RIGA che calcola il numero di paia per la taglia corrente che possono essere attribuite all’ordine attuale. Tale quantità la chiamiamo paia_att. Per i dettagli di questa funzione si veda il paragrafo 6.3.2.2.1.3.

o Se paia_att è pari a 0 significa che per quella particolare taglia non erano disponibili paia per l’attribuzione e allora si salta alla taglia successiva.

Altrimenti si attribuisce tale numero di paia all’ordine corrente. Ho, però due possibilità: l’attribuzione copre totalmente la quantità nella riga di ordine o le paia disponibili coprono solo parzialmente la riga d’ordine. Nel primo caso paia_att è maggiore o uguale a qta_ord_taglia e si attribuiscono qta_ord_taglia.

Nel secondo, che è il caso opposto, si attribuiscono paia_att.

o Una volta che è stato individuato il numero di paia da attribuire si procede alla registrazione di tale impegno. Per tale scopo è stata creata una apposita tabella LGGALPIMPE la cui struttura è presentata in Fig. 61. La store va a popolare un intero record della tabella per la taglia corrente.

Name Type Nullable Descrizione

OELEN NUMBER N Numero di elaborazione

NPROG_IELEN NUMBER N Numero ordine

CSOCI VARCHAR2(6) N Codice della società

CANAG_SCLIE NUMBER N Oid codice cliente (ANCLIENT) CCAUS_SDOCU VARCHAR2(6) N Causale dell’ordine cliente

YCALE NUMBER N Anno inserimento ordine

(26)

93 NPROT_DDOCU NUMBER N Numero dell’ordine cliente

CTIPO_DMAGA VARCHAR2(6) N Tipo del magazzino

CMAGA VARCHAR2(6) N Codice del magazzino

OARTI NUMBER N Oid dell’articolo (LGARTMAG)

CCOLO VARCHAR2(6) N Codice Combinazione colore

CFOND VARCHAR2(6) N Codice del fondo

CFORM VARCHAR2(6) N Codice della forma

NTAGL NUMBER N Oid della taglia (BLTAGLIE)

DAGGI DATE N Data di elaborazione

QATT NUMBER N Paia attribuite

NPROG_ATT INTEGER N Progressivo di attribuzione NRIGA_DDOCU INTEGER Y numero della riga ordine cliente

CIMBA VARCHAR2(10) Y codice dell'imballo

o Aggiunge la quantità attribuita al totale per l’ordine corrente.

• Una volta aver ciclato su tutte le righe controlla se il totale delle paia attribuite per tutto l’ordine è maggiore di 0 allora mette il flag di attribuzione (flag_att) a 1 altrimenti lo mette a 0

6.3.2.2.1.3 SP_DISP_P1_RIGA: calcolo della quantità ancora da evadere

Questa store procedure rende il numero delle paia che possono essere attribuite alla taglia passata. I parametri della store sono:

• Codice della società

• Causale del movimento di magazzino

• Anno del movimento di magazzino

• Numero del movimento di magazzino

• Taglia

• Numero dell’elaborazione

• Numero dell’ordine

• Paia che si possono attribuire (modalità OUT) (paia_att) La store compie le seguenti azioni:

• Richiama la funzione BL_FNC_QRESI_DRIGD, già presente nel gestionale, che ritorna il numero della paia della taglia corrente che devono essere ancora evase.

Tale quantità la chiamiamo paia_devad. Se tale quantità è nulla setta il valore paia_att a 0 e ritorna (non c’è niente da dover attribuire).

• Ricava le coordinate dell’ordine dalla tabella LGGALP:

o Causale dell’ordine o Anno dell’ordine o Numero dell’ordine a partire dai parametri passati.

Fig. 61 Tabella LGGALPIMPE

Riferimenti

Documenti correlati

int power( int numero, int esponente){intrisultato=1;for(inti=1;i&lt;=esponente ;i++){risultato=risultato*numero;}returnrisultato;} risultato=risultato *numero i&lt;=esponente inti

Rather, what would be required of the Court is an assessment of the adequacy of the means used to reach a given end: was Community action really necessary to

Questa condizione è messa in risalto, come si accennava prima, innanzitutto dalla conformazione degli spazi: gli slum sono generalmente situati nelle strisce periurbane e

The latest figures for the ICI correspond to August, and the updated forecasts point to a gradual fall in the confidence of economic agents in the industrial sector’s evolution

The Kalman filter provides a well-established procedure to compute the likelihood of a time series which is the outcome of a stationary Autoregressive Moving Average (ARMA)

The most im portant branch is th at of finished consumer goods (garments, leather, printing) and the food industry (including drinks and tobacco).. Since another

Hirohito comunque non cedette subito alle pressioni per entrare in guerra, ma per paura di un colpo di Stato e a causa della sua lealtà alla Costituzione

The balanced growth path BGP is defined as a state of the economy in which aggregate productivity, consumption and output grow at a constant rate g, aggregate prices decrease at