Cuando abro un formulario me aparece todos los campos con el símbolo de error

Necesito ayuda/consejo sobre cómo plantear el tema de los índices y las relaciones. Ya sé que a simple vista no guarda relación con el título, pero he intentado poner alguno más acorde y me da error.

Bien, me explico.

Para poder relacionar dos campos de distintas tablas, por ejemplo, el campo CodigoCliente de la tabla TClientes con el campo CodigoCliente de la tabla TTPV, es necesario que sean del mismo tipo (texto, numérico...), pero nunca, nunca autonumérico. Y aquí está el primer problema.

Yo lo que hago es utilizar una función para crear ese código. Por ejemplo, este:

Public Function GeneracionDeNuevoCodigo(FName As Form, Codigo As String, Tabla As String, Tipo As String, Optional Posicion As Boolean) 'Clientes, formas de pago, categorías
    Dim vAutonum, vUltimo As Variant
    If FName.NewRecord Then
        vAutonum = FName.Controls(Codigo)
        If Not vAutonum = "" Then Exit Function
            vUltimo = Nz(DMax(Codigo, Tabla), 0)
            If IsNull(vUltimo) Then
                vUltimo = 0
            End If
            vUltimo = vUltimo + 1
            FName.Controls(Codigo).Value = vUltimo
    End If
End Function

El problema que tiene utilizar esta función para añadir el codigo que se relaciona con el campo de otra tabla, es que, si creo ese nuevo registro (utilizo la función de arriba para rellenar el campo Codigo, que se relaciona, en este caso, con el campo del mismo nombre en la tabla Artículos):

Y cierro sin introducir ningún valor más (sin rellenar el nombre de la categoría ni seleccionar si está o no activo), al abrir me aparece así:

La solución pasa por eliminar ese registro de la tabla para que vuelva a aparecer bien el formulario:

Además, con este código tengo el problema de que me añade un nuevo registro si utilizo la barra del buscador y no hay coincidencias. Esto lo he arreglado de esta manera:

        If FName.NewRecord Then
            FName.Undo
        End If

Por último, una vez añadido el Codigo, no puede cancelar la entrada del registro, por lo que tengo que borrarlo manualmente en la tabla, ya que el formulario se pone con los símbolos de error que has visto antes.

Por otra parte, del código que puse antes, tengo la versión extendida:

Public Function GeneracionDeNuevoCodigo(FName As Form, Codigo As String, Tabla As String, Tipo As String, Optional Posicion As Boolean) 'Clientes, formas de pago, categorías y posición
    Dim vAutonum, vUltimo As Variant
    If FName.NewRecord Then
        vAutonum = FName.Controls(Codigo)
        If Not vAutonum = "" Then Exit Function
            If MsgBox("Vas a añadir " & Tipo & "." & vbCrLf & vbCrLf & "¿Quieres seguir?", vbYesNo, NombreBD) = vbYes Then
                vUltimo = Nz(DMax(Codigo, Tabla), 0)
            If IsNull(vUltimo) Then
                vUltimo = 0
            End If
            vUltimo = vUltimo + 1
            FName.Controls(Codigo).Value = vUltimo
            Else
                FName.Undo
                DoCmd.GoToRecord , , acPrevious
                Exit Function
            End If
    End If
End Function

Este te pregunta si quieres añadir un nuevo registro. Si le dices que no, te va al registro anterior. Y aquí viene el problema. Si no hay ningún registro, da error (cuando estás creando una base de datos y aún no has metido el primer registro). Además, cuando utilizas el buscador, si no hay registros, te pregunta si quieres añadir uno nuevo, y tanto si sí, como si no, tienes el problema. Por tanto, en formularios donde tengo un buscador no puedo usar este código.

Yo no sé de qué manera puedo plantear la generación del código, de tal forma de que consiga lo siguiente:

  1. Me introduzca un código sin necesidad de tener que recurrir a una pregunta para evitar registros vacíos.
  2. Que cuando cierre el formulario, tome ese código como si lo generara automáticamente, de tal manera que elimine el registro en que solo esté el código.
Respuesta
2

No te voy a responder a pregunta ni resolverte el problema porque a saber dónde tiene su origen entre todo el código que le tienes metido a tu BD...

Lo que sí te voy a comentar es un par de cosas que te ayudarán de cara al futuro:

1º/ Cuando dices: "Para poder relacionar dos campos de distintas tablas, por ejemplo, el campo CodigoCliente de la tabla TClientes con el campo CodigoCliente de la tabla TTPV, es necesario que sean del mismo tipo (texto, numérico...), pero nunca, nunca autonumérico. ", eso no sé de donde lo has sacado, pero no es del todo cierto.

Un campo autonumérico lo puedes relacionar con un campo numérico de otra tabla sin problema, siempre que sean del mismo tipo de dato numérico (un autonumérico es Entero Largo, y no lo podrías relacionar, por ejemplo con un Byte)

2º/ Acostúmbrate a usar controles de error en tus procedimientos y funciones, para ser capaz de actuar ante errores concretos. Por ejemplo, en tu caso, el problema de que no haya registro anterior, con un control de errores se resuelve fácilmente.

3º/ Si ya tienes un campo autonumérico, que va a ser único e invariable, úsalo para relacionar las tablas entre sí, y no uses otro campo (sobre todo si tienes otro código para renumerar tu código personalizado al borrar algún registro). Usar el autonumérico tiene la ventaja, como te decía, de que es único y fijo), por lo que si tienes un cliente con ID (autonumérico) 100 y CodigoCliente (autonumerico personalizado) de 100, si borras un registro anterior, el ID seguirá siendo 100 y tu CodigoCliente, si lo recalculas, será por ejemplo el 99, pero en la tabla relacionada, el identificador seguirá siendo el 100, lo que no te obliga a realizar ningún otro cambio de CodigoCliente en las tablas vinculadas.

4º/ En tus funciones hay una cosa que me chirría, y es que a la variable vAutonum (que es de tipo Variant, aunque no se lo indiques (y sobre esto vuelvo en el punto siguiente...)), le pasas un valor (vAutonum = FName. Controls(Codigo)), y en la linea el if la comparas con una cadena vacía (If not vAutonum=""), cuando lo que deberías validar es si es o no nula, con IsNull.

Solo veo dos motivos para usar esa comparación: que al asignarle un valor uses Nz para convertir los nulos en cadenas vacías (cosa que no haces), o que sepas, 100%seguro, que el campo del formulario que le asignas no es nulo sino una cadena vacía (que no son lo mismo)

5º/ Sobre la declaración de variables: es un error muy común pensar que si pones:

Dim unaVar, otraVar As Integer

Estás declarando dos variables de tipo Integer, cuando lo que realmente tienes es una variable de tipo Variant (unaVar) y una variable de tipo Integer (otraVar)

Hola. Muchas gracias por la información. Me ha abierto mucho la mente con respecto a este tema. Quiero preguntarte alguna cosa al respecto. Voy a ir recorriendo cada uno de los puntos que tú has puesto:

  1. Yo probé a relacionar un autonumérico con un campo que fuera numérico, y no me dejó. Luego, leyendo en la ayuda de Microsot, leí algo a respecto, y debí entenderlo mal. Así que, sobre este punto, no tengo más dudas. No hace falta que me contestes sobre él.
  2. Estoy aprendiendo sobre esto. De hecho, estos días he estado probando a utilizar Debug.Print, pero no soy capaz de verle toda la utilidad, y tampoco encuentro mucha información en internet que me enseñe cómo usarlo. Sobre el problema de que no haya un registro anterior, ¿puedes darme una pista de cómo podría solucionarlo? Olvídate de cómo tenga montada la base de datos, que yo me adapto.
  3. Este punto es superimportante para mí. A ver, yo lo que estoy haciendo últimamente sobre bases de datos para llevar la gestión de negocios. Entonces, hay tablas que sí que tienen que respetar un orden. Y aquí tengo dos casos:
    1. En las tablas donde inserto facturas y presupuestos, por ley, tienen que llevar un código correlativo, sin saltos. Yo lo que suelo hacer es añadir un campo autonumérico, con la clave, y otro campo que sea, por ejemplo, CodigoFactura. Este último es el que utilizo para relacionar la tabla Facturas (donde van los datos generales), con tabla de FacturasSubtabla (donde van cada concepto). Olvídate de cómo lo tenga yo montado, ¿vale? Quiero saber cómo lo harías tú teniendo en cuenta de que cada factura tiene que tener un código que sea correlativo a la anterior, y que el usuario pueda eliminar una factura y siga respetando el orden correlativo. Aquí hay dos casos:
      1. Puede eliminar la última. Es el caso más fácil.
      2. O puede eliminar una de entre medias, es decir, por ejemplo, tengo factura1, factura2, factura3, factura4 y factura5, y elimino la 3, ¿cómo arreglo eso?
    2. Con respecto al orden de los conceptos de cada factura o presupuesto. Es importante que en la factura se muestre en el orden en que se ha añadido, y este orden no se altere al eliminar un concepto. De ahí que yo añada en la tabla FacturasSubtabla un campo Posicion y lo vaya rellenando sumando uno por cada concepto añadido. Si el usuario elimina un concepto, esta Posicion se vuelve a regenerar. No sé si con con el campo autonumérico se me respetaría la posición. ¿Tú cómo lo plantearías?
  4. No tengo comentarios al respecto. Muchas gracias por esta corrección.
  5. Sobre la declaración de variables. Acabo de leer la ayuda de Microsoft y llevas razón. Eso tengo que corregirlo. Muchas gracias.

Nada más. Espero tu respuesta. Muchas gracias. Un saludo.

Te respondo mañana con calma, y si puedo, te preparo un mini-ejemplo para lo de los presupuestos y facturas

Sin prisa. Una última cosa que me he acordado más tarde. Si tienes dos tablas relacionadas por una relación CampoAutonumerico-Numerico, y haces una copia de esa base de datos, pero con las tablas vacías, ¿podrías pegar los valores de la primera base de datos? Te explico para qué. Lo que suelo hacer es que, cuando toco algo de la base de datos, me hago una copia de la base de datos, y luego lo que hago es copiar los datos de las tablas a la base de datos copia que he utilizado para añadir las mejoras.

¡Gracias!

Tengo un rato libre, te voy respondiendo a algunos puntos:

2º/ Control de errores y debug. Print:

Son dos cosas distintas: el debug. Print sirve para comprobar la correcta ejecución del código y depurar posibles errores, visualizando en la "Ventana inmediato" la información que quieras, por ejemplo, los valores que toman variables (te hubiera sido muy útil con el problema de la SQL del otro día, pues te hubieras dado cuenta de que estabas cogiendo mal un nombre), mostrar cálculos, mostrar información de dónde se está ejecutando el código... las posibilidades son muchas.

El control de errores sirve fundamentalmente para interceptar un error y actuar en consecuencia (normalmente haciendo "algo" o lanzando un mensaje) y además evitar que te salga el típico aviso de error de Access con la opción de ir a depurar (si le das esa opción a un usuario final, probablemente la líe gorda...)

Por ejemplo: en un botón para ir al registro anterior, el código típico sería:

On Error GoTo Sol_err
DoCmd. GoToRecord,, acPrevious
Salida:
Exit Sub
Sol_err:
If Err.Number = 2105 Then
    MsgBox "Ya estás en el primer registro"
Else
    MsgBox "Error " & Err.Number & ":" & vbCrLf & Err.desc
End If

porque si ya estás en el primer registro, al pulsarlo se produce el error 2105. Entonces lo "interceptas" y avisas al usuario de que no hay para dónde ir. La parte del else, en este caso concreto se podría omitir, pues es el único error con el que te vas a encontrar con esa acción. Pero no está de más acostumbrarse a ponerla.

Si quieres profundizar más, en la web de Neckkito (http://neckkito.xyz/nck/), en el apartado de "Manuales" puedes encontrar uno mío de Access (en el cap. 7 hablo un poco de los controles de errores), el curso de VBA de Neckkito (tiene un capítulo dedicado a ellos), y también una mini-guía de depuración.

3º/ A esto te respondo luego con el ejemplo.

En cuanto a la última duda: Si usas SQL para copiar los datos, sí te mantiene los IDs autonuméricos. Si haces un simple copia-pega, creo que no, pero no te lo aseguro.

En todo caso, creo que haces el proceso al revés.

Yo lo que hago es una copia de la BD, con datos y todo, hago las modificaciones sobre esa copia, las testeo y cuando veo que todo va bien, importo los formularios, informes, módulos... a la BD original. Así los datos no se alteran y no me tengo que preocupar por si pierdo información.

Releyendo el punto 3 que tenemos pendiente, no me hace falta realmente prepararte un ejemplo (aunque si quieres, me lo pides), creo que de palabra te lo puedo explicar y lo vas a entender.

Olvidémonos del supuesto de que si eliminas un factura o presupuesto "intermedio" se renumeran de nuevo todos los registros para que vayan correlativos sin saltos (legalmente, con una factura esto no lo podrías hacer, en vez de eliminar una factura se haría una nueva de abono o rectificativa, con su propio número, pero la factura inicial no se borraría...).

En la tabla, que es como lo haría yo, tienes un campo ID, autonumérico y clave principal, y luego un campo NumFact como "falso autonumérico" (que se podría indexar sin duplicados), que rellenas automáticamente con un código como el que tienes u otro similar. Mientras no elimines ningún registro, ni canceles la inserción de alguno (es decir, que empieces a escribir y luego canceles el registro haciendo un Undo, pulsando Esc...) ID y NumFactura irán parejos. Si eliminas algún registro, o cancelas alguno, empezarán las diferencias.

En la tabla DetalleFacturas, también tendría un campo ID, autonumérico y clave principal, y un campo NumFactura que lo relacionaría con el ID autonumérico de la tabla Facturas, y no con el campo NumFactura. Solo con tener este campo ID autonumérico, ya tienes solucionado lo que preguntas del orden, sin tener que recurrir a código para calcular el campo posición, porque el ID autonumérico ya te da el orden de inserción. Imagina que estás en la primera factura o presupuesto y empiezas a meter conceptos en el detalle. El primero tendrá el ID 1, el segundo el ID 2, e tercero el ID 3. Luego resulta que el cliente te dice que no quiere el primer concepto sino que quiere otro. Pues lo borras y añades el nuevo, que tendrá ID 4. Ahora tu factura/presupuesto tiene 3 conceptos, con los IDs 2, 3 y 4, y ya solo con ese campo ya sabes en qué orden se añadieron: primero el 2, luego el 3 y por último el 4. No necesitas para nada (en mi opinión) un campo Posición y rellenarlo por código. Si por lo que sea necesitas mostrar en la hoja que imprimes o envías esa posición, como lo adecuado sería hacerlo con un informe, puedes añadirle un cuadro de texto independiente y usar la propiedad Suma continua para obtenerlo sin ninguna dificultad.

Pongámonos ahora en el caso extremo de que al eliminar un registro, el NumFactura se recalcule para no dejar huecos. En este caso, y según cómo hayas creado la relación entre las tablas, puede que tengas 2 problemas:

1º/ Que elimines el registro en la tabla facturas, pero no los registros asociados en DetallesFacturas, con lo que al renumerar te quedarán asociados los conceptos de la factura eliminada a la nueva factura que coja ese número.

2º/ Que al renumerar un registro en Facturas, no se actualicen los registros en DetallesFacturas, lo que implicará que te queden las facturas/presupuestos con los conceptos incorrectos.

Si por medio de la relación entre tablas no se actualizan y eliminan en cascada (y aún así yo iría con cuidado al renumerar y lo testearía bien antes de empezar a usarla), todos estos aspectos los tienes que controlar por código, eliminando los conceptos de la tabla Detalles y renumerandolos a los nuevos números de factura.

Sin embargo, si usas el campo ID de la tabla principal como parte de la relación, evitas de un plumazo todos esos inconvenientes, porque te da lo mismo que el NumFactura sea el 50 y luego pase a ser el 49, y en otro momento el 20. Los conceptos, al ir asociados al ID, que siempre será el mismo, estarán siempre bien.

Hola, muchas gracias por tus comentarios. No necesito que me envíes ningún ejemplo. Me sirve así de mucho, porque estoy aprendiendo sobre algo a lo que no sabía darle un solución efectiva. Voy a aplicarlo en la base de datos que utilizamos para la tienda. Siguiendo tu recomendación, las voy a relacionar mediante un ID autonumérico-Codigo Numérico, también las de facturas y presupuestos.

En las tablas de facturas y presupuestos, no me queda claro dos cosas.

  1. Si lo montas como tú dices, con un ID autonumérico y un CodigoFactura en la Tabla Facturas, ¿qué más da que haya diferencias entre el ID y el CodigoFactura? Con bloquear la posibilidad de eliminar registros en el formulario que utilices para esa Tabla Facturas, no va a ver posibilidad de que haya saltos en la numeración. Es más, por eso veo útil preguntar al crear un nuevo registro, de esa manera previenes de que el usuario cree un nuevo registro sin darse cuenta. Otra opción para evitar esto es que con el tabulador no pase al registro siguiente, y tenga que, explicítamente, crear uno nuevo cuando quiera. Cuando creas un registro nuevo, no lo borras dando cancelar o con Me.Undo, porque ya has rellenado un campo que no sea el ID autonumérico, ¿me explico?
  2. Lo de la posición te lo dije porque en la última base de datos que hice salía en el informe un campo con la posición de cada artículo. Si utilizas el ID, claro que sabes la posición, pero no te aparece 1, 2, 3..., sino, por ejemplo, 253. Además, hay otro campo calculado para mostrar la marca de las ventanas (la base de datos que te digo era para una empresa de ventanas), y para no repetir en cada artículo, lo vinculé a la posición 1. Esto se podía haber resuelto fácil utilizando la función mínimo, pero como de todas formas tenía que aparecer la posición, por eso lo vinculé al 1.

El que haya diferencia entre el id autonumérico y el personalizado realmente no importa ni influye para nada, solo lo puse para clarificar mi exposición. De hecho, el id autonumérico solo tiene la función de identificar a cada registro de forma única y de establecer la relación entre las dos tablas. El autonumérico no debería aparecer en ningún formulario ni informe.

Que haya saltos en el id autonumérico tampoco te importa por lo anterior. Lo que te interesa es que no los en el "autonumérico personalizado".

En cuanto a la posición, si la necesitas en un formulario, si necesitas un campo en una tabla o consulta (supongo que donde van los detalles será un formulario continuo)

En un informe, lo resuelves con un cuadro de texto independiente con un "=1" en su origen de control y suma continúa en "grupo"

1 respuesta más de otro experto

Respuesta
1

Diego te digo que me he perdido, así que voy a divagar. Si el campo CodigoCliente de la tabla Clientes es autonumérico( le dejas el control a Access) es lógico que no te deje que el campo CodCliente de la otra tabla sea también autonumérico, ya que la relación sería de 1 a 1, cuando lo lógico parece que UN cliente VARIAS Compras, por tanto el segundo CodCliente debe ser numérico.

Segundo. Vamos a suponer que el CodCliente de la tabla Clientes es numérico. Si quieres que en un registro nuevo del formulario Clientes, le asigne un valor correlativo, aunque sea el primer registro, basta con poner, en el evento Al activar el registro del formulario Clientes

If me.newrecord then

CodCliente=Nz(Dmax("codcliente","clientes"))+1

End if

El cero del final no hace falta, ya que la función NZ se creó para transformar los nulos en ceros.

Y si quieres que si hubieras creado un registro nuevo pero no le hayas puesto datos, puedes hacerlo de muchas formas, por ejemplo, en el evento Al cerrar del formulario Clientes puedes poner( hay más formas)

docmd.setwarnings false

Docmd.runsql"delete * from clientes where categoria is null"

Que lo quieres eliminar antes de cerrar el formulario, basta con poner en algún evento lo anterior con una línea más.

Me. Requery

Es decir, que el propio formulario elimine ese registro de la tabla y luego reconsulte su origen de registro, que es la propia tabla, donde ya no está el registro eliminado.

Vamos a suponer que en la misma instrucción con la que creas un CodCliente, quieres que te cree un registro con ese codcliente en la tabla TTPV, basta conque le añadas

docmd.runsql"Insert into TTPV(codcliente)values(codcliente)"

Aunque, honestamente, no sé para que sirve.

Si lo que quieres es que te apatrezca un mensaje( aunque tampoco veo para que sirve) puedes ponerlo como

If me.newrecord then

dim respuesta as byte

respuesta=msgbox("¿Quiere crear un registro nuevo?",vbyesno+vbquestion,"No diga que no le pregunté")

If respuesta=vbyes then

codcliente=nz(......

elseif respuesta=vbno then

docmd.cancelevent

end if

End if

Si le dices que no el cursor se vuelve al control CodCliente para que decidas que hacer.

Joe, Julián. Muchas gracias por aportar siempre tu punto de vista. Me sienta fatal, pero me anoto todas tus ideas. Me da muchas pistas sobre cómo resolver los problemas que tengo, pero si puedo atajarlos desde la raíz, creo que lo voy a intentar.

Como te digo, muchas, muchas gracias. Un saludo.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas