FoxPro/Visual FoxPro - Boton eliminar

   
Vista:

Boton eliminar

Publicado por Luis (11 intervenciones) el 24/07/2013 23:18:36
hola amigos, tengo un Grid asociado a una tabla "Productos" y un combobox con todos los codigos de los productos!
Y quisiera saber como seria el codigo para el boton eliminar?, que elimine segun el codigo seleccionado el combobox!
Nota: la tabla no tiene indice!

He intentado ya con varios codigos que he conseguido en este foro y algunos me han funcionado pero a su vez estos codigos me arrojan otro problema; que al intentar cambiar de ese formulario a un segundo formulario a traves del Menu, me el siguiente error:

Nota: este segundo formulario tiene un combobox que se carga al iniciar con datos de la tabla productos, Como se ve en la siguiente imagen:
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder

Boton eliminar

Publicado por Fidel (558 intervenciones) el 24/07/2013 23:38:02
El error que muestras te surges porque tomas como base Reccount() para la cantidad de items.
Reccount() te devuelve el total de registros de la tabla, incluyendo los marcados para borrar. Si tienes SET DELETED ON, los marcados para borrar no se verán y tendrás menos registros.
En su lugar debes usar COUNT to CantidadDeRegistros (para Set DELETED ON)
Si SET DELETED OFF y quieres contar los que no están borrados:
COUNT FOR !DELETED() TO nCantidaddeRegistros

De todos modos, si "Producto" es un código que no se repite, es más fácil lo siguiente:
Select productos
WITH thisform.CmbProductos
Scan
.AddItem(producto)

endscan
ENDWITH

Aun así, no alcanzo a comprender para qué necesitas el ComboBox si ya tienes el grid.
Si tienes familias de productos, puedes utilizar un ComboBox que contenga las familias y a partir de ahí se muestran en el Grid solamente los productos de la familia del ComboBox. Incluso el ComboBox puede tener una opción "(Todas las Familias)" y mostrar todo de entrada en el Grid y luego se filtra por la familia buscada.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Luis (11 intervenciones) el 25/07/2013 00:30:08
Entiendo, Fidel. Y como seria el codigo correcto del codigo del boton eliminar?
Nota: producto no es un codigo, es un campo del tipo: Caracter. Lo uso para que los productos con nombres iguales se muestren solo una vez en un combobox.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Fidel (558 intervenciones) el 25/07/2013 01:15:35
1) Para que no se repitan los nombres pero que aparezcan todos los que no son iguales, debes tomar algún recaudo adicional. Pongo un ejemplo, usando una matriz de prueba. Esto es mucho más rápido que utilizar la matriz del ComboBox.
Acá en el array gaprod, los productos aparecen con dos separadores que los vuelven inconfundibles, cualquiera sea el estado de SET EXACT. Por otra parte, SCAN es mucho mejor que cualquier otro método de recorrer una tabla. Con Ascan() busca en la matriz. Si no lo encuentra, lo agrega al combo y a la matriz y sigue.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local array gaprod(1)
local np
np=0
Select productos
WITH thisform.CmbProductos
	.Clear
	Scan
		if ascan(gaprod,"<"+alltrim(producto)+">")=0
			.AddItem(trim(producto))
			np=np+1
			dimension gaprod(np)
			gaprod[np]="<"+alltrim(producto)+">"
		endif
 
	endscan
	.requery
ENDWITH


2) Por la restricción que impones sobre la formación del ComboBox, no puedes pretender ordenar borrar sobre la base de la selección del combo. Salvo que la tabla esté admitiendo valores que no deben admitirse y con el combo estés intentando una especie de SET UNIQUE pero sin indexar.
Los productos deben tener una identificación tal que permitan que el vendedor no se confunda cuando vende. Puede que tengan la misma denominación, pero deberías tener otro campo en la tabla para diferenciarlos. Entonces ya no son iguales. Si son iguales, la tabla tiene registros repetidos y eso solo puede provocar problemas.

Con el combo puedes buscar en la tabla y posicionar el control GRid como para refinar la búsqueda. Por ejemplo en el Click del combo pones:
Select (thisform.grid1.recordSource)
locate for producto=alltrim(this.value)
with thisform.grid1
.refresh
.SetFocus
endwith
Si el control grid es .AlowCellSelection=.T. (Default), se producirá el evento AfterRowcolChange.

Para el botón eliminar, se supone que logras el posicionamiento de registro con el Control GRid. Por lo tanto, en AfterRowColChange del Grid debería haber una instrucción que habilite el botón de Eliminar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
* CmdEliminar.Click
lcFile=Thisform.Grid1.RecordSource
select (lcFile)
if eof()	&& comprueba si está al final del archivo.
	Messagebox("Señale un registro en la cuadrícula")
	thisform.grid1.set focus
	return
endif
 
nMess=Messagebox("Desea eliminar este registro?",4,"Mensaje")
if nMess=6
	if rlock()     && para tablas SHARED
		DELETE
		UNLOCK && para tablas SHARED
	ENDIF
	Thisform.grid1.refresh
	if eof()
		this.Enabled=.f.	&& Sin registros?
	endif
endif
thisform.grid1.setfocus


En general, no parece una buena práctica Eliminar productos directamente. Antes habría que chequear que esos productos no se utilizaron nunca. Si se utilizaron y ya no existen (obsoletos, desaparecidos, etc.), debes prever un campo lógico en la tabla para indicar que están en desuso.
Si los eliminas, no podrás utilizar las referencias.
Puedes recorrer la tabla con
SCAN FOR !DESUSO

ENDSCAN

Generar un cursor con SELECT * FROM PRODUCTOS WHERE !DESUSO INTO CURSOR Cuproduc && SELECT SQL pasa por alto cualquier SET FILTER.

Incluso un SET FILTER TO !DESUSO no será pesado, aun cuando no aconsejo el uso de SET FILTER.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Luis (11 intervenciones) el 25/07/2013 03:11:27
Me gusta tu idea, pero me gustaria que me dieras el ejemplo del:
1
2
3
4
5
6
Select (thisform.grid1.recordSource)
locate for producto=alltrim(this.value)
with thisform.grid1
.refresh
.SetFocus
endwith

Pero del tipo numerico. Osea, que en vez de tomar en cuenta el campo "producto" que tomaras en cuenta este campo numerico "id_producto".

Yo supuse que para eso solo debia modificar el siguiente codigo y intente hacerlo asi:
1
2
3
4
5
6
Select (thisform.grid_inventario.recordSource)
locate for id_producto=(this.value)
with thisform.grid_inventario
.refresh
.SetFocus
endwith

Pero no me funciono del todo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Fidel (558 intervenciones) el 25/07/2013 15:53:47
Luis:
De acuerdo a lo que muestras del código, el combobox no se llenaba con el id_producto.
Es más, si Id_producto es un Interger o Numeric, no puedes utilizar AddIdtem(Id_producto), sino que necesitas transformar en caracter .AddItem(transform(Id_Producto))
Luego, cuando tienes que buscar por el Id_Producto, tienes que volver a transformar.
1
2
3
4
5
6
Select (thisform.grid_inventario.recordSource)
locate for id_producto=Val(this.value)
with thisform.grid_inventario
.refresh
.SetFocus
endwith


En el ejemplo siguiente, en lugar de Additem, utilizo AddListitem() y pongo en el combo tanto el nombre como el id.
Acá se agrega una propiedad (IDSEL) al formulario con el objeto de controlar la estabilidad de combo. El valor de Idsel está asignado a la columna 2 del combo por BoundColumn=2
Así, en principio, el combobox se presentará diciendo "Seleccione producto"
Thisform.Idsel es un Character, por lo que si decides utilizarlo en algún lugar del form, tienes que transformarlo a valor [ Val(thisform.idsel) ].
Recuerda que la propiedad del ControlSource se actualiza recién en el lostFocus, por lo que, si tienes un método o procedure que se lanza desde Click, interactiveChange, etc,. el valor de la propiedad thisform.Idsel será el anterior.

Igualmente, te dejo mi opinión. Este combobox puede funcionar bien cuando los registros son unos pocos. Si tienes 10000 artículos en la tabla, se tomará un buen rato para inicializarse y luego te resultará imposible buscar algo en ese Combo. Será un fastidio.
Debes ofrecer la posibilidad de buscar por nombre de producto ó por código con sendos textbox.
El que permita buscar por Id_producto, buscará por locate u otro método. La tabla debería tener un índice por Id_Producto y otro por Producto (descripción), lo cual te facilitará cualquier búsqueda cuando tenga cierto tamaño.
El textbox que permita la búsqueda por nombre de producto puede funcionar en estricto o en relativo. Acá utlizarás el SELECT SQL que normalmente se dirige a un formulario secundario en el que tienes un control grid donde muestras el resultado de la consulta y un CommandGroup con el clásico Aceptar / Cancelar. Para facilitar las cosas, el formulario secundario puede ser modal y devolver el Id_producto del producto seleccionado o cero. Este formulario secundario debe ser un genérico, de tal forma que te permita buscar y seleccionar tanto productos como clientes, proveedores, empleados, cuentas, etc. Te costará un rato desarrollarlo pero solo una vez.

Sigamos con el ComboBox.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
************************************************
* Con el tema de no mostrar productos repetidos
************************************************
local array gaprod(1)
local np
np=0
if !pemstatus(Thisform,"IdSel",5)
	Addproperty(thisform,"IDSEL","")
ENDIF
Select productos
WITH thisform.CmbProductos
 
	.Clear
	.BoundColumn=2
	.ControlSource='Thisform.IdSel'
	.AddListitem("(Seleccione Producto)",1,1)
	.addListitem("",1,2)
	Scan
		if ascan(gaprod,"<"+alltrim(producto)+">")=0
			*.AddItem(producto)
			np=np+1
			dimension gaprod(np)
			gaprod[np]="<"+alltrim(producto)+">"
			.AddListItem(producto,np+1,1)
			.AddListItem(TRansform(Id_Producto),np+1,2)
		endif
 
	endscan
	.requery
	.refresh
ENDWITH
 
*****************************************
* Sin el problema de productos repetidos
****************************************
local array gaprod(1)
local np
np=0
if !pemstatus(Thisform,"IdSel",5)
	Addproperty(thisform,"IDSEL","")
ENDIF
Select productos
WITH thisform.CmbProductos
	.Clear
	.BoundColumn=2
	.ControlSource='Thisform.IdSel'
	.AddListitem("(Seleccione Producto)",1,1)
	.addListitem("",1,2)
	Scan
		np=np+1
		.AddListItem(producto,np+1,1)
		.AddListItem(TRansform(Id_Producto),np+1,2)
	endscan
	.requery
	.refresh
ENDWITH
 
 
 
* Click del Combobox
nitem=This.ListitemId
if nitem#0
	lnIdSel=Val(this.Listitem(nitem,2))
       lcNombreSel=This.ListItem(nitem,1))
	if !empty(lnIdsel)
		Select (thisform.grid_inventario.recordSource)
		locate for id_producto=lnIsel
		with thisform.grid_inventario
			.refresh
			.SetFocus
		endwith
	endif
endif
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Luis (11 intervenciones) el 25/07/2013 19:49:58
Comprendo tu opinion Fidel, tienes razon, he decidido cambiar ese combobox por un textbox, para que sea mas facil identificar el codigo del producto a emilinar. Y he colocado como indice a los campos "id_producto" y "descripcion".
En cuanto al combo de los productos repetidos tranquilo que ya esta soucionado, gracias a los codigos que me has dado.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Luis (11 intervenciones) el 25/07/2013 21:42:29
Fidel, listo consegui solucionar ambos problemas analisando tu explicacion.
Y en el segundo formulario tengo otro combo que carga las descripciones. Con el siguiente codigo en el evento click;

1
2
3
4
5
6
7
8
9
10
11
12
13
thisform.cmbdescripcion.Clear
SELECT productos 				&&Selecciona la tabla a usar
GO TOP						&&ubica el indice en el inicio de la tabla
CantidadRegistros = RECCOUNT() 	&&guarda la cantidad de registros contenidos en la tabla
Policia = 0
FOR nCnt = 1  TO CantidadRegistros 		&& FOR que recorre todos los registros contenidos en la tabla
	IF 	thisform.cmbproductos.Text = productos.producto then
			thisform.cmbdescripcion.AddItem(productos.descripcion)
	ENDIF
	SKIP IN productos
ENDFOR
			thisform.cmbdescripcion.listindex = 1
			thisform.cmbdescripcion.Refresh

Pero tal como me dices, no es bueno utilizar el FOR. Porque me dice "que encontro el archivo final".
Quisiera que este combo me cargara las descripciones del producto seleccionado en el primer combo(cmbproductos).
Esto lo hago con la intencion de que el vendedor seleccione el tipo de producto, luego la descripcion del producto a vender y que en base a estos datos pueda proceder a vender.
Nota: en la tabla producto tengo el campo "descripcion" como indice, del tipo normal!
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Fidel (558 intervenciones) el 26/07/2013 02:01:18
Luis.
El problema para el tipo que intentas es CantidadRegistros = RECCOUNT()
REccount() es muy rápido porque lee la cabecera de la tabla y te dice cuántos registros tiene anotados. No sabes cuantos de esos se han marcado para borrar. Si trabajas con SET DELETED ON (que sería lo normal), y tienes registros marcados para borrar, estos registros son salteados por el SKIP, por lo que te puede dar el error de Eof() encountered. (se encontró fin de archivo). O sea, en este caso Reccount() devuelve más registros de los que encontrarás con SKIP.

Puedes hacer eso mismo, pero con
COUNT TO CantidadRegistros.
Este comando, si SET DELETED ON, de dice cuántos registros no están marcados para borrar.
De todos modos, el procedimiento que intentas es muy lento. Funciona con algunos pocos items, pero no hay que tomarse la costumbre.

