Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   manejo correcto de OnValidate (https://www.clubdelphi.com/foros/showthread.php?t=43441)

FGarcia 11-05-2007 07:39:20

manejo correcto de OnValidate
 
Hola!

Cuando se ejecuta este codigo se intenta validar que el campo placas no sea un valor duplicado o vacio, se le envia un mensaje de informacion al usuario. El problema consiste en que segun yo deberia de cancelarse el post a la bd y sin embargo no es asi, esto lo se porque si genero cualquiera de los dos errores al final me aparece el mensaje de error de que se ha violado el campo placas pues se intento ingresar un campo null. Lo interesante es que si se llena el edit que recibe esos datos y se intenta nuevamente el post el mensaje sigue apareciendo como si alguna operacion quedara inconclusa. No he podido encontrar el error. Se agradece la ayuda.

Código Delphi [-]
procedure TfrmDatos.MDOEntradaPLACASValidate(Sender: TField);
var
  Query: TMDOSQL;
begin
  if Trim(MDOEntrada.FieldByName('PLACAS').AsString) <> '' then
  begin
    Query := TMDOSQL.Create(nil);
      try
        Query.Database := frmCnx.MDODatabase1 ;
        Query.Transaction := frmCnx.MDOTransaction3 ;
        Query.SQL.Text := 'SELECT Placas FROM ENTRADA WHERE Placas = :Valor';
        Query.ParamByName('Valor').Value :=
                                  MDOEntrada.FieldByName('PLACAS').AsString;
        query.ExecQuery ;
        try
          if Trim(Query.Fields[0].AsString) <> '' then
            begin
              showmessage('¡Estas Placas estan duplicadas!');
              Abort; //es correcto esto?
            end;
        finally
          query.Close ;
        end;
      finally
        query.Free;
      end;
  end
  else
  begin
    ShowMessage('El campo placas es necesario');
    Abort ;
  end;
end;

Lepe 11-05-2007 13:11:57

Ahhh, que estas usando Firebird.... pues esos tipos de errores pasaron a la historia amigo.

Una de las formas puede ser esta:
- En la base de datos indicas que el campo es NOT NULL.
- En tu aplicación colocas un ApplicationEvents
- En el evento OnException del applicationsEvents añades el siguiente código.

La filosofía: Al ser NOT NULL, firebird lanzará una excepción cuando se intenta guardar un campo sin valor e indica el siguiente mensaje:
"Violation of constraint FK_FACTURA" (lo digo de memoria)

Lo que hacemos, en el ApplicationEvents, es interceptar la excepción, buscar la palabra "FK_FACTURA" y si está en el mensaje, mostramos nuestro texto en Español. Como es el objeto "Application" en ese evento centralizamos todos los errores de todas las tablas de nuestra aplicación, así usamos el mismo código para toda la Base de datos.

¿Qué es FK_FACTURA?
FK significa Foreign Key, significa que en la tabla FACTURA, se ha definido una clave ajena sobre un campo y este campo no puede ser nulo, así que Firebird crea una restricción (llamada Constraint). También se puede llamar PK_FACTURA, (Primary Key), es decir, en este caso significa que la clave primaria de la tabla FACTURA, no puede tener el valor nulo.

¿Cómo saber que la restricción se llama FK_FACTURA?
Usa el IbExpert, en la tabla que has definido el NOT NULL, aparece una pestaña constraint (restricción) sobre el campo en cuestión, si tienes varios, se llamarán FK_FACTURA_1, FK_FACTURA_2, etc, de ahí sacas el nombre.

En el ApplicationEvents-> OnException
Código Delphi [-]
const saltolinea = #10#13;
        saltodoble = saltolinea + saltolinea;


  if (e is EMDOError) then
  begin
    strDebug := 'SqlCode: ' +inttostr(EMDOError(E).SQLCode) + ' '+ E.ClassName;
    strMessage:=dtm.ParseError(EMDOError(E));
    dtm.log.Add(strMessage + saltolinea + strDebug);
  end;
    ShowMessage('Al usuario: ' + strMessage + saltodoble +
                'Mensaje de Debug: ' + strDebug + saltodoble );

Las funciones de ayuda son:
Código Delphi [-]
type Restriccion= record
      Nombre:string;
      Mensaje:string;
end;
const iRestricciones = 8;
var RestriccionesBD : array [1..iRestricciones,0..1] of string =
        (('FK_ENTREGA_3', 'El Tipo de pago no se puede borrar, se ha usado en una entrega'),
         ('FK_FACTURA_1', 'El Proveedor no se puede borrar, se ha usado en una Factura'),
         ('FK_JORNAL_1', 'El Trabjador no se puede borrar, tiene jornales trabajados'),
         ('FK_OBRA_2', 'El Estado de la obra no se puede borrar, se ha usado en una obra'),
         ('FK_TRABAJADOR_1','La Categoría no se puede borrar, un Trabajador la tiene asignada'),
         ('FK_TRABAJADOR_2', 'La Situación del trabajador no se puede borrar, se ha usado para otro trabajador'),
         ('FK_VENCIMIENTO_2','La Forma de pago no se puede borrar, se ha usado en un Vencimiento'),
         ('FK_VENCIMIENTO_4','El Proveedor no se puede borrar, ha sido usado en un Vencimiento.')
        );

function IsBDConstraint(ExpMessage:string; var SpanishError:string): Boolean;
var i:Integer;
begin
  Result:= False;
  for I := low(RestriccionesBD) to high(RestriccionesBD) do
    if Pos(restriccionesBD[i,0],ExpMessage)<> 0 then
    begin
      Result := True;
      SpanishError := restriccionesbd[i,1];
      Break;
  end;

end;

function Tdtm.ParseError(E: EMDOError):string;
const numeros = ['0'..'9'];
var  idx:Integer;
      strError:string;
begin
  Result := E.Message;
  if IsBDConstraint(E.Message, strError) then
  begin
    Result := strError;
  end
  else if e.SQLCode = -836 then 
  begin  
// es una excepción lanzada desde la BD
// Ejemplo: "exception 1: La cantidad suministrada no es válida"
// quitamos el texto "exception 1:"
    idx := pos(saltolinea,Result);
    if (idx <> 0) and ( idx < 13) then
     Delete(Result,1,idx+1); //fuera el texto : "exception X " + CRLF
  end;
end;

Otra cosa, veo que buscas con un query si la placa ya existe en la BD, eso también pasó a la historia:
- Si es un campo clave primaria, no tendrás problema, ya que no permite duplicados, así que todo el código que he escrito arriba, contempla ese caso.

- Si el campo "placa" no es clave primaria, define un índice único sobre él, así Firebird lanzará un mensaje de error cuando intentes guardar una placa repetida. Se deduce por tanto, que este caso también se contempla con el código de arriba.

Como ves, haciendo ese código una sola vez manejas todos los errores posibles sin tener que hacer virguerías.

En resumen, todo el código funcionaría en tu aplicación, lo único que tienes que modificar la variable RestriccionesBD para añadir tus restricciones, y cambiar la constante iRestricciones.

Saludos y espero que se entienda, si no es así, dime algo en este hilo.

FGarcia 11-05-2007 19:44:47

Gracias Lepe por la ayuda!

Apenas estoy implementando el codigo y me surgen varias preguntas (bueno el compilador gruño):

¿en donde esta declarado EMDOError?

Result := E.Message; // Aqui dice que falta un operador o un "semicolon"
if IsBDConstraint(E.Message, strError) then // Aqui faltan parametros
begin

Nuevamente gracias!!

roman 11-05-2007 20:13:35

Yo usaría el evento OnPostError del dataset que se esté usando. Porque si centralizamos todo en ApplicationEvents, corremos el riesgo de terminar con un evento DIOS que hace de todo en lugar de la sana repartición de responsabilidades.

// Saludos

luisgutierrezb 11-05-2007 22:32:39

me llama la atención este trozo de codigo:

Código Delphi [-]
        try
          if Trim(Query.Fields[0].AsString) <> '' then
            begin
              showmessage('¡Estas Placas estan duplicadas!');
              Abort; //es correcto esto?
            end;
        finally
          query.Close ;
        end;

y me llama la atención por una cosa, si el Registro NO esta duplicado, entonces te regresa un Dataset Vacio por lo tanto, esto no es valido:
Query.Fields[0].AsString
lo que mas bien debes comprobar es:
if not Query.isEmpty then ....

FGarcia 12-05-2007 01:22:01

hola! si en minusculas
ya no se ni que, ni cual, ni si seguir con esto del firebird o me regreso a access:p

Bueno ya con mas animo :rolleyes: deshice todas las validaciones que tenia deje el programa funcionando tal cual hasta antes de empezar con la comprobacion de errores, nuevamente cree la bd con sus claves primarias y restricciones de campos UNIQUE.

La pregunta ¿cual seria la manera de manejar los errores de campo NULL? (y errores en general) ¿Donde se validarian en FB o en delphi? ¿como se validarian?

jovenes nuevamente agradezco su apoyo!

roman 12-05-2007 01:31:51

Cita:

Empezado por FGarcia
¿cual seria la manera de manejar los errores de campo NULL? (y errores en general) ¿Donde se validarian en FB o en delphi? ¿como se validarian?

Este tipo de restricciones deben estar en la base de datos, porque al motor no se le va una y a nosotros puede que sí. Es decir, el motor de la base siempre será infalible para detectar este tipo de errores: campos requeridos, duplicación de claves, claves foráneas inexistentes, etc.

Pero, tal como explica Lepe, cuando un intento de violación de alguna de estas restricciones suceda, el motor lo notifica al cliente en forma de excepción. Esta excepción puede ser capturada en el evento OnPostError del dataset que estés usando, o dejar que pase hasta llegar a ApplicationEvents, como describe Lepe.

De cualquier forma que lo hagas, realmente no estás tú validando los datos; es el motor de la base quien lo hace, y tu aplicación simplemente maneja la notificación de un posible error.

// Saludos

FGarcia 12-05-2007 02:42:03

De nuevo por aqui!

bueno considerenme como de lento aprendizaje. Hice estas excepciones y triggers:

Código SQL [-]
//LA EXCEPCION
CREATE EXCEPTION NO_PLACAS 'EL NUMERO DE PLACAS ES NECESARIO';

//EL TRIGGER
AS
begin
  if (NEW.placas IS null ) then
    exception NO_PLACAS;
end
//lA EXCEPCION
CREATE EXCEPTION PLACAS_2 'LAS PLACAS ESTAS DUPLICADAS';

//EL TRIGGER
AS
begin
  if (NEW.placas IS not null ) then
    EXCEPTION PLACAS_2;
end

les recuerdo que estoy haciendo un post a la bd y que estoy tratando de capturar los errores al tratar de ingresar un campo vacio o con datos duplicados. ¿Bueno ahora que hago con esto? ¿ Cual seria el codigo a poner? ¿en BeforePost o en PostError?

una vez mas les agradesco su ayuda y atencion.

Lepe 12-05-2007 04:20:08

EMDOError está declarado en mdo.pas

Lo demás fijo que no son erroes, es que no sabe lo que era EMDOError.

Veo que el hilo ha avanzado mucho, pero hace falta tener las cosas algo más claras.

Si el campo placas está declarado como NOT NULL, y en tu programa creas un registro y le das a guardar (sin modificar nada), ya Firebird protestará con un mensaje de error "violation of constraint ENTRADA_1 for table ENTRADA" o algo similar, por tanto, no es necesario ni crear trigger ni excepciones. Pruebalo para que veas como funciona la cosa.

Ahora aplicas una mezcla entre lo dicho por roman y lo mío, esto es, en el dataset entrada de tu form de delphi, localiza el evento onPostError, y añade ShowMessage mostrando los parámetros que trae el evento para ver que te muestra. Ahora en ese evento es donde tendrás que adaptar las rutinas "parseError" y también "IsBDConstraint" para tu caso particular.

Lo mismo para las placas duplicadas, si pones un indice único sobre el campo, Firebird ya protesta, por ende, no hace falta crear la excepción, es más en este caso está mal planteado, ya que si introduce un número de placa correcto (no duplicado), en el trigger solo se compara si es distinto de null y salta la excepción, asi que, no dejará introducir ningún número de placa, ¡ninguno!

OFFTOPIC: Que no te vea yo declarar una excepción así:
Código SQL [-]
CREATE EXCEPTION PLACAS_2 'LAS PLACAS ESTAS DUPLICADAS';
que te pego un tirón de orejas, crealá así:
Código SQL [-]
CREATE EXCEPTION PLACAS_DUPLICADAS  'LAS PLACAS ESTAS DUPLICADAS';



Si quieres dejar pasar las excepciones al ApplicationsEvent, no uses el OnPostError para nada.

Efectivamente me parece más adecuado usar el evento OnPostError del dataset porque está precisamente para eso, para detectar errores al guardar.

Saludos

FGarcia 12-05-2007 05:50:27

Lepe, Roman: Muchas gracias por la ayuda, la verdad estaba harto pero ya va saliendo esto.

Efectivamente elimine los triggers y exception de la bd ya que tenia los campos declarados como UNIQUE coloque el showmessage para ver el mensaje de horror como dijo Lepe y en realidad no tuve que modificar nada de IsConstraint pues ya lo habia declarado pero como existia la excepcion habia un conflicto en los mensajes a mostrar al eliminar las excepciones quedo el mensjae de Isconstraint.

Aunque aun persiste un error al parecer la operacion de post no se cancela o se queda en el limbo pues aunque corriga el dato de placa el mensaje sigue siendo el mismo y si cierro la aplicacion me aparece el mensaje de "RunTime error 216 at 00438CF9"

Caray! ya parece serie de TV "To be continued"

Gracias

FGarcia 12-05-2007 20:08:39

Hola!

Bueno despues de tratar de averiguar que pasaba con esa operacion que se quedaba en el limbo acabe haciendo esto:

Código Delphi [-]
//Al generarse un error durante el post
procedure TfrmDatos.MDOEntradaPostError(DataSet: TDataSet;
  E: EDatabaseError; var Action: TDataAction);
begin
  //Si es un error de la base de datos
  if (e is EMDOError) then
  begin
    strDebug := 'SqlCode: ' +inttostr(EMDOError(E).SQLCode) + ' '+ E.ClassName;
    strMessage:= ParseError(EMDOError(E));
    
    Action := daAbort;
  end;
  
  //Y esto fue todo!!!
  MDOEntrada.Close ;
  MDOEntrada.Open ;
  
  //Mostramos el mensaje de error al usuario
  ShowMessage('Al usuario: ' + strMessage + saltodoble +
                'Mensaje de Debug: ' + strDebug + saltodoble );
end;

¡Que cosas!¿no?

:D :D :D :D
Nuevamente me repito muchas gracias por su ayuda y atencion!!!!


p.d. ¡Amenazo con regresar!:eek:


La franja horaria es GMT +2. Ahora son las 23:48:30.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi