jueves, 26 de febrero de 2015

Recorrer subformulario

Esta es una de las cosas, que estuve años haciéndolo manual, hasta que Patxi Sanz me enseñó a hacerlo, después con el paso del tiempo, me doy cuenta que existen más formas de hacerlo, así que les voy a mostrar varias de ellas, para este ejemplo conté con la colaboración de MexMan70. Veremos 7 formas diferentes que podemos llamar:

1.-Recordsetclone directo
2.-Recordsetclone con recordcount
3.-Recordsetclone con absoluteposition
4.-En la tabla con Recordset
5.-Consulta con Execute
6.-Consulta con DoCmd.RunSQL
7.-Desde el subformulario

Vamos a necesitar una tabla, le llamaremos Productos y constará de 4 campos id Autonumerico, Nombre (texto), Precio (número) e Importe (número), y agregamos algunos registros:


Productos
id Nombre Precio Importe
1 Tomate 3 6
2 Cebolla 4 8
3 Papa 5 10
4 Lechuga 6 12


Creamos un formulario en blanco, y  en vista diseño agregamos la tabla Productos y la dejamos caer dentro del formulario, nos pregunta que nombre le queremos dar y le decimos que Subproductos, agregamos un cuadro de texto y 7 botones, luego iremos cambiando los nombres, se ve más o menos así:




1.-Recordsetclone directo
Podemos decir que Recordsetclone es una copia de la consulta o tabla de la que se basa el formulario y se usa para navegar y realizar operaciones en los registros del formulario. Y si hacemos un bucle podemos recorrerlos, nosotros lo haremos con Do Loop para ir actualizando cada registro.
Ahora en vista diseño vamos a ponerle de nombre: Cantidad al cuadro de texto y al primer botón lo llamaremos RecordClone y en eventos al hacer click le damos a los 3 puntos y le decimos que generador de código y nos lleva a vba, y entre Private Sub RecordClone_Click() y End Sub ponemos:

With Me.SubProductos.Form.RecordsetClone
.MoveFirst
Do Until .EOF
    .Edit
    !Importe = !Precio * Me.cantidad
    .Update
    .MoveNext
    Loop    
End With

Podemos probar nuestro primer Botón, poniendo algún número en el campo cantidad y dándole al botón y debe verse parecido a esto:




2.-Recordsetclone con recordcount
Es una pequeña variante del anterior solo que ahora usaremos un For Next y contaremos los registros con RecordCount asi que en el Botón segundo le cambiamos el nombre a RecordCloneCount y de nuevo, nos vamos a eventos, al hacer click y generador de código y ponemos:
Private Sub RecordCloneCount_Click()
Dim rs As DAO.Recordset 
Dim i As Integer 
Set rs = Me.SubProductos.Form.RecordsetClone 
If Not rs.BOF Then 
rs.MoveFirst 
For i = 1 To rs.RecordCount 
    rs.Edit 
    rs!Importe = Me.cantidad * rs!Precio 
    rs.Update 
    rs.MoveNext 
Next i
End If
rs.Close
Set rs = Nothing 
End Sub

Probamos el nuevo botón.
3.-Recordsetclone con absoluteposition
Este es muy parecido al primero solo que cuando lleguemos al final de los registros, nos vamos a salir cuando AbsolutePosition sea igual al total de los registros. En el tercer botón le cambiamos nombre y le ponemos RecordCloneAbsolute, y nos vamos a eventos, al hacer click y generador de código y ponemos:
Private Sub RecordCloneAbsolute_Click()
With Me.SubProductos.Form.RecordsetClone
 .MoveFirst
   Do
    .Edit
    !Importe = !Precio * Me.cantidad
    .Update
     If .AbsolutePosition = .RecordCount - 1 Then Exit Do
     .MoveNext
   Loop
End With
End Sub
Probamos el nuevo botón.
4.-En la tabla con Recordset
Con un Recordset directo en la tabla, también puede ser bastante fácil, en el cuarto botón le cambiamos nombre y le ponemos Recordset, y nos vamos a eventos, al hacer click y generador de código y ponemos:
Private Sub Recordset_Click()
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT id, Nombre, Precio, Importe FROM Productos", dbOpenDynaset)
rs.MoveFirst
Do While Not rs.EOF
    rs.Edit 
    rs![Importe] = rs!Precio * Me.cantidad 
    rs.Update
    rs.MoveNext
Loop
rs.Close
Set rs = Nothing
Me.Refresh
End Sub

Probamos el nuevo botón.
5.-Consulta con Execute
Las consultas son las formas más directas y quizás las más rápidas, podemos usar Execute y DoCmd.RunSQL  y usando variable o no, vamos a ver la primera, en el quinto botón le cambiamos nombre y le ponemos CtaExecute, y nos vamos a eventos, al hacer click y generador de código y ponemos:

Private Sub CtaExecute_Click()
Dim SQL As String
SQL = "UPDATE Productos SET Importe = Precio * '" & Me.cantidad & "'"
    CurrentDb.Execute SQL, dbFailOnError
Me.Refresh
End Sub

Probamos el nuevo botón.
6.-Consulta con DoCmd.RunSQL
Las consultas son las formas más directas y quizás las más rápidas, podemos usar Execute y DoCmd.RunSQL  y usando variable o no, vamos a ver la segunda y sin variable, en el sexto botón le cambiamos nombre y le ponemos CtaRunSQL, y nos vamos a eventos, al hacer click y generador de código y ponemos:
Private Sub CtaRunSQL_Click()
DoCmd.RunSQL "UPDATE Productos SET Importe = Precio * '" & Me.cantidad & "'"
Me.Refresh
End Sub

Probamos el nuevo botón.
7.-Desde el subformulario
Esta es una idea de Patxi en la que en cada registro del subformulario tenemos que crear el procedimiento que actualiza registro por registro y para poder usarlo en el formulario principal declaramos el procedimiento como Public en el subformulario. Entonces aquí primero tenemos que irnos al subformulario y en el control Importe, eventos, después de actualizar, generador de código y Primero cambiamos Private por Public y ponemos:

Public Sub Importe_AfterUpdate()
Me.Importe = Me.Precio * Me.Parent.cantidad
End Sub

Después en el séptimo botón le cambiamos nombre y le ponemos Subform, y nos vamos a eventos, al hacer click y generador de código y ponemos:

Private Sub Subform_Click()
Me.SubProductos.SetFocus
    With Me.SubProductos.Form
        .Precio.SetFocus
        DoCmd.GoToRecord , , acFirst
        Do While Not .NewRecord
            .Importe_AfterUpdate
            DoCmd.GoToRecord , , acNext
        Loop
    End With
End Sub

Y está listo para probarlo.



Puede bajar el archivo
https://www.dropbox.com/s/5fudwwgrsbrw5ry/Recorrosubfor.mdb?dl=0




6 comentarios:

  1. Hola Emilio he usado la opcion uno para modificar un campo de un subformulario, pero me da error 2950 no entiendo porque. yo tengo access 2010. Alguna idea

    ResponderBorrar
  2. Hola uso todos los ejemplos u nada no me hace el recorrido por los registros, tengo 7 filas y solo pasa la primera y ahí se queda

    ResponderBorrar
  3. El 7 tiene error porque no puede ir al registros siguiente, el 2 funciona muy bien.

    ResponderBorrar
  4. muchas gracias ,me ayudo bastante.
    para el 7
    lo hice de esta forma:
    Private Sub act_Click()
    Dim nro_reg As Integer

    nro_reg = Me.N_F_Datos_Planilla_Notas_SUB.Form.Recordset.RecordCount ' nro de registros para el cicli
    Me.N_F_Datos_Planilla_Notas_SUB.SetFocus

    With Me.N_F_Datos_Planilla_Notas_SUB.Form
    .N1.SetFocus
    DoCmd.GoToRecord , , acFirst
    Do While nro_reg > 1
    .N1_AfterUpdate
    DoCmd.GoToRecord , , acNext
    nro_reg = nro_reg - 1
    Loop
    DoCmd.GoToRecord , , acLast
    .N1_AfterUpdate
    End With
    End Sub

    me funciono bien.
    saludos.

    ResponderBorrar
  5. donde N1 es un campo que tengo en el subformulario N_F_Datos_Planilla_Notas_SUB y que tiene su propia funcion.

    ResponderBorrar
  6. Buenas noches... estoy trabajando con un Inventario de productos, pero al momento de actualizar el stock en la tabla productos me suma o resta la cantidad igual al numero de productos que tenga en el subformulario. no se que estare haciendo mal.

    El codigo que estoy empleando para recorrer el subformulario es el siguiente:

    With Me.Subformulario_Detalle_Salida_Factura.Form.RecordsetClone
    .MoveFirst
    Do While Not .EOF
    CurrentDb.Execute "UPDATE Productos INNER JOIN Detalle_Salida_Factura ON Productos.Cod_Productos = Detalle_Salida_Factura.Cod_Productos SET Productos.Stock = Productos.Stock - Cantidad WHERE Productos.Cod_Productos = Detalle_Salida_Factura.Cod_Productos;"
    .MoveNext
    Loop
    End With

    Le agradezco la ayuda que me pueda prestar.. soy novato en esto..

    ResponderBorrar