Ahora, veamos un poco de estructura de datos.
Normalmente vas a clasificar los productos por tipo o rubro. Generalmente, tendrías que tener una tabla de Rubros, que a su vez, tendrá en algún futuro algunas asociaciones contables.
Pero para hacerlo fácil ahora, la tabla tendría que tener;
Id_rubro - Describe (Descripción de Rubro.)
En la tabla de Productos, no va la descripción de rubro, sino el Id_rubro. Esto te permite varias cosas:
1) Sistematizar correctamente pensando en el futuro
2) Si necesitas cambiar el nombre a un rubro, en lugar de volverte loco, simplemente cambias el campo Descripción en la tabla de rubros y todo quedará bien.
3) Si necesitas informes por rubro de produtos, te ahorras todo el problema de la comparación de caracteres porque tienes un identificador absoluto.
4) En la tabla de detalle de ventas, conviene tener un campo con el Id_rubro, además del código de producto y los demás datos de la venta. Con esto podrás conseguir totales por rubro sin necesidad de hacer un intermediario para determinar a qué rubro pertenece tal producto.
5) Si tienes una tabla de rubros, actualizas tu combo de rubros muy fácilmente sin necesidad de preguntar si ya tomó ese valor (que es mala práctica por lenta).
6) Acá se te agrega la posibilidad de asociar la tabla directamente al combo, de crear un cursor por SQL y crear esa asociación, o seguir trabajando con Rowsourcetype=0, pero ojo, no utilices AddItem, salvo cuando tengas una sola columna. Para el ejemplo, debes utilizar AddListItem donde puedes meter en el combo tantas columnas como necesites. En este caso te quedará en la primera la descripción del rubro y en la segunda el Id_Rubro. Si trabajas correctamente el combo (ya te pasé un ejemplo que puedes adaptar), te resultará muy fácil hacer una selección de productos en base al rubro.

Este mismo Combo, tiene que estar en el formulario de Altas de productos, para indicar a qué rubro pertenece el producto que se ingresa.

La tabla de Rubros tiene que tener un índice sobre el Id_Rubro, pero no necesita un índice sobre la descripción. En el combo donde muestres los rubros, simplemente le indicas SORTED=.T. y ya.

Para mostrar la descripción del rubro en el grid, puedes crearte una función de usuario;

PROCEDURE SEEKRUBRO(xValor)
* ------------------------------------------------
lcRetorna=""
if Indexseek(xValor,.f.,"RUBROS","ETIQUETA")
INDEXSEEK(xValor,.T.,"RUBROS","ETIQUETA")
lcRetorna=Rubros.Describe
endif
return LcRetorna
ENDPROC

luego, en el controlsource de la columna que corresponda, por ejemplo la Col 5:
Thisform.Grid1.Columns[5].ControlSource="SeekRubro(Id_rubro)"

La única precaución es que la tabla de rubros debe estar abierta. Pero funciona muy bien.
Estas funciones de búsqueda te evitan el uso de SET RELATION, algo totalmente no recomendable. SET RELATION es un comando cómodo, pero necesita que el archivo base esté en el master correcto, de lo contrario se pierden las relaciones. La función como la descripta no necesita nada.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Luis (11 intervenciones) el 26/07/2013 04:02:35
Fidel, utilize el "COUNT TO CantidadRegistros" para cargar las descripciones de los productos con igual nombre, y me sigue generando el problema; se ha encontrado el archivo final. He usado este codigo:
1
2
3
4
5
6
7
8
9
10
11
cmb_des=thisform.cmbdescripcion.value
SELECT productos 				&&Selecciona la tabla a usar
GO TOP						&&ubica el indice en el inicio de la tabla
COUNT TO CantidadRegistros	&&guarda la cantidad de registros contenidos en la tabla
FOR nCnt = 1  TO CantidadRegistros 		&& FOR que recorre todos los registros contenidos en la tabla
		IF 	cmb_des = productos.descripcion then
			thisform.txtpre.Value=(productos.precio_venta_unitario)
			thisform.txtexi.Value=(productos.cantidad)
			thisform.txtcodigo.Value=(productos.id_producto)
		ENDIF
		SKIP IN productos

Amigo en cuanto a tu estructura de datos, esta perfecta. Pero no quisiera complicarme mucho, ya que el sistema que intento realizar es para mi pequeño negocio, donde no manejo muchos productos, pero si tengo varios productos con diferentes descripciones. Por ejemplo: Producto: refresco, Descripcion: Pepsi 2lt.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Boton eliminar

Publicado por Fidel (558 intervenciones) el 26/07/2013 17:07:38
Bien Luis. Tu decides. Para mí, agregar una tabla de rubros te simplifica el desarrollo.
En cuanto al tema de recorrer todos los registros de la tabla, libérate de del For NEXT que no es para eso. En su lugar, utiliza SCAN ENDSCAN, que es justamente para eso y no tiene problemas de EOF() porque lo testea automáticamente. Incluso es preferible Do While !eof() Enddo antes que el bucle for next que intentas.

Igualmente te comento, simpre que utilices Skip, debes tener un test de fin de archivo.
Skip
if eof()
exit
endif
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar