Ho realizzato un progetto di esempio per Instant Developer Foundation che implementa sul mitico database Northwind una gestione generalizzata di salvataggio delle modifiche della versione precedente del record o della catena di record in caso di master-detail.
L’esempio lo potete scaricare qui: Log Modifiche Documenti.zip (1,4 MB)
Naturalmente questa cosa è facilmente
realizzabile utilizzando i documenti di Instant Developer Foundation, gli eventi globali e alcune caratteristiche del framework.
Vi racconto un po’ come funziona.
Per prima cosa occorre avere un posto dove memorizzare le versioni precedenti un aggiornamento di record di una tabella e nel database Northwind ho creato la tabella Logs con i seguenti campi:
- ID - la chiave primaria
- Document - Nome del documento
- Date Mod - Data e ora fino alla quale il documento aveva la forma qui ssalvata
- Doc DNA - La chiave univoca del documento
- Record - qui salviamo in formato JSON il documento prima della modifica
Al salvataggio dei documenti andremo a scrivere in questa tabella la versione prima della modifica e lo facciamo utilizzando l’vento GlobalBeforeSave dei documenti.
Il codice nell’evento utilizza la reflection di Instant Developer Foundation per analizzare la struttura del documento ed andare a prendersi i valori precedenti dei campi e richiamare il metodo CreaLog().
Le strutture padre-figlio scrivono un unico record sulla tabella Logs con tutto il documento comprensivo dei due livelli come si vede nel codice qui sotto (occorre modificarlo per renderlo multilivello).
// Se sono in modifica e non sto salvado un documento di classe Log procedo
if (Phase == 2 && Doc.updated && Doc.typeName() != "Log")
{
// Disabilito il log singolo dei documenti figli di una struttura padre figlio (viene fatto nel documento principale)
IDDocumentStructure ds = Doc.getStructure()
for (int i = 1; i <= ds.getCollectionCount(); i = i + 1)
{
IDCollection coll of IDDocument = (IDCollection)Doc.getCollection(i)
for each IDDocument docColl in coll
{
docColl.setTag(NOLOGCHILD, true)
}
}
boolean noLog = Doc.getTag(NOLOGCHILD)
if (noLog == false)
{
if (!(Log.CreaLog(Doc)))
{
// Se non riesco a creare il log potrei annullare il salvataggio impostando Cancel = true in questo blocco di codice
}
}
}
Il metodo statico CreaLog del documento Log recupera i valori originali e salva il tutto nella tabella Logs.
boolean esito = false
IDDocument docOriginal = Log.RecuperaValoriOriginali(Doc)
string docXml = docOriginal.saveToXML(false, "", 9999, JSON, ...)
Log l = new()
l.init()
l.Document = docOriginal.typeName()
l.DateMod = now()
l.DocDNA = docOriginal.getDNA()
l.Record = docXml
esito = l.saveToDB(...)
return esito
Non sto a descrivere tutti metodi che sono presenti nel progetto ma era giusto per farvi vedere al volo che si possono fare cose simpatiche con la reflection di Instant Developer Foundation.
Comunque in questo modo abbiamo che quando un documento viene salvato causa una modifica viene scritto un record sulla tabella Logs.
Per il momento non è stata gestita la cancellazione di un record.
Vediamo ora come possiamo far vedere queste modifiche.
Per prima cosa ho creato una videata che visualizza i record del documento Log (LogsDoc):
Il contenuto del record prima della modifica è nel riquadro rosso ed è in formato JSON che è di poco aiuto per gli nostri dell’applicativo, ma in esso ci sono tutti i dati che ci occorrono per visualizzare un record in una videata.
Come facciamo a visualizzare correttamente questi dati?
Per ogni documento ho creato una videata apposita con il solo dettaglio e in sola visualizzazione così da avere un posto specifico dove visualizzare ogni singolo documento.
Nella videata LogsDoc è stato gestito l’evento l’evento OnActivatingRow che riporta questo codice:
Log l = Logs.document
if (l != null)
{
IDDocument doc = IDDocument.getFromDNA(l.DocDNA, ...)
doc.loadFromXML(l.Record, false, JSON, ...)
doc.show(Popup)
if (this.RichiamataDaMenu == false)
{
this.close(...)
}
}
Qui prendo il documento attivo dal pannello e poi instanzio un IDDocument (il documento base da cui ereditano tutti i documenti) e con il metodo getFromDNA() ne creo la struttura in memoria nella variabile doc.
Poi carico i suoi dati dal campo del record di Log preso dal video e a questo punto posso usare il metodo show() del documento che lo porterà a video nella videata di default del documento stesso.
La videata di default di un documento è quella che è stata creata per prima con il drag & drop per quel documento ed è possibile cambiare questa impostazione come spiegato più sotto.
Nel progetto ho creato le videate per visualizzare i dati prima della modifica dei miei documenti e sono quelle che Vis alla fine del nome e sono impostate solo per visualizzare un record in dettaglio.
Cliccando con il tasto destro del mouse su di una videata si vede a quale classe è collegata perché sono presenti i comandi Vai alla classe xxx e Stacca dalla classe xxx.
Per cambiare la videata predefinita si deve trascinare la nuova videata (che vuoi diventi quella predefinita) sul documento tenendo premuto il tasto il tasto SHIFT.
Questo un esempio di videata per visualizzare il record di Log.
Nell’immagine si vede che il campo Shipped Date ha il testo di colore rosso ad indicare che quello è il valore modificato rispetto al documento attuale.
Questa finezza è realizzata dal metodo confrontaDocs della classe DocHelper.
if (doc != null && docPrec != null)
{
IDDocumentStructure ds = doc.getStructure() //
//
for (int i = 1; i <= ds.getPropertyCount(); i = i + 1)
{
string value = doc.getProperty(i)
string valuePrec = docPrec.getProperty(i)
if (value != valuePrec)
{
IDPropertyDefinition pd = ds.getPropertyDefinition(i)
//
for (int k = 0; k < pan.fieldsCount(); k = k + 1)
{
if (pan.getFieldDBCode(k) == pd.DBCode)
{
pan.setFieldTextColor(k, RGBColor(255, 0, 0, ...))
break
}
}
}
}
}
Questo metodo riceve tre parametri:
- doc - la versione attuale del documento
- docPrev - la versione precedente del documento
- pan - il pannello dove viene visualizzato il docPrev
Per ora il metodo confrontaDocs non va a verificare i documenti figli ma lo sistemo quanto prima.
La descrizione di questo esempio non è completa ma almeno con quello che ho scritto vi saprete orientare al suo interno e poi con vostri consigli posso migliorarlo.

