Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Forma correcta de guardar un Maestro-Detalle (https://www.clubdelphi.com/foros/showthread.php?t=92147)

wilcg 08-08-2017 05:17:27

Forma correcta de guardar un Maestro-Detalle
 
Amigos del foro un cordial saludo, necesito su ayuda con respecto a este tema de como es la manera mas correcta de guardar datos de un maestro-detalle
en la base de datos. Utilizo componentes UNIDAC y base de datos POSTGRESQL.

Tengo un ClientDataset en donde hay datos en memoria, lo que estoy haciendo es lo siguiente:

Código Delphi [-]
with UniQuery1 do
 begin

   try   
     // MAESTRO
     Close;
     SQL.Clear;
     SQL.Add('INSERT INTO factura ( '+
        ' id_factura, fecha_emision, id_proveedor, id_comprobante, observaciones ) VALUES ( '+
        ' :id_factura, :fecha_emision, :id_proveedor, :id_comprobante, :observaciones )
        );
     ParamByName('id_factura').Value         := iDFactura;
     ParamByName('fecha_emision').Value      := FormatDateTime( F_FF, edtFEmision.Date );
     ParamByName('id_proveedor').Value       := edtProveedor.Value;
     ParamByName('id_comprobante').Value     := edtComprobante.Value;
     ParamByName('observaciones').AsString   := edtObservaciones.Lines.Text;
     ExecSQL;

     // DETALLE
     if (ClientDataset1.Active) then
     begin
        Marca := ClientDataset1.Bookmark;

        ClientDataset1.First;
        while Not ClientDataset1.Eof do
        begin
          Close;
          SQL.Clear;
          SQL.Add('INSERT INTO detalles ( '+
            ' id_factura, id_producto, id_medida, cantidad, costo_unidad, precio_venta ) VALUES ( '+
            ' :id_factura, :id_producto, :id_medida, :cantidad, :costo_unidad, :precio_venta )'
            );
          ParamByName('id_factura').Value          := iDFactura; 
          ParamByName('id_producto').Value         := ClientDataset1.FieldByName('id_producto').Value;
          ParamByName('id_medida').Value           := ClientDataset1.FieldByName('id_medida').Value;
          ParamByName('cantidad').AsFloat          := ClientDataset1.FieldByName('cantidad').AsFloat;
          ParamByName('costo_unidad').AsFloat      := ClientDataset1.FieldByName('costo_unidad').AsFloat;
          ParamByName('precio_venta').AsFloat      := ClientDataset1.FieldByName('precio_venta').AsFloat;
          ExecSQL;

        ClientDataset1.Next;
      end;

      ClientDataset1.Bookmark := Marca;
      Application.MessageBox(pchar(  'Registro creado correctamente.'),pchar(' Mensaje'),
            MB_OK+MB_ICONINFORMATION);
   except
     Application.MessageBox(pchar(  'Surgieron errores durante la creación del registro.'),
            pchar(' Mensaje'),MB_OK+MB_ICONERROR);

   end;

 end;

Si me pueden ayudar con este tema, o un ejemplo mucho mas correcto de realizar este tipo de operaciones.

duilioisola 08-08-2017 10:49:32

Yo persobalmente haría dos bloques try...except.
En el caso de que falle el segundo, podría ser que desearas borrar la cabecera para no dejar restos o cosas incompletas.
Además, si recorres un DataSet que estás mostrando deberías deshabilitarlo antes y habilitarlo después. Todo esto dentro de un bloque try...finally.
Finalmente,, y solo por estética, prefiero crear los SQL con varios SQL.Add().

Te dejo a continuación mi versión...

Código Delphi [-]
with UniQuery1 do
begin
  try 
    // Al principio estoy segudo de que no hay errores
    HayError := False;
    
    // MAESTRO
    Close;
    SQL.Clear;
    SQL.Add(' INSERT INTO factura ( ');
    SQL.Add(' id_factura, fecha_emision, id_proveedor, id_comprobante, observaciones ) ');
    SQL.Add(' VALUES ( ');
    SQL.Add(' :id_factura, :fecha_emision, :id_proveedor, :id_comprobante, bservaciones ) ');
    ParamByName('id_factura').Value         := iDFactura;
    ParamByName('fecha_emision').Value      := FormatDateTime( F_FF, edtFEmision.Date );
    ParamByName('id_proveedor').Value       := edtProveedor.Value;
    ParamByName('id_comprobante').Value     := edtComprobante.Value;
    ParamByName('observaciones').AsString   := edtObservaciones.Lines.Text;
    ExecSQL;
  except
    on E:Exception do
    begin
      HayError := True;
      Application.MessageBox(pchar('Surgieron errores durante la creación del registro cabecera.' + #13#10 + E.Message),
      pchar(' Mensaje'),MB_OK+MB_ICONERROR);
    end;
  end;

  // Si no hubo errores al crear la cabecera sigo con el detalle
  if (not HayError) then
  begin
    try
      // DETALLE
      Marca := ClientDataset1.Bookmark;
      
      // Deshabilito refrescos del DataSet
      ClientDataset1.DisableControls;
      try
        if (ClientDataset1.Active) then
        begin
          ClientDataset1.First;
          while Not ClientDataset1.Eof do
          begin
            Close;
            SQL.Clear;
            SQL.Add(' INSERT INTO detalles ( ');
            SQL.Add(' id_factura, id_producto, id_medida, cantidad, costo_unidad, precio_venta ) ');
            SQL.Add(' VALUES (  ');
            SQL.Add(' :id_factura, :id_producto, :id_medida, :cantidad, :costo_unidad, recio_venta ) ');
            ParamByName('id_factura').Value          := iDFactura; 
            ParamByName('id_producto').Value         := ClientDataset1.FieldByName('id_producto').Value;
            ParamByName('id_medida').Value           := ClientDataset1.FieldByName('id_medida').Value;
            ParamByName('cantidad').AsFloat          := ClientDataset1.FieldByName('cantidad').AsFloat;
            ParamByName('costo_unidad').AsFloat      := ClientDataset1.FieldByName('costo_unidad').AsFloat;
            ParamByName('precio_venta').AsFloat      := ClientDataset1.FieldByName('precio_venta').AsFloat;
            ExecSQL;

            ClientDataset1.Next;
          end;
        end;
        ClientDataset1.Bookmark := Marca;
      finally
        // Vuelvo a habilitar refrescos del DataSet
        ClientDataset1.EnableControls;
      end;

      Application.MessageBox(pchar(  'Registro creado correctamente.'),pchar(' Mensaje'),
      MB_OK+MB_ICONINFORMATION);
    except
      on E:Exception do
      begin
        Application.MessageBox(pchar(  'Surgieron errores durante la creación del registro.'  + #13#10 + E.Message),
        pchar(' Mensaje'),MB_OK+MB_ICONERROR);
      end;
    end;
  end;
end;

bitbow 08-08-2017 21:04:57

Lo ideal si tienes pensado manejar un correcto control de errores, seria usar transacciones con lo cual si hay algun error solo haces rollback y listo.

Saludos.

wilcg 09-08-2017 03:43:18

Gracias amigos por responder, es de mucha ayuda sus respuestas.^\||/^\||/


La franja horaria es GMT +2. Ahora son las 07:19:14.

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