Gestione documentaria con Automation (esempio didattico)
Il trattamento di documenti, in particolare di quelli creati con Microsoft Word sta assumendo un’importanza crescente nelle aziende. Senza ricorrere a particolari (e costosi!) software, due sono i mezzi possibili:
· Utilizzo del formato Open XML;
· Utilizzo della tecnologia (OLE) Automation
Il primo ha il vantaggio di funzionare anche su macchine ove Word non è installato, tuttavia NON è in grado di gestire il formato .doc delle versioni di Word precedenti la 2007 o la prossima 2010. In attesa che Open XML si affermi totalmente consigliamo il secondo che da tale limitazione non è affetto.
Purtroppo neanche con Automation sono tutte rose e fiorellini, in quanto non solo può presentare rallentamenti con archivi corposi, ma perché la libreria specifica è offerta in più versioni, cosa che ne complica la diffusione (deployment) su PC dotati di edizioni eterogenee (v. più avanti). Per questo motivo, ossia per evitare di essere disturbato con chiarimenti & lagnanze da parte dei meno esperti, non fornisco un modellino bell’e pronto bensì le (semplici) indicazioni per farselo da soli, unitamente - ci mancherebbe - ai necessari listati VBA.
L’idea base: un minimo di strutturazione
I file Office differiscono nettamente dalle tabelle dei database per la loro natura libera, fin troppo. In tali condizioni pretendere di gestire documenti “qualsiasi” è utopistico, eccezion fatta per le ricerche di stringhe e simili, per le quali esiste già una varietà di strumenti più o meno sofisticati. Per dare un senso più “aziendale” alla faccenda mi pare indispensabile prevedere un minimo di struttura nei documenti di un certo tipo, auspicabilmente creati a partire da tipici template (file di estensione .dot).
A tale scopo ho, anche per una semplificazione didattica ho previsto la presenza di due caratteristiche:
a) un Oggetto, precisamente a seguito di una stringa “OGGETTO: “ e fino al primo carattere CR (Carriage Return) di codice ascii 13;
b) una TABELLA contenente i dati rilevanti di quel certo documento (fattura, bolla o qualunque altra cosa).
Inoltre, sempre per semplicità, ho ipotizzato che tutti i .doc (o docx) di quel certo tipo risiedano nella stessa directory che ospita anche il modello Excel che li elabora e siano del tipo seguente:
Primo Documento
OGGETTO: Questo e quello per me pari sono.
|
CODICE |
Descrizione |
Quantità |
Prezzo |
|
AA |
Mele |
120 |
2 |
|
BB |
Pere |
150 |
3 |
Resto del documento...
Secondo Documento
OGGETTO: Quanto è bella giovinezza, che si fugge tuttavia.
|
CODICE |
Descrizione |
Quantità |
Prezzo |
|
AA |
Mele |
100 |
2 |
|
BB |
Pere |
90 |
3 |
|
AA |
Mele |
120 |
2 |
Resto del documento...
Eccetera.
A sua volta il modellino Excel proposto ha grossomodo l’aspetto seguente (sulla destra viene emulato un pulsante ActiveX etichettato, in modo eloquente, “Inserisci l’Oggetto”):
|
|
A |
B |
C |
D |
E |
F |
|
1 |
Documento Word |
Oggetto |
|
|
|
|
|
2 |
Primo documento.doc |
|
|
|
|
|
|
3 |
Secondo documento.doc |
|
|
Inserisci l'Oggetto |
|
|
4 |
Terzo documento.doc |
|
|
|
|
|
|
5 |
Quarto documento.doc |
|
|
|
|
|
|
6 |
Quinto documento.doc |
|
|
|
|
|
|
7 |
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
9 |
|
|
|
|
|
|
|
10 |
|
|
|
|
|
|
Nota Sempre per semplicità e... pigrizia i nomi dai vari documenti sono già scritti in colonna A, da A2 in giù. Il codice VBA per caricarli – ad es. con un controllo Common Dialog – è lasciato per esercizio.
Per fruire delle macro che descriveremo è necessario che, nell’Editor VBA, vengano fissati, previa scelta dal menu Strumenti del comando Riferimenti... i riferimenti seguenti:
ü Visual Basic For Application (ovviamente!)
ü OLE Automation (indispensabile)
ü Microsoft Excel ** Object Library
ü Microsoft Word ** Object Library
I due asterischi (**) stanno per il numero di versione, che è 11 con MS Office 2003 e 12 con MS Office 2007. Di qui la preannunciata necessità di inserire la libreria corretta a seconda della versione installata su questo o quel PC.
Nota Un certo MioModello.xls nato, putacaso, sotto Excel 2007 se caricato in Excel 2003 esibirebbe un’indicazione di non disponibilità alla sinistra del riferimento Microsoft Word 12 Object Library. Il rimedio al malfunzionamento delle macro è ovvio (sostituire 12 con 11) ma, ahimè, manuale.
Le macro per inserire i vari Oggetto
Premesso che tutte le routine si possono (ma non obbligatoriamente) scrivere nel modulo Foglio1. Ecco la prima tranche di codice finalizzata all’inserimento dell’Oggetto in colonna B, ovvero accanto a ciascun nome di documento.
La prima coppia di routine ha avuto scopo sperimentale:
Sub ApriDoc(Docum As String)
Dim QuestaDir As String
QuestaDir = ThisWorkbook.Path & "\"
Dim MioDoc As Document
Set MioDoc = GetObject(QuestaDir & Docum)
Dim ParagrOggetto As String
With MioDoc
ParagrOggetto = .Paragraphs(2).Range.Text
End With
ParagrOggetto = Replace(ParagrOggetto, "OGGETTO: ", "")
MsgBox ParagrOggetto
MioDoc.Parent.Application.Quit
Set MioDoc = Nothing
End Sub
Sub ProvaApriDoc()
ApriDoc "Secondo documento.doc"
End Sub
Commenti essenziali. Diciamo solo che QuestaDir = ThisWorkbook.Path & "\" determina la directory che per ipotesi accomuna il modello Excel e i vari archivi .doc. Subito dopo interviene Automation con il codice GetObject applicato al file QuestaDir & Docum che palesemente fornisce il pathname completo del file Docum passato come argomento. Quanto a ParagrOggetto in questa prima battuta lo si prende pari al secondo paragrafo, Paragraphs(2), del Docum, una regola un po' troppo restrittiva che comunque “Secondo documento.doc” dovrà rispettare.
La routine termina eliminando con Replace della stringa inziale “OGGETTO: “ da tale ParagrafOggetto in modo che il solo contenuto dell’Oggetto venga enunciato dalla MsgBox. Si notino infine due importanti istruzioni. La prima, con Miodoc.Parent punta al babbo del documento, che poi è l’applicativo Word che agisce nell’ombra. Dal quale si deve, tassativamente!, uscire con .Application.Quit.
Nota In assenza di tale istruzione si incappa in inattese quanto fastidiose comparse di Word, che reclama la propria chiusura. I più esperti se la cavano obbedendo all’imperativo tornando regolarmente a Excel, ma la situazione non è delle più felici...
La seconda istruzione, Set MioDoc = Nothing libera l’oggetto incorporato in Miodoc.
Dopo le precedenti lungaggini lascio interamente all’esegesi autonoma dei più o meno esperti le routine affidate alla fin della fiera al pulsante CommandButton1, il cui effetto è quello già detto di affiancare tutte le celle (contigue) a partire da A2 con gli Oggetto di ciascun file .doc elencato. Anche il caso di Oggetto mancante è previsto, come si può constatare.
Function OggettoDocum(ByRef Doc As Document) As String
Dim Ogg As String, PosOggetto As Integer
On Error GoTo Fine
Ogg = "OGGETTO:"
With Doc
For Each Paragr In .Paragraphs
PosOggetto = InStr(1, UCase(Paragr.Range), "OGGETTO:")
If PosOggetto > 0 Then Exit For
Next
End With
If PosOggetto = 0 Then
OggettoDocum = "OGGETTO ASSENTE!"
Else
OggettoDocum = Replace(Paragr.Range, "OGGETTO: ", "")
End If
Doc.Close
Fine:
End Function
Sub InserisciOggetto()
Dim ZonaDocum As Range, CellaDoc As Range
Dim QuestaDir As String
On Error GoTo Fine
QuestaDir = ThisWorkbook.Path & "\"
With Range("A2")
Set ZonaDocum = Range(.Cells(1), .End(xlDown))
End With
Dim MioDoc As Document
Dim NumDoc As Integer, i As Integer
NumDoc = ZonaDocum.Count
For i = 1 To NumDoc 'Each CellaDoc In ZonaDocum
Set MioDoc = GetObject(QuestaDir & ZonaDocum(i)) 'CellaDoc.Value)
ZonaDocum(i, 2) = OggettoDocum(Doc:=MioDoc)
If i = NumDoc Then
MioDoc.Parent.Application.Quit ' Chiudi Word!
End If
Next
Fine:
End Sub
Private Sub CommandButton1_Click()
InserisciOggetto
End Sub
Le macro per inserire la tabella
Si affidano all’evento BeforeDoubleClick del modulo Foglio1. Prima del codice poco più avanti riportato vanno fatte alcune premesse:
- la cella a partire dalla quale si vuol inserire la tabella prelevata da ciascun .doc dell’elenco in colonna A va denominata “IniTab”; potrebbe essere la cella D7 ma l’importante è che sia circondata da tutte celle vuote almeno all’apertura del modello Excel;
- La tabella è la prima dell’insieme Tables del documento, ovvero MioDoc.Tables(1);
- È prevista una forma (Shape) di tipo rettangolo, di nome, diciamo, “RettangoloTab” incollata suppergiù sopra la cella “IniTab”, nella quale si possano inserire testi indicativi del tipo “Tabella di Terzo documento.doc” mediante la proprietà
Me.Shapes(“RettangoloTab”).TextFrame.Characters.Text
Ma ecco la routine base di inserimento tabella dotata di argomenti Docum (il documento) e Cart (la cartella ove giace: una prece +), con la sua brava Sub di prova:
Sub InserisciTabella(Docum As String, Cart As String)
Dim MioDoc As Document, Tabella As Table
Dim Nrig As Integer, Ncol As Integer
Dim i As Integer, j As Integer
On Error GoTo Fine
Set MioDoc = GetObject(Cart & Docum)
If MioDoc.Tables.Count = 0 Then
MsgBox "TABELLA ASSENTE!"
Me.Shapes("RettangoloTab").TextFrame.Characters.Text = ""
Else
Set Tabella = MioDoc.Tables(1)
With Tabella.Range
Nrig = .Rows.Count
Ncol = .Columns.Count
.Copy
End With
Me.Shapes("RettangoloTab").TextFrame.Characters.Text = _
"Tabella di " & Docum
Dim ValCella As String
With Range("IniTab")
.CurrentRegion.ClearContents
'.PasteSpecial ' Creerebbe una tabella EMBEDDED del documento
For i = 1 To Ncol
For j = 1 To Nrig
ValCella = Tabella.Columns(i).Cells(j).Range
ValCella = Left(ValCella, Len(ValCella) - 1)
.Cells(j, i) = ValCella
Next
Next
End With
End If
Fine:
MioDoc.Parent.Application.Quit 'MioDoc.Close dava ERRORI!
Set MioDoc = Nothing
End Sub
Sub ProvaInserisciTabella()
Dim QuestaDir As String
QuestaDir = ThisWorkbook.Path & "\"
InserisciTabella "Primo documento.doc", QuestaDir
End Sub
I commenti? Lasciati quasi interamente alla pazienza di chi legge, al quale ricordo solo che Range(“IniTab”).CurrentRegion individua tutte le celle “attorno” alla “IniTab” – corrisponde alla manovra manuale Ctrl+* (asterisco), di qui l’importanza dell’ultima osservazione al riguardo – permettendo di cancellare l’eventuale tabella precedente con ClearContents. Viene lasciato al comprendonio di Chicche and Sia pure il doppio ciclo iterativo di indici i e j che spazzola le colonne della tabella e le celle di ciascuna riversandone via via i contenuti nelle celle del foglio di lavoro che “partono” da Range(“IniTab”) con Range(“IniTab”).Cells(i, j).
Nota La Left(ValCella, Len(ValCell) – 1 si rende necessaria per eliminare il carattere speciale all’estrema destra del contenuto effettivo di una cella di tabella Word.
Concludo riportando senza particolari osservazioni la routine dell’evento che si scatena quando si dà un doppio clic su una cella Target, salvo ricordare che Cancel = True evita che il cursore resti in tale cella:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim QuestaDir As String
QuestaDir = ThisWorkbook.Path & "\"
Cancel = True
If Target = "" Or Target.Address = "$A$1" Then Exit Sub
If Target.Column <> 1 Then Exit Sub
InserisciTabella Target.Text, QuestaDir
End Sub
?>