lundi 27 août 2012

Business Objects : Publipostage de PDF à partir d'un seul .rep

Nous avons développé un mécanisme de publication de tableaux de bord à partir d'un sas dans lequel on dépose une collection de fichier, un fichier par "identifiant cible". Ces identifiants peuvent être un code service, un matricule ou tout ce que vous pouvez imaginer comme clé de découpage. Une application tierce consomme ces fichiers à partir de règles de gestion qu'on aura préalablement documentées. Par exemple, on indiquera à l'application consommatrice, qu'elle trouvera dans le sas des documents de suivi budgétaire par service, elle considèrera alors que la structure des noms de fichiers est "CodeService.pdf".

Pour pouvoir alimenter ce sas, nous avons construit une batterie de rapports Business Objects XI R2 (fichiers .rep) dont la section principale est précisément le code qui nous servira de clé de découpage. Nous avons ensuite ajouté une macro qui se déclenche après le rafraichissement et qui va se charger de parcourir le fournisseur de données du rapport qui contient la liste des codes sur lesquels appuyer le découpage.
La macro présentée ci joint part du postula qu'il existe dans le document BO un fournisseur de données sur lequel on va s'appuyer pour générer autant de document PDF qu'il y a d'entrées distinctes d'une variable de ce fournisseur.

L'exemple suivant est celui d'un rapport de suivi budgétaire pour lequel on génère autant de PDF qu'il y a de comptes présents dans les tableaux de bord. Dans cet exemple, les documents contiennent un fournisseur de données "Comptes" (Constante DATA_PROVIDER) dont on va extraire la variable "Compte" (Constante VAR_NAME).
Le principe est ensuite de récupérer la liste des comptes différents puis d'appliquer un filtre global sur la valeur de ce filtre et d'enregistrer le résultat au format PDF avec comme nom "xxxx.pdf" ou "xxxx" est le n° du compte considéré.
La macro s'appuie sur des variables d'environnement :
  • FICHIERS_LOG pour déterminer le répertoire dans lequel sont écrits les logs
  • FICHIERS_PDF pour déposer les fichiers PDF
Avant la génération des PDF, le système vérifie que le sas est vide. Si ce n'est pas le cas, il vérifie que tous les fichiers déjà présents seront écrasés par les nouveaux, sinon, il averti l'utilisateur qu'il risque d'y avoir des incohérences.
Enfin, à la fin du traitement, le système envoie un mail à l'aide de "Postie.exe" pour donner la liste des fichiers générés.


Option Explicit
Dim sLogFile, sLogMsg As String
Dim FilePath, LogFilePath As String
Dim fs As Variant
Const VAR_NAME = "Compte"
Const DATA_PROVIDER = "Comptes"
Const BATCH_FILE = "myBatchFile.bat"
#Const DEBUG_MODE = 0


Private Sub Document_AfterRefresh()

'Déclaration des variables
Dim doc As Document
Dim dp As DataProvider
Dim cols As Columns
Dim col As Column
Dim rep As Report

Dim Compte As String
Dim FullVarName As String

Dim i As Integer
Dim nb_file, nb_pending As Integer

Dim FileName As String
Dim PrevFileName As String
Dim OutputFile

On Error GoTo ErrorHandler

Set fs = CreateObject("Scripting.FileSystemObject")

    ' Récupération de la valeur de la variable d'environnement FICHIERS_LOG
    ' pour générer le chemin d'accès au fichier de log


    LogFilePath = Environ$("FICHIERS_LOG")
    If LogFilePath = "" Then
        LogFilePath = "C:\TEMP\FICHIERS_LOG"
    End If

    ' S'il n'y a pas de "\" à la fin, on l'ajoute
    If Right(LogFilePath, 1) <> "\" Then
        LogFilePath = LogFilePath + "\"
    End If

    ' Création du fichier de log
    sLogFile = LogFilePath & "logfile_" & Format(Now, "YYYYMMDD_HHMM") & ".log"

    fs.CreateTextFile (sLogFile)

    ' Récupération de la valeur de la variable d'environnement FICHIERS_PDF
    ' pour générer le chemin d'accès aux fichiers PDF générés par BO

    FilePath = Environ$("FICHIERS_PDF")
    If FilePath = "" Then
        FilePath = "C:\TEMP\FICHIERS_PDF"
    End If

    ' S'il n y a pas de "\" à la fin, on l'ajoute
    If Right(FilePath, 1) <> "\" Then
        FilePath = FilePath + "\"
    End If

#If DEBUG_MODE Then
Dim vars As Variables
Set vars = Application.Variables
Call LogMessage("DEBUG - Listes des variables d'environnement :")
Call LogMessage("DEBUG ----------------------------------------")
For i = 1 To vars.Count
    Call LogMessage("DEBUG - " & vars.Item(i).Name & " = " & vars.Item(i).Value)
Next
Call LogMessage("DEBUG ----------------------------------------" & vbCrLf)
Call LogMessage("DEBUG - Environ$(FICHIERS_LOG)=" & Environ$("FICHIERS_LOG"))
Call LogMessage("DEBUG - LogFilePath=" & LogFilePath)
Call LogMessage("DEBUG - Environ$(FICHIERS_PDF)=" & Environ$("FICHIERS_PDF"))
Call LogMessage("DEBUG - FilePath=" & FilePath)
#End If

    ' Vérification que le répertoire de dépôt existe
    ' si non, essayer de le créer
    If Not fs.FolderExists(FilePath) Then
        Call LogMessage("INFO - Création du répertoire " & FilePath)fs.CreateFolder (FilePath)

End If
    ' Pour que cette macro fonctionne, on suppose que le document contienne
    ' un fournisseur de données dont le nom est défini dans la constante DATA_PROVIDER
    ' qui est supposé contenir la liste des valeurs à analyser dans un objet
    ' dont le nom est défini dans la constante VAR_NAME

    Set doc = Application.Documents.Item(1)
    Set dp = doc.DataProviders(DATA_PROVIDER)
    Set cols = dp.Columns
    Set col = cols(VAR_NAME)
    Set rep = doc.Reports.Item(1)

    FullVarName = VAR_NAME & "(" & DATA_PROVIDER & ")"

#If DEBUG_MODE Then
Call LogMessage("DEBUG - doc=" & doc.Name)
Call LogMessage("DEBUG - dp=" & dp.Name)
Call LogMessage("DEBUG - cols (nb)=" & CStr(cols.Count))
Call LogMessage("DEBUG - col (nb)=" & CStr(col.Count))
Call LogMessage("DEBUG - rep=" & rep.Name)
Call LogMessage("DEBUG - FullVarName=" & FullVarName)
#End If

    ' rep.Activate => ne fonctionne pas en InfoView
    ' Test de présence de fichiers correspondant aux mêmes comptes
    ' Dans le cas où il y en a déjà, on arrête le traitement
Call LogMessage("INFO - Contrôle des fichiers encore en attente :")
Call LogMessage("INFO -------------------------------------------")

    nb_file = 0
    nb_pending = 0

    FileName = Dir(FilePath & "*.pdf")
    Do While FileName <> ""
        ' Dès qu'on trouve un fichier en attente, on ajoute 1
        ' au nombre de fichier en attente
        nb_file = nb_file + 1
        nb_pending = nb_pending + 1

        Call LogMessage("WARNING - Le fichier " & FileName & " est en attente d'intégration.")

        i = InStr(1, FileName, ".pdf")
        Compte = Left(FileName, i - 1)

        For i = 1 To col.Count
            If col.Item(i) = Compte Then
                Call LogMessage("INFO - Le fichier " & FileName & " sera écrasé.")
                ' Si ce fichier est concerné par le régénération
                ' on retire 1 au nombre de fichier en attente
                ' car il sera remplacé par un nouveau fichier
                nb_file = nb_file - 1

                ' Et on force la sortie de la boucle "for"
                i = col.Count + 1
            End If
        Next

        FileName = Dir ' passe au fichier suivant.
    Loop

    ' Si le nombre de fichier en attente (et qui ne sont pas remplacé par le traitement en cours)
    ' n'est pas nul, on averti l'utilisateur qu'il doit d'abord vider le répertoire d'attente
    ' avant de lancer une régénération de fichiers PDF
    If nb_file > 0 Then
        Call LogMessage("WARNING - Il existe " & CStr(nb_pending) & " fichiers en attente de publication " _
            & "parmi lesquels, " & CStr(nb_file) & " ne seront pas régénérés par ce traitement." & vbCrLf _
            & "Vous devez d'abord intégrer les publications avant de générer de nouveaux fichiers, " _
            & "ou demander aux administrateurs de supprimer ces fichiers." & vbCrLf _
            & vbCrLf _
            & "Arrêt du traitement.")
        Call LogMessage("INFO - Fin du Contrôle des fichiers en attente")
        Call LogMessage("INFO -------------------------------------------" & vbCrLf)

        CreateBatchFile doc.Name, sLogFile
        Shell LogFilePath & BATCH_FILE

        Exit Sub
    End If

    If nb_pending > 0 Then
        Call LogMessage("WARNING - Les " & CStr(nb_pending) & " fichiers en attente de publication " _
        & "seront tous régénérés par ce traitement." & vbCrLf)
    Else
        Call LogMessage("INFO - Aucun fichier en attente")
    End If

    Call LogMessage("INFO - Fin du Contrôle des fichiers en attente")
    Call LogMessage("INFO -------------------------------------------" & vbCrLf)


    PrevFileName = "x"
    nb_file = 0
    For i = 1 To col.Count
        Compte = col.Item(i)
        FileName = FilePath & Compte & ".pdf"

#If DEBUG_MODE Then
Call LogMessage("DEBUG - Compte=" & Compte)
Call LogMessage("DEBUG - FileName=" & FileName)
#End If

        ' Il se peut qu'il y ait plusieurs lignes pour le même compte dans le fournisseur de données
        ' Dans ce cas, on ne régénère pas fichier
        If FileName <> PrevFileName Then
            rep.ForceCompute

            Call rep.AddComplexFilter(FullVarName, "=<" & FullVarName & ">=""" + Compte + """")

            rep.ExportAsPDF (FileName)

            Set OutputFile = fs.GetFile(FileName)

            nb_file = nb_file + 1

            Call LogMessage("INFO - Creation du PDF " & Compte & ".pdf" & " de taille " & OutputFile.Size & " octets")
        End If
        PrevFileName = FileName
    Next

    Call rep.AddComplexFilter(FullVarName, "=1=1")

    Close

    Call LogMessage("INFO - Fin du traitement. " & CStr(nb_file) & " document(s) généré(s).")

    CreateBatchFile doc.Name, sLogFile
    Shell LogFilePath & BATCH_FILE

Exit Sub

ErrorHandler:
    sLogMsg = "ERROR - " & Err.Number & " - " & Err.Description
    Call LogMessage(sLogMsg)
    Resume Next

End Sub

Sub LogMessage(sLogMsg)
    Dim msgText As String
    Dim nFile As Integer

    nFile = FreeFile
    Open sLogFile For Append As #nFile<     msgText = Format(Now, "mm/dd/yy hh:mm:ss") & " - " & sLogMsg
    Print #nFile, msgText
    Close #nFile
End Sub

Sub CreateBatchFile(DocName, LogFile)
    Dim batFile
    Dim nFile As Integer

    batFile = LogFilePath & BATCH_FILE
    nFile = FreeFile

    If fs.FileExists(batFile) Then
        fs.DeleteFile (batFile)
    End If
    fs.CreateTextFile (batFile)

    Open batFile For Append As #nFile

    Print #nFile, LogFilePath & "postie " _
    & " -host:SNMP" _
    & " -from:BusinessObjects@me.com" _
    & " -tolist:" & LogFilePath & "myMailingList.lst" _
    & " -s:" & Chr(34) & "Creation des PDF pour " & DocName & Chr(34) _
    & " -msg:" & Chr(34) & "Si un fichier est a 0 octet, appeler les administrateurs" & Chr(34) _
    & " -file:" & LogFile _
    & vbCrLf

    Close

End Sub

Aucun commentaire:

Enregistrer un commentaire