jueves, 6 de junio de 2024

81 - Guardar rangos de Excel como Imagen.

 Cuando tenemos que enviar información parcial de alguna hoja, por ejemplo filtrada por Productos, o por Clientes, generalmente guardamos la hoja filtrada y la exportamos.

Aquí vamos a crear el informe como imagen. Sin necesidad de enviar la hoja filtrada sino solamente en un archivo jpg o png.

Para esto contamos con una hoja auxiliar (opcional) y el programa creará una carpeta (si aún no la tenemos) en el mismo directorio donde se encuentra nuestro libro.

Tanto el nombre de la subcarpeta, su ubicación y la extensión del archivo de imagen son argumentos opcionales que pueden ser modificados.

En el ejemplo del libro que se encuentra para descargar, el nombre para el archivo de imagen lo ingresará el usuario mediante un InputBox. Aquí, otra opción podría ser que se tome como 'nombre' el criterio filtrado (nombre del producto o nombre del cliente para este ejemplo).

Como esta macro puede ser utilizada para 2 procesos diferentes, es que tendré 2 llamadas: copiaRango para guardar imágenes en una subcarpeta y llamaUF para enviar un rango a un control de imagen de un Userform (se tratará en el próximo video).

* Traducción y adaptación de la macro del sitio: www.thespreadsheetguru.com

Sub guardaImagen()

nbreArchivo = InputBox("Ingrese el nombre para el archivo de imagen."

If nbreArchivo = "" Then MsgBox "Se canceló el proceso.": Exit Sub

nbreHoja = ActiveSheet.Name

Call exportaImagen(nbreArchivo, nbreHoja)

End Sub


Sub exportaImagen(nombre, hoja)

Dim hoL As Worksheet

Dim hoX As Worksheet

Dim rutaIMG As String

Dim nbreArchImg As String

 

' Establecer referencias a las 2 hojas de trabajo

Set hoL = ThisWorkbook.Sheets(hoja)

Set hoX = ThisWorkbook.Sheets("Hoja1")

Application.ScreenUpdating = False

 

' Obtener la ruta de la carpeta "IMG"     'Ajustar nombre

rutaIMG = ThisWorkbook.Path & "\IMG\"

' Crear la carpeta "IMG" si no existe

If Dir(rutaIMG, vbDirectory) = "" Then

    MkDir rutaIMG  'crear la subcarpeta

End If

'ruta y nombre completo para la imagen

nbreArchImg = rutaIMG & nombre & ".png"      'JPG

 

' Limpiar la hoja auxiliar

hoX.Activate

hoX.Cells.Delete

 

' Definir el rango de la imagen filtrada en la hoja activa

x = hoL.Range("B" & Rows.Count).End(xlUp).Row

If x <= 4 Then MsgBox "No hay datos filtrados.": Exit Sub

rgo = hoL.Range("B4:I" & x).Address

hoL.Range(rgo).CopyPicture Format:=xlPicture

' Pegar la imagen en la hoja "Hoja1" en el rango A1

hoX.Range("A1").PasteSpecial


'luego del pegado el objeto queda seleccionado. Se lo guarda en una variable 

    Set miShape = Selection

'se agrega un objeto chart con las dimensiones del rango

    Set miChart = hoX.ChartObjects.Add(Left:=miShape.Left, Top:=miShape.Top, Width:=miShape.Width, Height:=miShape.Height)

'el objeto seleccionado se pega en el chart y ese objeto se exporta

    miShape.Copy

    miChart.Select

    ActiveChart.Paste

'se guarda la imagen en la subcarpeta

ActiveSheet.ChartObjects(1).Activate

ActiveChart.Export Filename:=nbreArchImg, FilterName:="png"    'JPG

 

'eliminar los objetos agregados en la hoja 1

ActiveSheet.Shapes(1).Delete

ActiveChart.ChartArea.Select

ActiveChart.Parent.Delete

Selection.Delete

 

'se quita el filtro a la lista.

With hoL

    .Select

    .Unprotect

    If .FilterMode = True Then .ShowAllData

    .Protect DrawingObjects:=True, Contents:=True, Scenarios:=True _

        , AllowFiltering:=True

    .[E4].Select

End With

MsgBox "Fin del proceso."

End Sub


Acceso al VIDEO Nº 81 desde aquí.

Descargar libro desde aquí o solicitarlo a mi correo de Gmail.


domingo, 28 de abril de 2024

79 - Filtrar con criterio en otra hoja.

Generalmente seleccionamos, desde las columnas de una hoja de datos, el elemento buscado. O dejamos en alguna celda el criterio a buscar y con una macro se filtra la hoja.

La propuesta que presento aquí está pensada para cuando tenemos listas: de Alumnos, Cuentas de clientes o proveedores, Productos y tantas otras, donde guardamos el nombre y su código.

Y donde las hojas de Movimientos de cuentas solo cuentan con la columna de Código, lo que dificulta la búsqueda de algún registro. 


Entonces vamos a recurrir a una macro que se ejecutará desde la hoja de la Lista. En este ejemplo se utilizó el evento BeforeDoubleClick, aunque bien podría ser ejecutada desde un botón o el menú Desarrollador/Programador, Macros.

En el Editor de Macros seleccionamos la hoja Listado y allí colocaremos estas instrucciones:

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

'llamamos a la macro de filtrado

Call filtro_Total

End Sub

Y la macro llamada 'filtro_Total' la colocaremos en un módulo:

Sub filtro_Total()   

'macro única para uso en los 2 modelos de hoja: rango o tabla

'acotamos el rango desde donde podremos hacer doble clic para llamar a la macro

If ActiveCell.Column <> 3 Or ActiveCell.Row < 4 Then Exit Sub

'verificamos si la celda seleccionada tiene contenido

If ActiveCell = "" Then Exit Sub

'se guarda el dato de la celda seleccionada y se pasa a la otra hoja

dato = ActiveCell.Value

Sheets("MOVIMIENTOS").Select   'agregar instrucciones (*)

'quitamos previamente cualquier filtro aplicado

If ActiveSheet.FilterMode = True Then ActiveSheet.ShowAllData

'se evalúa si se trata de una hoja con rango o con Tabla (**)

If ActiveSheet.ListObjects.Count = 0 Then

    'establecer el rango y filtrar por el criterio guardado en la variable

    rgo = [C3].CurrentRegion.Address

    ActiveSheet.Range(rgo).AutoFilter Field:=2, Criteria1:=dato

Else

    'si se conoce en qué col se encuentran las claves, utilizar esta instrucción

    ActiveSheet.ListObjects(1).Range.AutoFilter Field:=2, Criteria1:=dato


    'si no se conoce la ubicación de la col 'claves' utilizar estas otras

     'Ajustar nombre de tabla y el título de columna al modelo

    'colx = ActiveSheet.Range("Tabla1[[CODIGO]]").Column        

    'ActiveSheet.ListObjects(1).Range.AutoFilter Field:=colx - 1, Criteria1:=dato

End If

End Sub


NOTAS ACLARATORIAS:

(*) Para que la macro sea de uso en cualquier hoja del libro, se deben agregar instrucciones para evaluar previamente en qué hoja se filtrará. Por ejemplo: colocando el nombre en alguna celda de la hoja Listado.

Hojita= [E1] 

Sheets(Hojita).Select 



(**) Como intentamos utilizar la misma macro para diferentes formatos de hojas, necesitamos evaluar si la hoja activa tiene un objeto Tabla o no. ListObjects.Count nos devuelve al número de tablas,

(***)  Para trabajar con un solo modelo de hojas, recomiendo mirar el video donde se muestran en módulos diferentes cada una de sus macros (filtra_Rango o filtra_Tabla)


Acceso al VIDEO Nº 79 desde aquí.

Descargar libro desde aquí o solicitarlo a mi correo de Gmail.

jueves, 18 de abril de 2024

78 - Crear hojas a partir de una Plantilla, para lista de destinatarios.

 Si tenemos nuestra documentación en forma de base de datos (facturación, cobranzas, documentos emitidos, etc), en algún momento necesitaremos enviar esa documentación a los destinatarios. O necesitaremos guardar en hojas separadas esa información.

En este ejemplo, parto de una lista de facturas emitidas y la idea es guardar esa información en formato de documento, por cada cliente de la lista.

Ejemplo 1: la lista no se encuentra filtrada. Se controla si el registro tiene saldo a facturar.




Sub facturando()

Dim hoC As Worksheet, hoF As Worksheet

'se declaran las 2 hojas del proceso

Set hoC = Sheets("Clientes")

Set hoF = Sheets("Formato")

'recorrer el rango a facturar

With hoC

    For i = 4 To Range("B" & Rows.Count).End(xlUp).Row

        'comprobar si tiene saldo <> 0

        If .Range("X" & i) = 0 Then GoTo sigueOtro

 

        'crear copia de la hoja Formato agregándola al final

        hoF.Copy After:=Sheets(Sheets.Count)

        On Error Resume Next

        ActiveSheet.Name = .Range("D" & i).Text

        If Err.Number > 0 Then GoTo existeHoja

sigo:

        On Error GoTo 0

        Set hoX = ActiveSheet

        'pasar los datos a la nueva hoja

        hoX.[D2] = .Range("D" & i)      'nbre clie

        hoX.[D3] = .Range("C" & i)      'cod clie

        hoX.[D4] = .Range("E" & i)      'domicilio clie

        hoX.[B6] = .Range("F" & i)      'nro fact

        hoX.[E6] = .Range("B" & i)      'fecha

       

        'evluar si hay valores en H o en I

        If .Range("H" & i) = 0 Then

            hoX.[E15] = .Range("I" & i)

        Else

            hoX.[E15] = .Range("H" & i)

        End If

       

        'concatenar col AA + AB

        hoX.[A10] = .Range("AA" & i) & " " & .Range("AB" & i)

        hoX.[E16] = .Range("M" & i)                    'iva

        hoX.[E17] = .Range("W" & i)                     'retencion

'se pasa a la fila siguiente de la hoja base

sigueOtro:

    Next i

End With 

MsgBox "Fin del proceso.", , "Información"

Exit Sub

 

existeHoja:

MsgBox "Ya existe una hoja de nombre " & hoC.Range("D" & i).Text & Chr(10) & _

"El formato se guardará con nombre de hoja " & ActiveSheet.Name & ".", vbCritical

GoTo sigo

End Sub



Ejemplo 2: Se aplicará filtro a la lista. Se recorre el rango rellenando el formato solo con datos de filas filtradas. Se controla si el registro tiene saldo a facturar.

Sub facturando_Filtrado()

'se declaran las 2 hojas del proceso

Set hoC = Sheets("Clientes")

Set hoF = Sheets("Formato")

'controles previos a la salida del formato (si se aplicó filtro => si hay filas filtradas)

x = hoC.Range("B" & Rows.Count).End(xlUp).Row

If x < 4 Then

    MsgBox "No hay datos para facturar.", , "Atención"

    Exit Sub

End If

'recorrer el rango a facturar

With hoC

    For Each celdita In .Range("B4:B" & x).SpecialCells(xlCellTypeVisible)

        'fila de la celda visible

        i = celdita.Row

        'comprobar si tiene saldo <> 0

        If .Range("X" & i) = 0 Then GoTo sigueOtro   

        'crear copia de la hoja Formato agregándola al final

        hoF.Copy After:=Sheets(Sheets.Count)

        On Error Resume Next

        ActiveSheet.Name = .Range("D" & i).Text

        If Err.Number > 0 Then GoTo existeHoja

sigo:

        On Error GoTo 0

        Set hoX = ActiveSheet

        'pasar los datos a la nueva hoja

        hoX.[D2] = .Range("D" & i)

        hoX.[D3] = .Range("C" & i)

        hoX.[D4] = .Range("E" & i)

        hoX.[B6] = .Range("F" & i)

        hoX.[E6] = .Range("B" & i)

        If .Range("E" & i) = 0 Then

            hoX.[E15] = .Range("H" & i)

        Else

            hoX.[E15] = .Range("I" & i)

        End If

        hoX.[A10] = .Range("AA" & i) & " " & .Range("AB" & i)

        hoX.[E15] = .Range("B" & i)

        hoX.[E16] = .Range("M" & i)

        hoX.[E17] = .Range("W" & i)   

sigueOtro:

Next celdita

End With

MsgBox "Fin del proceso.", , "Información"

Exit Sub

 

existeHoja:

MsgBox "Ya existe una hoja de nombre " & hoC.Range("D" & i).Text & Chr(10) & _

"El formato se guardará con nombre de hoja " & ActiveSheet.Name & ".", vbCritical

GoTo sigo

End Sub


Descargar libro con las macros desde aquí o solicitarlo al correo Gmail de: cibersoft.arg

Acceso al VIDEO Nº 78 desde aquí.

miércoles, 10 de abril de 2024

77 - Graficando sin Gráficos.

 Si tenemos que presentar Informes de planillas con gran cantidad de filas y columnas, podemos hacer uso de un par de herramientas para graficar resultados, sin necesidad de crear Gráficos.

Modelo Nº 1:

a- En el primer grupo de Totales aplicamos Gráfico de barras.  

    Desde menú Inicio, Formato Condicional, Barra de datos, y seleccionamos un estilo a gusto.


b- En el segundo grupo, desde el mismo menú Inicio, Formato condicional, aplicamos Escalas de color, simulando un semáforo.

NOTA: En la primera imagen se superpuso una Escala de color sobre un gráfico de Barras.


Modelo Nº 2:


Aquí se intenta graficar la evolución de los resultados mensuales. Para ello, desde menú Insertar optamos por el grupo Minigráficos seleccionando un estilo. En el primer grupo de valores se optó por Columnas y en el segundo por Líneas.


Se puede llamar a esta herramienta con algún rango ya seleccionado. Por ejemplo, si seleccionamos el rango de celdas donde se va a ubicar el gráfico, se nos pedirá el ingreso del rango de Datos.

NOTA: una vez insertado el Minigráfico, se activará una nueva barra de herramientas que nos permitirá darle formato a gusto: color de las barras o líneas, marcar el punto más alto y el más bajo y otras opciones más. En el ejemplo, además, se le dió color de fondo a las celdas y se colocó como título las iniciales de los meses para una mejor visualización.


Modelo Nº 3:

En este modelo, el objetivo es colocar un objeto junto a los valores máximo, mínimo y coincidente con el contenido de una celda.

Para ello primero insertaremos unos objetos gráficos como imágenes o iconos (en versión Excel 365). Y luego ejecutaremos una macro para ubicar esos objetos en el Informe.

NOTA: para insertar Iconos en versiones Excel 365 los invito a buscar en este Blog, la entrada de Mayo 2023


En el Editor de macros, insertamos un módulo y allí colocaremos la macro que recorrerá esta tabla colocando los objetos en el lugar que les corresponda según los valores de la tabla.

NOTA: En el VIDEO Nº 77 (Graficando sin Gráficos) de mi canal, alrededor del minuto 5:50 se explica cómo obtener los nombres de los objetos para ser utilizados desde la programación.

Sub graficando_tabla()

Dim maxi As Single, mini As Single, ideal As Single

Dim cd As Range

Dim rangoTabla

'guardar los valores máximos y mínimos del rango

rangoTabla = Range("Préstamo[[Saldo final]]").Address

maxi = Application.WorksheetFunction.Max(Range(rangoTabla))

mini = Application.WorksheetFunction.Min(Range(rangoTabla))

 'celda donde se encuentra el valor 'ideal'

ideal = [K1]      

'se recorre el rango buscando el valor maxi y mini

For Each cd In Range(rangoTabla)

    'si la celda contiene el valor 'maxi' se coloca el gráfico 4 en el tope y margen izquierdo de esa celda

    If cd.Value = maxi Then

        ActiveSheet.Shapes.Range(Array("Gráfico 4")).Top = cd.Top

        ActiveSheet.Shapes.Range(Array("Gráfico 4")).Left = cd.Offset(0, 1).Left + 20   

    ElseIf cd.Value = mini Then

        'si la celda contiene el valor 'mini' se coloca el gráfico 2 en el tope y margen izquierdo de esa celda

         ActiveSheet.Shapes.Range(Array("Gráfico 2")).Top = cd.Top

         ActiveSheet.Shapes.Range(Array("Gráfico 2")).Left = cd.Offset(0, 1).Left + 20

    ElseIf cd.Value = ideal Then

        'si la celda contiene el valor 'mini' se coloca la imagen en el tope y margen izquierdo de esa celda

         ActiveSheet.Shapes.Range(Array("Group 17")).Top = cd.Top

         ActiveSheet.Shapes.Range(Array("Group 17")).Left = cd.Offset(0, 1).Left + 20

    End If

Next cd

End Sub

 


NOTA: En el libro que se puede descargar desde este enlace o solicitarlo al correo Gmail: cibersoft.arg encontrarán otra macro que recorre un rango común (no Tabla).

Acceso al VIDEO Nº 77 desde aquí.


domingo, 24 de marzo de 2024

76 - Diferencias entre Notas y Comentarios. Cómo agregarlos mediante VBA.

En entradas anteriores (Nov.2018 y Abr.2019) vimos diferentes modelos de Comentarios. Cómo formatearlos (formas, colores e imágenes) y además cómo programar en VBA el agregado o modificación de estos objetos. 

Estos temas fueron mostrados en mi canal Soluciones Excel, en los siguientes videos:

Nº 17: Agregar COMENTARIOS en hoja Excel   (no apto para versiones Excel 365)

Nº 24: Comentarios en Excel mediante programación VBA    (no apto para versiones Excel 365)

Ahora, con la llegada de la versión Excel 365, los antiguos 'comentarios' pasaron a llamarse 'Notas'. Estos objetos Notas todavía conservan la posibilidad de cambios en sus formatos (colores, formas e imagen). Y en VBA siguen llamándose Comment.

Los nuevos Comentarios en cambiono permiten el cambio de formato. Pero sí permiten el agregado de respuestas en dicho cuadro.

En esta nueva versión 365, al seleccionar una celda con comentario, se abrirá un panel lateral a la derecha mostrando el texto original y la posibilidad de agregar una respuesta que se publicará al presionar el botón verde. Y así se irán viendo los autores, fecha y texto de las diferentes respuestas.


Y en VBA, el término 'Comment' pasa ahora a llamarse CommentThreaded.

Veamos un ejemplo de cómo agregar comentarios y respuestas en VBA, ya sea en las versiones clásicas de Excel y en versión 365:

A continuación evaluaremos el cambio en las celdas de dos columnas.  

Si se trata de la columna C agregaremos una NOTA. Si la celda ya presenta una NOTA se sobrescribirá. Y si se borra la celda, también se borrará la NOTA.

En cambio si se trata de la columna I agregaremos COMENTARIOS. Si se borra la celda, se mostrará el cambio como un nuevo comentario.

 Private Sub Worksheet_Change(ByVal Target As Range)

'no se evalúan los cambios en fila 1 ni tampoco si se han seleccionado varias celdas

If Target.Count > 1 Or Target.Row = 1 Then Exit Sub

'si el cambio se da en col 3 (C) se deja solo una NOTA

If Target.Column = 3 Then

    Target.ClearComments    'otra opción: evaluar si hay una nota o no.

    Target.AddComment "Compartir este evento con Jose Luis."


'si el cambio se da en col I de la tabla de costos se agrega como Comentarios el nombre del usuario y el importe ingresado. 

ElseIf Target.Column = 9 Then

    'evaluar si la celda no tiene aún comentario, o sea es el primero a registrar

    If Target.CommentThreaded Is Nothing Then

        Target.AddCommentThreaded Application.UserName & "_" & Target.Value

    Else

        'si ya tiene algún comentario se agrega una 'respuesta'

        Target.CommentThreaded.AddReply Application.UserName & "_" & Target.Value

    End If

End If

End Sub



NOTA: Si utilizamos el mismo libro en diferentes versiones, los comentarios creados en versión Excel clásico se verán en Excel 365 tal como fueron creados, solo que ahora estarán en el menú 'Notas'.
En cambio los nuevos comentarios creados desde Excel 365, al pasarlos a la versión Excel clásico se verán como los de siempre (con formato) y con un aviso de parte de Microsoft, donde se informa de que los cambios realizados aquí ya no se verán como 'Respuestas' en la versión 365.



Acceso al VIDEO N° 76 desde aquí.


martes, 12 de marzo de 2024

75 - Ejecutar macros dinámicamente

 Las macros en Excel pueden ejecutarse de varias maneras:

- al clic de un botón ActiveX, un botón de la barra Formulario o algún control dentro de un Userform.

- al clic en algún objeto incrustado en la hoja.

- con la instrucción Call nombre_de_macro . De esta manera las llamamos desde cualquier otro proceso. Por ejemplo: al abrir un Userform deseamos primeramente ordenar una lista para rellenar algún control ComboBox o ListBox, tendremos una instrucción del tipo: Call ordena_Clientes

- desde algún evento de Libro o de Hojas.

- de modo dinámico. Es decir, que sin invocar su nombre, podremos llamar a la macro por el contenido de alguna celda o selección dentro de un desplegable.

Las macros ya estarán ubicadas en un módulo. El nombre de cada macro tendrá que coincidir con el contenido de las celdas o con los elementos de una lista desplegable si fuera el caso.

Ejemplo Nº 1:  al doble clic en celdas de meses o conceptos de Caja, se llamará a la macro según el contenido de la celda: En la hoja de Caja, se colocará el siguiente código:

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

'controla posible error al seleccionar una celda sin macro asociada.

On Error Resume Next                

'ejecuta la macro cuyo nombre coincida con el contenido de la celda activa 

Application.Run (Target.Value) 

'coloca el foco en otra celda. Aquí, 1 fila hacia abajo en la misma columna. 

Target.Offset(1, 0).Select

End Sub 



Ejemplo Nº 2:  al seleccionar algún elemento del desplegable, se ejecutará la macro cuyo nombre coincida con el elemento seleccionado. El código ubicado en el Userform será:

Private Sub ComboBox2_Click()
On Error Resume Next
Application.Run (ComboBox2.Text)
End Sub

                
            En uno o varios módulos tendremos las macros:

Sub INGRESOS()

MsgBox "Estoy ejecutanto la macro de INGRESOS!"

End Sub 

 

Sub EGRESOS()

MsgBox "Estoy ejecutanto la macro de EGRESOS!"

End Sub


Sub ENERO()

MsgBox "Estoy ejecutanto la macro de ENERO!"

End Sub


Sub FEBRERO()

MsgBox "Estoy ejecutanto la macro de FEBRERO!"

End Sub


Sub TALLER_CORTE()

MsgBox "Estoy ejecutanto la macro de TALLER_CORTE!"

End Sub


Sub TALLER_COSTURA()

MsgBox "Estoy ejecutanto la macro de TALLER_COSTURA!"

End Sub

 

NOTA: En caso de tener otras hojas donde los nombres, por ejemplo de Meses, aparecen en minúscula se antepondrá la función UCASE al valor de la celda.

 

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim nbreHoja As String
If Target.Column = 3 And Target.Row > 2 Then
    nbreHoja = UCase(Target.Value)      'convertir texto en mayúsculas
    Application.Run (nbreHoja)
    Target.Offset(1, 0).Select
End If
End Sub

 


                    Acceso al VIDEO Nº 75 desde aquí.