PDA

Ver la Versión Completa : Prolema con guardar datos usando un tStringGrid


jose001
03-07-2016, 02:51:05
Hola buenas tardes estoy trabajando con una aplicacion de venta usando mysql y zeos para lo cual cargo en un TstringGrid los datos como (idproducto,nombre,cantidad,precio,subtotal) luego al apretar el boton aceptar se guarda el idventa,fecha,idcliente y el total que seria la suma de todos los sub totales. El tema es el siguiente cuando guardo los datos en la base se cargan 2 filas una con los valores de la venta realizada y otra fila vacia con todos los campos null
por ejemplo en la tabla venta
idventa
total
idcliente
fecha

se carga lo siguiente para dejarlo mas claro
idventa:1
idcliente:null
total:null
fecha:null
y en otra fila se carga por fin los datos guardados
idventa:2
idcliente:2
total:20
fecha:2016-07-02





Dejo el codigo del boton aceptar


procedure TfVentas.bAceptarClick(Sender: TObject);
var
i:integer;
begin

try
fmodulo.conexion.StartTransaction;
with fmodulo,self do
fmodulo.qVentas.Active:=true;
fmodulo.qVentas.Insert;
fmodulo.qVentas['idcliente'] := idcliente;
fmodulo.qFecha.Open;
fmodulo.qVentas['fechavent'] := fmodulo.qFecha['fecha'];
fmodulo.qVentas['horavent']:= fmodulo.qFecha['hora'];
fmodulo.qVentas['totalventa']:= eTotal.text;
fmodulo.qFecha.Close;
fmodulo.qVentas.Post;
with fmodulo,self,sgDetalle do
begin
fmodulo.qdetalle.Active:=true;
fmodulo.qProductos.Active:=true;
for i := 1 to rowcount - 2 do
begin
fmodulo.qdetalle.Insert;
fmodulo.qdetalle['idventa']:= fmodulo.qVentas['idventa'];
fmodulo.qdetalle['idproducto']:= strtoint(sgDetalle.Cells[0,i]);
fmodulo.qdetalle['precio_venta']:= strtofloat(sgDetalle.Cells[2,i]);
fmodulo.qdetalle['cantidad_venta']:= strtofloat(sgDetalle.Cells[3,i]);
fmodulo.qdetalle.Post;

if fmodulo.qProductos.Locate('idproducto',strtoint(sgDetalle.Cells[3,i],[]))= True then
begin
fmodulo.qProductos.Edit;
fmodulo.qProductos['cantidadpr']:= fmodulo.qproductos['cantidadpro']-StrToInt(cells[2,i]);
fmodulo.qProductos.Post;
end;
end;
end;
finally
fmodulo.conexion.Commit;
end;


Del boton que utilizo para seleccionar los productos


procedure TfListadosProductos.bSeleccionarClick(Sender: TObject);
begin
with fventas,fventas.sgDetalle,fmodulo do
fventas.sgDetalle.Cells[0,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['idproducto'];
fventas.sgDetalle.Cells[1,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['nombrepr'];
fventas.sgDetalle.Cells[2,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['precio_unit'];
fventas.sgDetalle.Cells[3,fventas.sgDetalle.RowCount-1]:= eCantidad.Text ;
subtotal:= fmodulo.qProductos['precio_unit']*StrToInt(self.eCantidad.Text) ;
fventas.sgDetalle.Cells[4,fventas.sgDetalle.RowCount-1]:= FloattoStr(subtotal);
total:=total+subtotal;
fventas.eTotal.Text:= floattostr(total);
fventas.sgDetalle.RowCount:= fventas.sgDetalle.RowCount+1;
end;

espero que puedan ayudarme con este problema muchas gracias

AgustinOrtu
03-07-2016, 03:42:30
No das informacion suficiente. No sabemos que son todos los componentes de acceso a datos que estas usando: qFecha, qVentas, etc. Sobre estos componenes invocas al metodo Open y no sabemos si estaras ejecutando un SQL

Ademas, tu codigo tiene varios problemas. En ningun lado chequeas por errores; el resumen es:


Conexion.StartTransaction;
try
codigo
finally
Conexion.Commit;
end;


Eso es un problema grave porque si se graba algun dato (ocurre un .Post) y luego mas adelante se produce un error (sobre todo con tanta conversion de string a otro tipo), se ejecuta el bloque finally, es decir, se ejecuta el Commit y te queda la informacion a medias (inconsistente)

Te sugiero que lo hagas de esta manera:


Conexion.StartTransaction;
try
codigo

fin del codigo
Connection.Commit;
except
Conexion.Rollback;
end;



Por otra parte, el codigo es muy complicado de leer: with entre 2 o 3 objetos, luego otro with anidado. Es imposible saber que objeto recibe los mensajes

Ademas, el modo que usas para asignar los valores a cada campo es peligroso, ya que estas asignando a un Variant


fmodulo.qVentas['idcliente']


Cuando en realidad el Variant se deberia usar si "no se el tipo del dato", pero aca los conoces perfectamente por lo que va a ser mas seguro, rapido y facil de entender si lo cambias por esto:


fmodulo.qVentas.FieldByName('idcliente').AsInteger := VariableInteger;

...
fmodulo.qVentas.FieldByName('algun_campo').AsString := VariableString;
fmodulo.qVentas.FieldByName('algun_campo').AsFloat := VariableSingle/Double/Extended;
fmodulo.qVentas.FieldByName('algun_campo').AsCurrency := VariableCurrency;

etc
...

jose001
03-07-2016, 20:33:18
No das informacion suficiente. No sabemos que son todos los componentes de acceso a datos que estas usando: qFecha, qVentas, etc. Sobre estos componenes invocas al metodo Open y no sabemos si estaras ejecutando un SQL

Ademas, tu codigo tiene varios problemas. En ningun lado chequeas por errores; el resumen es:


Conexion.StartTransaction;
try
codigo
finally
Conexion.Commit;
end;


Eso es un problema grave porque si se graba algun dato (ocurre un .Post) y luego mas adelante se produce un error (sobre todo con tanta conversion de string a otro tipo), se ejecuta el bloque finally, es decir, se ejecuta el Commit y te queda la informacion a medias (inconsistente)

Te sugiero que lo hagas de esta manera:


Conexion.StartTransaction;
try
codigo

fin del codigo
Connection.Commit;
except
Conexion.Rollback;
end;



Por otra parte, el codigo es muy complicado de leer: with entre 2 o 3 objetos, luego otro with anidado. Es imposible saber que objeto recibe los mensajes

Ademas, el modo que usas para asignar los valores a cada campo es peligroso, ya que estas asignando a un Variant


fmodulo.qVentas['idcliente']


Cuando en realidad el Variant se deberia usar si "no se el tipo del dato", pero aca los conoces perfectamente por lo que va a ser mas seguro, rapido y facil de entender si lo cambias por esto:


fmodulo.qVentas.FieldByName('idcliente').AsInteger := VariableInteger;

...
fmodulo.qVentas.FieldByName('algun_campo').AsString := VariableString;
fmodulo.qVentas.FieldByName('algun_campo').AsFloat := VariableSingle/Double/Extended;
fmodulo.qVentas.FieldByName('algun_campo').AsCurrency := VariableCurrency;

etc
...


Hola disculpa no me exprese bien y me faltaron cosas por decir utilizo componentes Tquery de zeos en los cuales tengo uno de detalle y otro tquery de ventas en cada uno tengo una sentencia sql que es la siguiente
Query venta
SELECT
`venta`.`idventa`,
`venta`.`totalventa`,
`venta`.`estadoventa`,
`venta`.`idcliente`,
`venta`.`fechavent`,
`venta`.`horavent`

FROM
`venta`

Query detalle venta
SELECT
`detalle_venta`.`idventa`,
`detalle_venta`.`idproducto`,
`detalle_venta`.`cantidad_venta`,
`detalle_venta`.`precio_venta`,
`detalle_venta`.`estado_detalleventa`
FROM
`detalle_venta`

// en esta sentencia amigo me tira el siguiente error "Invalid operation in AutoCommit mode"
Connection.Commit;
except
Conexion.Rollback;
end;

Aun soy muy nuevo en esto soy estudiante, y ese es el codigo que me dijieron que utilize para trabajar con el TstringGrid me dijieron que debia hacer esas conversiones para guardar los datos.Por ejemplo si deberia guardar

fmodulo.qdetalle['idproducto']:= strtoint(sgDetalle.Cells[0,i]);
esta linea quedaria algo parecido a esto ?
fmodulo.qdetalle.FieldByName('idproducto').AsInteger:= strtoint(sgDetalle.Cells[0,i]);
gracias amigo por la respuesta y la ayuda

AgustinOrtu
03-07-2016, 21:21:50
Con respecto al AutoCommit no puedo ser muy preciso, porque nunca he usado Zeos. Pero todo apunta a que hay una propiedad AutoCommit que por defecto viene a True, y habria que pasarla a False. Yo me he acostumbrado a realizar manejo explicito de transacciones, es decir, decidir cuando inician y como finalizan

Para la segunda consulta si, me parece mucho mas adecuado que uses la forma .AsInteger/.AsString

Otra sugerencia es eliminar los with de tu codigo

Por ejemplo, cuidado con este metodo que tienes en el boton de seleccionar:


procedure TfListadosProductos.bSeleccionarClick(Sender: TObject);
begin
with fventas,fventas.sgDetalle,fmodulo do
fventas.sgDetalle.Cells[0,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['idproducto'];
fventas.sgDetalle.Cells[1,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['nombrepr'];
fventas.sgDetalle.Cells[2,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['precio_unit'];
fventas.sgDetalle.Cells[3,fventas.sgDetalle.RowCount-1]:= eCantidad.Text ;
subtotal:= fmodulo.qProductos['precio_unit']*StrToInt(self.eCantidad.Text) ;
fventas.sgDetalle.Cells[4,fventas.sgDetalle.RowCount-1]:= FloattoStr(subtotal);
total:=total+subtotal;
fventas.eTotal.Text:= floattostr(total);
fventas.sgDetalle.RowCount:= fventas.sgDetalle.RowCount+1;
end;


No entiendo muy bien el porque usas el with en ese caso. Pero vamos por partes

Primero que el with al no estar delimitado por un bloque begin-end, solamente "afecta" a la siguiente instruccion inmediata. Es decir, en tu caso (coloreo con negro la linea que es "afectada" por el with, y le agrego un sangrado para que quede bien claro)


procedure TfListadosProductos.bSeleccionarClick(Sender: TObject);
begin
with fventas,fventas.sgDetalle,fmodulo do
fventas.sgDetalle.Cells[0,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['idproducto'];

fventas.sgDetalle.Cells[1,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['nombrepr'];
fventas.sgDetalle.Cells[2,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['precio_unit'];
fventas.sgDetalle.Cells[3,fventas.sgDetalle.RowCount-1]:= eCantidad.Text ;
subtotal:= fmodulo.qProductos['precio_unit']*StrToInt(self.eCantidad.Text) ;
fventas.sgDetalle.Cells[4,fventas.sgDetalle.RowCount-1]:= FloattoStr(subtotal);
total:=total+subtotal;
fventas.eTotal.Text:= floattostr(total);
fventas.sgDetalle.RowCount:= fventas.sgDetalle.RowCount+1;
end;


Fijate como el resto de las instrucciones no son alcanzadas por tu with

Por otra parte, concentremonos en lo que quiere transmitir tu codigo. Mas alla de que no se aconseja el uso del with por los varios problemas que puede tener (sobre todo la ambiguedad), uno usa un with para "ahorrarse" el trabajo de no volver a escribir a que objeto le quiere mandar un mensaje

Pero aun asi, usas el with sobre un objeto, y en el cuerpo del codigo sigues referenciando el objeto en cuestion. Osea, el with esta totalmente de mas!


procedure TfListadosProductos.bSeleccionarClick(Sender: TObject);
begin
with fventas,fventas.sgDetalle,fmodulo do
fventas.sgDetalle.Cells[0,fventas.sgDetalle.RowCount-1]:= fmodulo.qProductos['idproducto'];


fventas no lo usas nunca
En el with, colocas un fventas.sgDetalle, pero en el codigo, en vez de usar "la forma abreviada" y obviar el fventas.sgDetalle, lo vuelves a escribir

El mismo caso es para fmodulo

Ese mismo codigo se puede escribir asi, resultando mucho mas claro de leer:


procedure TfListadosProductos.bSeleccionarClick(Sender: TObject);
begin
fventas.sgDetalle.Cells[0, fventas.sgDetalle.RowCount - 1] := fmodulo.qProductos['idproducto'];
// resto del codigo


Este mismo problema lo tenes en el codigo de grabado:


with fmodulo,self do
fmodulo.qVentas.Active:=true;

// mas codigo


Sin embargo, mas abajo en el mismo metodo usas un begin-end para un with


with fmodulo,self,sgDetalle do
begin
// codigo
end;


Pregunta: Por que usas el with? Para que sirve? Quien te dijo que uses with? Usaste algun codigo de ejemplo para guiarte?. En definitiva, te recomiendo no usarlo (mejor aprenderlo desde novato asi no te queda como vicio)

Luego te recomiendo que "embellezcas" tu codigo:

1- Utiliza la sangria de forma inteligente: Respeta los distintos niveles de anidamiento de los bloques (facilita la lectura). Ejemplo:


if AlgunaCondicion then
begin
instruccion1;
instruccion2;
if OtraCondicion then
begin
instruccion2.1;
instruccion2.2
end;
end;


Eso es mucho mas facil de leer que esto, o similares, donde no se respeta un orden:


if AlgunaCondicion then
begin
instruccion1;
instruccion2;
if OtraCondicion then
begin
instruccion2.1;
instruccion2.2
end;
end;


2 - Si bien en Delphi no se distinguen mayusculas de minusculas (yo lo veo como un gran error de diseño), el escribir todo en mayusculas o todo en minusculas hace mas dificil leer el codigo. Piensa en alguien que da un discurso y usa siempre el mismo tono de voz, sin ningun tipo de pausa. Esto es lo mismo.


fmodulo.qdetalle.fieldbyname('idproducto').asinteger:= strtoint(sgdetalle.cells[0,i]);

FModulo.qDetalle.FieldByName('IdProducto').AsInteger:= StrToInt(sgDetalle.Cells[0, I]);


Esto implica tambien respetar los nombres de las cosas que ya vienen implementadas. Por ejemplo, nadie te obliga a seguir la notacion PascalCase para escribir tu propio codigo (es cierto que estoy tocando un tema muy personal) pero si me parece correcto que lo que ya esta escrito de una manera, respetarlo: lo mas probable es que el resto de los programadores escribamos StrToInt y no strtoint o strToInt. De este modo tu codigo queda idiomatico y es mas facil de leer

3 - No gastas mas memoria por ponerle nombres largos a las variables :D. Es preferible un nombre de variable largo a uno corto. Es mas importante que se entienda bien para que sirve. No se en donde se aprendio que los nombres de las variables son siempre: tot, sum, i, temp, x, y, z, temp_x... ponle el nombre de lo que realmente significan!

4 - Aprende a depurar un programa. No importa si eres novato, intermedio, experto o entusiasta. Programar sin saber depurar es como pretender correr sin saber caminar. Una cosa lleva a la otra.

El depurador de Delphi es increible, podes seguir paso a paso como se ejecuta tu codigo, detenerte en el momento que quieras, ver el valor de todas las variables del programa, ver que es lo que exactamente hace, seguir el flujo de ejecucion, incluso el depurador te permite modificar los valores de las variables para "ver que pasa si aca en lugar de 100 vale 101".
Incluso podes monitorear los valores de las variables para ver como se van actualizando

Hay muchos hilos sobre como depurar en el foro, con una busqueda encontraras muchos resultados

Casimiro Notevi
04-07-2016, 10:11:48
Buenísima y didáctica la respuesta :)
2 - Si bien en Delphi no se distinguen mayusculas de minusculas (yo lo veo como un gran error de diseño), el escribir todo en mayusculas o todo en minusculas hace mas dificil leer el codigo.
^\||/^\||/^\||/