Page 1 of 1

APORTE - Refundición de libros excel: clase TExcelPaste

PostPosted: Wed Apr 06, 2016 10:17 am
by hmpaquito
Hola,

Os presento una clase que refunde libros excel en un solo libro excel.
Características:
- Infinitos libros a refundir
- Posibilidad de renombrar nombres hojas origen

Espero que os sirva.

Code: Select all  Expand view

////////////
// ExcelPaste.Prg   - Copia hojas y pega sobre un libro.
//
//
//
// Clases para Copiar de una o varias hojas excel a otra hoja excel
// destino
////////////////////////////////////////////////////////////////////////

#Include "FiveWin.Ch"
#Include "\prg\genlib\debug.ch"
#Include "\prg\genlib\xtry.ch"



STATIC aExcelAbierto:= {}
//-------------------------------------------------------------------------//
FUNCTION SampleExcelPaste()

Sample2()

RETURN NIL

//-------------------------------------------------------------------------//
STATIC FUNCTION Sample2()
Local oPaste, oOri
Local cPath:= "C:\plantillas\datos"

oPaste:= TExcelPaste():New()
oPaste:lVisible    := .f.
oPaste:cFileDestino:= PathCompleto("Destino-bis.xlsx")



// Nota: El orden final de aparicion de las hojas sera el mismo
// que se establezca aqui.

// PRIMER WORKBOOK ORIGEN
oOri:= TExcelPasteOrigen():New()
oOri:cFileOrigen := cPath+ "\Detalle factura de pruebas.xls"

oOri:cHojaOrigen := "Hoja1"
oOri:cHojaDestino:= "1Hoja1"        // Hoja destino
oOri:Add()
oPaste:Add(oOri)


oOri:= TExcelPasteOrigen():New()
oOri:cFileOrigen := cPath+ "\Tarifa Referencias.xlsx"          // Solo hoja origen. La hoja destino tendra el mismo nombre
oOri:cHojaOrigen := "Hoja1"
oOri:cHojaDestino:= "2Hoja1"        // Hoja destino
oOri:Add()
oPaste:Add(oOri)

oOri:= TExcelPasteOrigen():New()
oOri:cFileOrigen := cPath+ "\Lista de los Productos.xlsx"          // Solo hoja origen. La hoja destino tendra el mismo nombre
oOri:cHojaOrigen := "Hoja1"
oOri:cHojaDestino:= "3Hoja1"        // Hoja destino
oOri:Add()
oPaste:Add(oOri)


oPaste:Activate()


RETURN NIL


//-------------------------------------------------------------------------//
STATIC FUNCTION Sample1()
Local oPaste, oOri

oPaste:= TExcelPaste():New()
oPaste:lVisible    := .f.
oPaste:cFileDestino:= PathCompleto("Destino.xlsx")



// Nota: El orden final de aparicion de las hojas sera el mismo
// que se establezca aqui.

// PRIMER WORKBOOK ORIGEN
oOri:= TExcelPasteOrigen():New()
oOri:cFileOrigen := "Origen1.xlsx"


oOri:cHojaOrigen := "HojaOrigen1"         // Hoja origen
oOri:cHojaDestino:= "HojaDestino1"        // Hoja destino
oOri:Add()


oOri:cHojaOrigen:= "HojaOrigen2"          // Solo hoja origen. La hoja destino tendra el mismo nombre
oOri:Add()



oPaste:Add(oOri)



// SEGUNDO WORKBOOK ORIGEN
// Todas las hojas
oOri:= TExcelPasteOrigen():New()
oOri:cFileOrigen := "Origen2.xlsx"

oPaste:Add(oOri)



oPaste:Activate()


RETURN NIL




//--------------------------------------------------------------------------//
CLASS TExcelPaste


   METHOD New()

   METHOD Activate()


   METHOD Add(oOri)


   DATA aOrigen HIDDEN


   DATA cFileDestino

   DATA lVisible

   DATA oExcel, oBook     HIDDEN   // Destino
   DATA lSaveAs           HIDDEN

   METHOD lCrealoAbrelo() HIDDEN
   METHOD Save()          HIDDEN
   METHOD End()           HIDDEN
   METHOD PasteCuore()    HIDDEN
   METHOD CopyUno         HIDDEN

   // lNuevo indica si es nuevo: si existe, lo sobreescribe. Lo contrario
   // de lNuevo sera a¤adir a las hojas existentes
   DATA lNuevo INIT .T.        

ENDCLASS


//-------------------------------------------------------------------------//
METHOD New()                 CLASS TExcelPaste

::aOrigen:= {}
RETURN Self

//-------------------------------------------------------------------------//
METHOD Activate()            CLASS TExcelPaste
Local nI, nCountBorrar:= 0, oHoja
Local lAnterior, nVueltas:= 0, nUltima
Local nCount, oPage
::cFileDestino:= FileCarValidos(::cFileDestino)
IF !::lCrealoAbrelo()
   RETURN NIL
ENDIF
*
IF ::lNuevo
   // Renombro hojas existentes, que borrare al final, para que no
   // choquen con otras que se copien.
   #Define INI_BORRAR     "NOVALID_"
   #Define NAME_BORRAR(n) INI_BORRAR+ StrZero(n, 3)


   nCountBorrar:= ::oBook:WorkSheets:Count


   FOR nI:= 1 TO nCountBorrar
      oHoja:= ::oBook:WorkSheets(nI)


      // Como todas no se pueden borrar, dejo la ultima, pero
      // renombrada
      oHoja:Set("Name", NAME_BORRAR(nI)) // Para que no interfiera con hojas que se copiaran

   NEXT
   oHoja:= NIL
ENDIF
*

*
::PasteCuore()
*
IF nCountBorrar > 0
   // Borra las hojas existentes... al menos las Hoja1, Hoja2, Hoja3
   // que siempre estan aunque recien creado.
   // Se borra al final porque no se pueden borrar todas, asi que las borro
   // al final.


   lAnterior:= ::oExcel:DisplayAlerts
   ::oExcel:DisplayAlerts:= .f.           // IMPORTANTISIMO !!


   // Borrado por nombre porque la segunda vez, entrando y saliendo
   // del programa.
   DO WHILE .T.
      oHoja:= RetHoja(::oExcel, ::oBook, NIL, INI_BORRAR)
      IF oHoja == NIL
         EXIT
      ENDIF
      oHoja:Delete()
      *
      *
      // Control de cuelgue
      nVueltas++
      IF nVueltas > 50
         MERROR_("No se puede borrar hoja !!", oHoja, oHoja:Name)
         EXIT
      ENDIF
      *
   ENDDO


   ::oExcel:DisplayAlerts:= lAnterior


   oHoja:= NIL
ENDIF
*
// Pone tipo de papel. Por defecto: estrecho.
nCount:= ::oBook:WorkSheets:Count
FOR nI:= 1 TO nCount
   oHoja:= ::oBook:WorkSheets(nI)
   oPage:= oHoja:PageSetup


   // Configuracion de margenes igual que ESTRECHO
   oPage:LeftMargin  := 0.64
   oPage:RightMargin := 0.64
   oPage:TopMargin   := 1.91
   oPage:BottomMargin:= 1.91
   oPage:HeaderMargin:= 0.76
   oPage:FooterMargin:= 0.76


NEXT
*
::Save()
*

*
::End()
*
oHoja:= NIL
oPage:= NIL
*
RETURN NIL

//-------------------------------------------------------------------------//
METHOD End()                 CLASS TExcelPaste

   ::oBook:Close()
   ::oBook:= NIL

   ::oExcel:Quit()
   ::oExcel:= NIL

RETURN NIL

//-------------------------------------------------------------------------//
METHOD PasteCuore()          CLASS TExcelPaste
Local nI
Local oOri
Local oExcelOrigen, oBookOrigen
Local cHojaOrigen, cHojaDestino
Local nHojas, nHoja, oHojaTmp
Local oTry
Local cFileOrigen


*

oExcelOrigen:= CreateObjectExcel()
IF oExcelOrigen == NIL
   RETURN NIL
ENDIF



FOR nI:= Len(::aOrigen) TO 1 STEP -1   // Al reves para que el orden quede correcto

   oOri:= ::aOrigen[nI]
   *

   IF !File(oOri:cFileOrigen)
      MERROR_("No existe file excel origen !!", oOri:cFileOrigen)
      RETURN NIL
   ENDIF
   *
   cFileOrigen:= PathCompleto(oOri:cFileOrigen)
   *
   xTRY INI TO oTry
      oBookOrigen:= oExcelOrigen:WorkBooks:Open(cFileOrigen)
   xTRY END
   IF oTry:lError
      oTry:MsgError(oExcelOrigen, nI, oOri, oOri:cFileOrigen, cFileOrigen)
   ENDIF
   *
   *
   IF !Empty(oOri:aOrigen)
      // Se copia UNA o VARIAS hojas de este libro excel origen

      #Define POS_HOJA_ORIGEN  1
      #Define POS_HOJA_DESTINO 2
      FOR nHoja:= Len(oOri:aOrigen) TO 1 STEP -1 // Al reves para que el orden quede correcto

         oBookOrigen:Activate()  // importantisimo


         cHojaOrigen := oOri:aOrigen[nHoja, POS_HOJA_ORIGEN]
         cHojaDestino:= oOri:aOrigen[nHoja, POS_HOJA_DESTINO]

         IF Empty(cHojaDestino)
            cHojaDestino:= cHojaOrigen
         ENDIF
         cHojaDestino:= FileCarValidos(cHojaDestino)

         ::CopyUno(cFileOrigen, oExcelOrigen, oBookOrigen, cHojaOrigen, cHojaDestino)

      NEXT
   ELSE
      // Se copia TODAS las hojas de este libro excel origen
      nHojas:= oBookOrigen:WorkSheets:Count
      FOR nHoja:= nHojas TO 1 STEP -1            // Al reves para que el orden quede correcto

         oBookOrigen:Activate()  // importantisimo
         oHojaTmp:= oBookOrigen:WorkSheets( nHoja)
         *
         cHojaOrigen := oHojaTmp:Name
         cHojaDestino:= cHojaOrigen
         ::CopyUno(cFileOrigen, oExcelOrigen, oBookOrigen, cHojaOrigen, cHojaDestino)

          oHojaTmp:= NIL
      NEXT
   ENDIF
   *
   *
   *
   oBookOrigen:Close()
   oBookOrigen:= NIL
   *

NEXT
*
*
oExcelOrigen:Quit()
oExcelOrigen:= NIL


RETURN NIL
*
//-------------------------------------------------------------------------//
METHOD CopyUno(cFileOrigen, oExcelOrigen, oBookOrigen, cHojaOrigen, cHojaDestino) CLASS TExcelPaste
Local oHojaOrigen, oHojaDestino, oSelect
Local oTry
Local oExcelDestino:= ::oExcel
Local oRango
Local nI
Local nCols, oCellOri, oCellDes
Local nRows, oRowOri, oRowDes
Local nPics, oPicOri, oPicDes
Local nShapes, oShapeOri, oShapeDes
Local nFilIni, nFilFin, cRango
Local nUltimo
Local oTry2
*
*
oHojaOrigen:= RetHoja(oExcelOrigen, oBookOrigen, cHojaOrigen)

IF oHojaOrigen == NIL
   MERROR_("Hoja no encontrada !!", cFileOrigen, oExcelOrigen, cHojaOrigen)
   RETURN NIL
ENDIF
*
*
oHojaDestino:= ::oBook:WorkSheets:Add()
oHojaDestino:Activate()

*
xTRY INI TO oTry
   oHojaDestino:Set("NAME", cHojaDestino)
   *

   oRango:= oHojaOrigen:Cells()
   oRango:Copy()


   ::oBook:ActiveSheet:Paste()



   nCols:= oHojaOrigen:UsedRange:Columns:Count()

   FOR nI:= 1 TO nCols
      oCellOri:= oHojaOrigen:Cells(nI)

      oCellDes:= oHojaDestino:Cells(nI)
      oCellDes:ColumnWidth:= oCellOri:ColumnWidth
   NEXT


   // Tambien el height de las rows... porque no
   // sale perfecto con lo anterior. Esta operacion puede ser lenta.
   nRows:= oHojaOrigen:UsedRange:Rows:Count()

   FOR nI:= 1 TO nRows
      oRowOri:= oHojaOrigen:Rows(nI)

      oRowDes:= oHojaDestino:Rows(nI)
      oRowDes:RowHeight:= oRowOri:RowHeight
   NEXT



   #Define msoPicture   13
   nShapes:= oHojaOrigen:Shapes:Count()
   FOR nI:= 1 TO nShapes

      oShapeOri:= oHojaOrigen:Shapes:Item(nI)


      oShapeOri:Copy()

      oHojaDestino:Paste()
      nUltimo:= oHojaOrigen:Shapes:Count()
      xTRY INI TO oTry2
         oShapeDes:= oHojaDestino:Shapes:Item(nUltimo)
      xTRY END
      IF oTry2:lError
         *
         oTry2:SaveError(nI, nShapes, nUltimo, oShapeDes)  // Solo graba error... puede ser un error sin importancia
         *
         LOOP
      ENDIF

      oShapeDes:IncrementTop := - (oShapeDes:Top - oShapeOri:Top )
      oShapeDes:IncrementLeft:= - (oShapeDes:Left- oShapeOri:Left)

   NEXT




   
xTRY END

oHojaDestino:Cells(1,1):Select()  // Para que apague el seleccionado

IF oTry:lError
   oTry:MsgError(oExcelOrigen, cHojaOrigen, cHojaDestino,;
                 oHojaOrigen, oHojaDestino, nUltimo)
ENDIF
*
oHojaorigen := NIL
oHojaDestino:= NIL
oSelect     := NIL
oRango      := NIL
oCellOri    := NIL
oCellDes    := NIL
oRowOri     := NIL
oRowDes     := NIL
oPicOri     := NIL
oPicDes     := NIL
oShapeOri   := NIL
oShapeDes   := NIL
*
RETURN NIL
*
//-------------------------------------------------------------------------//
METHOD Add(oOri)             CLASS TExcelPaste
Aadd(::aOrigen, oOri)
RETURN NIL
*
//-------------------------------------------------------------------------//
METHOD lCrealoAbrelo()       CLASS TExcelPaste
Local cSaveAsFileName:= ::cFileDestino
Local oExcel, oBook, lSaveAs
Local bSaveHandler, oError

DO WHILE File(cSaveAsFileName) .AND. !lFiAccess(cSaveAsFileName)


      #Define MSG_EXCEL ;
        "­ Libro de Excel no disponible porque probablemente est  abierto!"+ CRLF+;
        "Ciérrelo y reintente la operación"+ CRLF+;
        "¨ Reintentar ?"
      IF !mMsgYesNo( MSG_EXCEL, FileNoPath(cSaveAsFileName))
        RETURN .f.
      ENDIF

ENDDO

oExcel:= CreateObjectExcel()
IF oExcel == NIL
   RETURN .F.
ENDIF



IF !File(cSaveAsFileName)
   lSaveAs:= .t.
   *
   *
   *
   *
   *
   *
   bSaveHandler := errorblock( { |x| break(x) } )

   BEGIN SEQUENCE
      oBook:= oExcel:WorkBooks:Add()

   RECOVER USING oError
      MERROR_( "­ Se produjo un error !",;
               ;
               cSaveAsFileName,;
               ;
               ;
               File(cSaveAsFileName),;
               IsDirectory(PathFile(cSaveAsFileName)),;
               ;
               ;
               ;
               ;
               oBook,;
               ;
               oError,;
               oError:SubSystem(),;
               oError:Description,;
               oError:Operation,;
               oError:SubCode,;
               oError:FileName,;
               DosError(),;
               FError(),;
               ;
               Ole2TxtError(),;
               ;
               ;
               GetEnv("TMP"),;
               GetEnv("TEMP"),;
               ;
               oExcel,;
               ;
               lFiAccess(cSaveAsFileName),;
               DosError(),;
               FError();       // File Error de lFiAccess()
               ;
              )

   END
   // Restore the default error handler
   errorblock( bSaveHandler )
   *
   *
   *
   *
   *
   *
   *
   *
ELSE
   lSaveAs:= .f.
   oBook:= oExcel:WorkBooks:Open(cSaveAsFileName)
ENDIF
*
oExcel:Visible:= ::lVisible
*
::oExcel:= oExcel
::oBook := oBook
::lSaveAs:= lSaveAs
RETURN .T.

//-------------------------------------------------------------------------//
METHOD Save()                CLASS TExcelPaste
Local cSaveAsFileName:= ::cFileDestino
Local oBook:= ::oBook
Local bSaveHandler, oError
Local lSaveAs:= ::lSaveAs
Local oExcel:=  ::oExcel

IF !lSaveAs
   // Lo borro antes de grabarlo para que no pregunte
   // si lo deseo sobreescribir
     *
     *
     *
     *
     *
     *
     *
     bSaveHandler := errorblock( { |x| break(x) } )

     BEGIN SEQUENCE
        oBook:Save()

     RECOVER USING oError
        MERROR_( "­ Se produjo un error !",;
                 ;
                 cSaveAsFileName,;
                 ;
                 ;
                 File(cSaveAsFileName),;
                 IsDirectory(PathFile(cSaveAsFileName)),;
                 ;
                 ;
                 ;
                 ;
                 ;
                 oBook,;
                 ;
                 oError,;
                 oError:SubSystem(),;
                 oError:Description,;
                 oError:Operation,;
                 oError:SubCode,;
                 oError:FileName,;
                 DosError(),;
                 FError(),;
                 ;
                 Ole2TxtError(),;
                 ;
                 ;
                 GetEnv("TMP"),;
                 GetEnv("TEMP"),;
                 ;
                 oExcel,;
            ;
            lFiAccess(cSaveAsFileName),;
            DosError(),;
            FError();       // File Error de lFiAccess()
            ;
                 )

     END
     // Restore the default error handler
     errorblock( bSaveHandler )
     *

ELSE
   TRY
      oBook:SaveAs(cSaveAsFileName) // Esto muy lento sobre todo cuando se generan varias hojas de calculo !!!!
   CATCH oError

       MERROR_( "­ Se produjo un error !",;
                ;
                cSaveAsFileName,;
                ;
                ;
                File(cSaveAsFileName),;
                IsDirectory(PathFile(cSaveAsFileName)),;
                ;
                ;
                ;
                ;
                oBook,;
                ;
                oError,;
                oError:SubSystem(),;
                oError:Description,;
                oError:Operation,;
                oError:SubCode,;
                oError:FileName,;
                DosError(),;
                FError(),;
                ;
                Ole2TxtError(),;
                ;
                ;
                GetEnv("TMP"),;
                GetEnv("TEMP"),;
                ;
                oExcel,;
            ;
            lFiAccess(cSaveAsFileName),;
            DosError(),;
            FError();       // File Error de lFiAccess()
            ;
               )


   END

ENDIF


RETURN NIL
*
//-------------------------------------------------------------------------//
// Agrupa Hojas de un mismo libro (cFileOrigen)
CLASS TExcelPasteOrigen

   METHOD New()
   METHOD Activate()
   METHOD Add()

   DATA cFileOrigen

   // MUCHO OJO !!! SOLO para parametros para :Add()
   DATA cHojaOrigen
   DATA cHojaDestino



   DATA aOrigen HIDDEN

ENDCLASS

//--------------------------------------------------------------------------//
METHOD New()                 CLASS TExcelPasteOrigen

::aOrigen:= {}
RETURN Self

//--------------------------------------------------------------------------//
METHOD Activate()            CLASS TExcelPasteOrigen

RETURN NIL

//--------------------------------------------------------------------------//
METHOD Add()                 CLASS TExcelPasteOrigen
Local aOri

// Control de errores mios para que solo se añada TODAS o una hoja
// en concreto UNA SOLA VEZ
IF AScanea(::aOrigen, {|x| x[1] == ::cHojaOrigen} ) > 0
   MERROR_("Este libro u hoja (NIL/hoja) ya ha sido añadido !!",;
           ::cHojaOrigen, ::aOrigen, aDebug(::aOrigen) )
   RETURN NIL
ENDIF

aOri:= {::cHojaOrigen, ::cHojaDestino}
Aadd(::aOrigen, aOri)

::cHojaOrigen := NIL
::cHojaDestino:= NIL

RETURN NIL

//-------------------------------------------------------------------------//
¤STATIC FUNCTION CreateObjectExcel()
Local oExcel
*
oExcel := TOleAuto():New( "Excel.Application" )

If Ole2TxtError() != "S_OK"
   MsgStop( "Error al intentar acceder a Excel","Error de conexión")
   oExcel:Quit()
   oExcel:= NIL
Endif
*
Aadd(aExcelAbierto, oExcel)
RETURN oExcel
*
//--------------------------------------------------------------------------//
STATIC FUNCTION RetHoja(oExcel, oBookOrigen, cNombreHoja, cIniNombreHoja)
Local nHojas, nI, oHojatmp, oHojaRet:= NIL
nHojas:= oBookOrigen:WorkSheets:Count // cuantas hojas tiene el libro de excel

FOR nI:= 1 TO nHojas //- 1

   oHojaTmp:= oBookOrigen:WorkSheets( nI )
   IF If(cNombreHoja != NIL, oHojaTmp:Name == cNombreHoja,;
                             cLeft(oHojaTmp:Name, cIniNombreHoja) )
      oHojaRet:= oHojaTmp
      EXIT
   ENDIF
NEXT
RETURN oHojaRet
*

*
*
//-------------------------------------------------------------------------//
EXIT PROCEDURE LimpiaMemoriaExcelPas()
Local nI
FOR nI:= 1 TO Len(aExcelAbierto)
   IF aExcelAbierto[nI] != nil
      aExcelAbierto[nI]:Quit()
   ENDIF
NEXT
RETURN NIL
*


 

Saludos

Re: APORTE - Refundición de libros excel: clase TExcelPaste

PostPosted: Thu Apr 07, 2016 5:43 pm
by ACC69
Gracias Ing. buen aporte.

Saludos

Re: APORTE - Refundición de libros excel: clase TExcelPaste

PostPosted: Thu Apr 07, 2016 9:55 pm
by Armando
hmpaquito:

Aunque de momento no la voy a utilizar, indudablemente es un
excelente aporte, cómo siempre!.

Gracias, Saludos

Re: APORTE - Refundición de libros excel: clase TExcelPaste

PostPosted: Sun Apr 10, 2016 7:49 pm
by Massimo Linossi
Very, very nice.
Compliments !!!

Re: APORTE - Refundición de libros excel: clase TExcelPaste

PostPosted: Sun Apr 10, 2016 9:35 pm
by cnavarro
Muy interesante

Gracias