Está en la página 1de 15

Antecedentes

En los primeros tiempos de mi experiencia como desarrollador, en la era del Fox Dos, me vi en la necesidad de
desarrollar una forma mas eficiente de hacer facturas, que la que venia utilizando hasta entonces. Lo que yo
hacia era utilizar el SCROLL para hacer que la pantalla se mueva hacia arriba cada vez que se ingresaba un
nuevo articulo.
Cada vez que el usuario quera revisar la factura, especialmente si contena un gran nmero de lneas que
desaparecan de la pequea superficie de dibujo de la pantalla, yo tenia que crear una matriz en forma dinmica,
a partir de los registros guardados en una tabla local, y mostrarlos con un POPUP. Cuando era necesario hacer
cambios (borrar una lnea, cambiar un cdigo, una cantidad o un precio unitario), yo elega un tem del popup,
llamaba a una rutina especial, hacia los cambios necesarios, redibujaba la pantalla. Cunto trabajo..!
Si mi cliente deseaba hacer algn cambio minsculo, me llevaba horas, debido a lo complicado de todos los
procedimientos. Por eso, buscando una manera mejor de hacer las cosas, comenc a usar el BROWSE. Y con
eso lo logre. El browse permita tanta facilidad de manipulacin, era tan flexible, que descarte todo el cdigo
anterior y cambie todas las pantallas usando Browse.
Ahora, en la era de la POO, todo eso paso a la historia. El browse puede ser reemplazado con mucha facilidad
por una cuadricula, si se tiene cuidado de considerar todos los aspectos de uso que ella requiere.
Qu tenemos que considerar al hacer facturas
Puedo mencionar los siguientes puntos a considerar cuando se hacen facturas, especialmente si la aplicacin,
como la mayora en estos das, va a funcionar en un entorno de red:

Use un cursor local para ingresar y modificar datos


Despus que la factura se acepto y esta lista para imprimir, piense en usar transacciones para guardar los
datos en las distintas tablas
Use una tabla compartida para generar nmeros de facturas

Por qu aconsejo el uso de un cursor local para el ingreso de datos? Si se usa un cursor local, se puede agregar,
modificar o borrar registros segn se necesite, sin comprometer los datos del servidor de ninguna manera.
Adems, no utilizamos recursos de la red innecesariamente, ni tenemos que lidiar con el sndrome de "me fui a
almorzar", que termina lockeando indefinidamente los registros.
Por qu usar transacciones? Eso depende. Si el sistema se va a usar en una maquina sola, quizs el uso de
transacciones no sea necesario. Sin embargo, un posible corte de luz o algn programa que cuelgue la maquina,
puede hacer aconsejable su uso. En un sistema en red, por el contrario, pienso que las transacciones son lo ms
aconsejable.
Cuando se emite una factura, intervienen muchas tablas en el proceso: tablas de consulta, tales como las de
clientes, artculos y stock, para obtener datos de los clientes, precios y descripciones de los artculos y
cantidades en existencia; tablas de servicio, como por ejemplo la tabla de numeracin de las facturas o, en
algunos pases, las tablas de tasas impositivas, y tambin tablas de grabacin, como las de stock, totales de la
factura, detalles de las facturas, cuentas a cobrar, valores recibidos y otras mas, segn sea el caso.
Dado que intervienen varias tablas, especialmente en el proceso de grabacin, y debido a que el mismo se
efecta secuencialmente, tabla por tabla, lnea por lnea, en algn momento algo podra fallar (La ley de
Murphy operando a pleno): un corte de luz, una tarjeta de red defectuosa, un ndice corrupto en una de las

tablas o un encabezado de tabla desmadrado. En tales casos, algunas de las tablas se graban y otras no. El
resultado es una pesadilla si tratamos de arreglarlo. Por eso recomiendo el uso de transacciones, advirtindole al
programador que lo haga de la manera lo ms eficiente posible, para evitar el locking de las tablas y registros
durante mucho tiempo.
Y tambin, use una tabla compartida para generar nmeros de facturas. Esto es necesario porque, cuando dos o
ms usuarios hacen facturas, nunca se sabe quien va a pulsar el botn Aceptar primero. Usando una tabla
compartida que controle el numero secuencial de las facturas, se pueden emitir eficientemente centenares de
facturas sin mezclar los datos grabados.
Hay otras cosas a considerar, tales como formularios de facturas preimpresos atascados en algunas impresoras,
diferentes mtodos de pago en la misma factura (cupones, tarjetas de crdito, efectivo, cheques, etc.),
descuentos globales o individuales, diferentes tasas impositivas para diferentes tipos de clientes, como puede
ser el caso en algunos pases, etc.
En este articulo, sin embargo, solamente voy a enfocar el tema especifico de la confeccin de facturas con
cuadriculas. Y tratare de hacerlo de manera simple, sin complicar las cosas con descuentos en cada lnea, ni
controles agregados en las columnas de la cuadricula. Voy a presentar un ejemplo simple de cuadricula, como
"viene de fabrica".
El formulario
Existen varias opciones para el tipo de formulario a utilizar. En algunos casos se podra usar un formulario para
definir los datos del cliente (nombre, direccin, categora de impuestos en algunos pases, si la venta es a
crdito o en efectivo, etc.)
Se podran ingresar todos los datos necesarios en dicho formulario y luego llamar a otro formulario con una
cuadricula para hacer la factura.
O se podra usar un nico formulario con un page frame, en una de cuyas paginas se obtendran los datos
necesarios del cliente y en la otra se podra poner la cuadricula.
O se podra utilizar un nico formulario con el espacio suficiente como para poner los datos del cliente y de la
factura. Cualquiera sea el caso, la eleccin es suya.
Adems de la cuadricula, se necesitan algunas etiquetas para mostrar los totales de la factura, que se Irn
actualizando y refrescando a medida que se hace un cambio en la cuadricula, como por ejemplo: agregar un
nuevo registro, modificar un precio o una cantidad, o simplemente borrar una lnea no deseada.
Si se ingresa un descuento global, como por ejemplo el 10% sobre el monto total, se necesitara una textbox
para ingresar el porcentaje de descuento y otra etiqueta para mostrar el monto del descuento.
De cualquier forma, cuando se hace cualquiera de los cambios indicados, se hace una llamada al mtodo
INV_TOTALS, que ara todos los clculos, como se mostrara mas adelante.
El cursor y su estructura
Como se indico, se usa un cursor local para el ingreso de datos. Por supuesto, un cursor local, es de
lectura/escritura. La estructura del cursor es la siguiente:

Campo Tipo
Observaciones
ItemCode C(10) o la longitud que tenga el cdigo del articulo
Detail
C (50) o mayor, dependiendo de la descripcin del articulo
Quantity N (5)
UnitPrice N (12,2)
LineTotal N(12,2)
Se puede generar este cursor local en el evento LOAD del formulario a utilizar, con el siguiente comando:
Create cursor INVOICE( ;
ItemCode C(10), Detail C(50), Quantity N(5,0),;
UnitPrice N(12,2),LineTotal N(12,2) )
Append blank

(hace falta al menos un registro para poder ingresar datos en el cursor)


Las tablas
Se puede usar el DATAENVIRONMENT para cargar todas las tablas necesarias. Solo mencionare aqu las
tablas que necesita mi ejemplo. Su estructura, ndices y relaciones no vienen al caso aqu.
Dado que este es un ejemplo de venta en mostrador, no necesitamos identificar al cliente, por lo que solamente
voy a necesitar las siguientes tablas:

ITEMS.DBF
contiene los datos de los artculos (cdigo, descripcin, precio)
STOCK.DBF
para actualizar las existencias
INVTOTAL.DBF
guarda un registro por cada factura con la fecha de la factura, el numero de factura, los impuestos
aplicables, el numero del vendedor, el descuento global y el total de la factura
DETAILS.DBF
guarda las cantidades, precios unitarios y cdigo del articulo por cada lnea de la factura
CASH.DBF
para el numero de la factura, su fecha, total parcial o general y tipo de pago (efectivo, cheque, cupn,
tarjeta de crdito)
CREDCARD.DBF
si se aceptan tarjetas de crdito para el pago, necesitamos una tabla de consulta con todas las tarjetas
posibles.

Segn sea cada situacin en particular, se podran necesitar otras tablas, pero como mnimo, creo que la lista
anterior es suficiente.
La cuadrcula
Si seguimos la estructura del cursor INVOICE, vemos que hacen falta cinco columnas en nuestra cuadricula.
Para instanciar el formulario sin problemas, solamente mostramos la cuadricula, pero sin fijarle un recordsource
o ninguna de sus propiedades, excepto quizs el ancho, numero de columnas, encabezados y ancho de las

mismas. Esto es para que, al abrir el formulario para modificarlo, tengamos una idea de cmo se vera la
cuadricula en tiempo de ejecucin.
Pero podramos simplemente poner la cuadricula encima del formulario y configurar sus propiedades con una
llamada al mtodo SET_GRID desde el mtodo INIT del formulario, como se muestra a continuacin:
Cdigo del mtodo Init
(aqu deberamos inicializar propiedades del formulario segn se haga necesario, antes de inicializar la
cuadricula).
this.nPrice = 0.00
this.cDescrip = ""
this.nMaxLinesAllowed = 30

&& numero mximo de lneas que caben en el


&& formulario preimpreso de la factura

this.cDocType = "FC"
this.nFedTax = 7.00
this.nProvTax = 8.00
this.nPercent = 0.00

&& estos valores se pueden obtener


&& de una tabla de impuestos

this.lblFTpctg.caption = "Fed tax "+ alltrim(transform( this.nFedTax ))+" %"


this.lblPTpctg.caption = "Prov tax "+ alltrim(transform( this.nProvTax ))+" %"
this.set_grid()

El mtodo Set_grid
Como mi intencin es mostrar un ejemplo simple, voy a usar un nombre por defecto para la cuadricula:
With thisform.grid1
.fontbold = .t.
.readonly = .f.
.columncount = 5
.recordsource = "invoice"
.allowaddnew = .f. && esto se explica mas adelante
.columncount = 5
.deletemark = .f.
.scrollbars = 2
.width = 642
with .column1
.controlsource="invoice.quantity"
.width = 70
with .header1
.caption = "Quantity"
.alignment=2
&& centered
endwith
endwith
with .column2
.width = 96
.controlsource="invoice.itemcode"
with .header1
.caption = "Code"
.alignment=2
endwith
endwith

with .column3
.controlsource="invoice.detail"
.width = 275
with .header1
.caption = "Description"
.alignment=2
&& centered
endwith
endwith
with .column4
.controlsource="invoice.unitprice"
.width = 70
with .header1
.caption = "Unit price"
.alignment=2
&& centered
endwith
endwith
with .column5
.controlsource="invoice.linetotal"
.width = 96
with .header1
.caption = "Totals"
.alignment=2
&& centered
endwith
endwith
Endwith

Consideraciones adicionales

Si consideramos la lgica natural de la confeccin de una factura, vemos que el diseo de la cuadricula de la
manera indicada se debe a que simplemente contestamos a la pregunta: cmo se hace una factura?
La respuesta es muy simple: primero ingresamos las cantidades, luego ingresamos el cdigo del articulo, en
cuyo momento el sistema consulta la tabla de artculos, trae la descripcin y su precio, pone dicha informacin
en la lnea que estamos modificando, luego se efecta la multiplicacin del precio por la cantidad y, finalmente,
se calcula el total de la lnea. El proceso termina cuando se agrega una nueva lnea vaca en la cuadricula y el
cursor se enfoca en la primera columna, para recibir un nuevo articulo.
Cmo hacemos que todo funcione?
Dmosle una mirada a la columna de cantidades. Nos gustara que el usuario ingresara cantidades mayores que
cero. En este ejemplo, solamente queremos valores enteros. As que tendramos que validar el input, no
permitiendo cantidades con decimales o ceros. Sin embargo, deberamos permitir el ingreso de cantidades
negativas, para el caso de que el formulario se use para confeccionar notas de crdito. O hacer el formulario
"inteligente", convirtiendo todas las cantidades en negativas en el caso de que estemos confeccionando una nota
de crdito.
Por lo tanto, pondremos el siguiente cdigo en el mtodo INIT del textbox en la columna 1:
This.value
This.InputMask
This.MaxLength
This.Format

=
=
=
=

0
"99999"
5
"Z"

Con este cdigo nos aseguramos que solamente se ingresen nmeros, de que el valor inicial de la textbox sea
cero, de que no se muestre nada si no se ingreso nada y de que la cantidad no supere 99999.
Pero aun as, el usuario podra ingresar un cero. As que tenemos que proporcionar el medio de, o bien avisarle
al usuario que no se acepta un valor cero, o transformar el cero en un uno.
En mi experiencia, es mas rpido dejar que el usuario simplemente apriete la tecla enter, cuando esta
posicionado en esta columna, lo cual ara que se ingrese un cero automticamente.
Pero el siguiente cdigo en el mtodo VALID de la textbox, har que el cero se transforme en un uno, saltando
desde la columna de las cantidades a la siguiente columna.
this.value = iif(this.value = 0,1,this.value)
this.refresh

Suponiendo que tenemos una propiedad del formulario llamada cDocType, podramos usar el mtodo VALID
para hacer dicha cantidad negativa, dejando de esa manera que el usuario ingrese cualquier numero, sin tener
que preocuparse por hacerlo negativo.
this.value = iif(this.value = 0,1,this.value)
if thisform.cDocType = 'CN'
if abs(this.value) > 0
this.value = -this.value

&& CN significa nota de crdito

endif
endif
thisform.inv_totals()

&& este mtodo se explica mas adelante

(el valor de la propiedad del formulario cDocType lo determina el usuario, antes de empezar a ingresar datos en
este formulario. Pero queda fuera del alcance de este articulo el mostrar como se logra esto)
Tenemos que asegurar una forma de salir de la grid para poder, ya sea descartar la misma o grabarla e
imprimirla. As que programamos el mtodo KEYPRESS con lo siguiente:
Cdigo del mtodo Keypress
LPARAMETERS nKeyCode, nShiftAltCtrl
If nKeyCode = 27

&& apret la tecla escape

Thisform.WhatNow()
Endif

Se podra poner cdigo en el mtodo WhatNow preguntndole al usuario si desea descartar la factura o grabarla
e imprimirla.
El cdigo anterior nos llevara a esa etapa.

La columna del cdigo de artculo


El cursor ya esta en la columna del cdigo de articulo.
segn el tipo de cdigo del articulo, se utilizara una diferente rutina de validacin.
Por ejemplo, podramos tener un cdigo alfanumrico como: DURACELL AA, PKJ/90-85, BAYER
ASPIRINE o cualquier otro. Tambin podramos tener cdigos numricos, una combinacin de nmeros,
guiones y barras, etc.
La lista es enorme y no puedo mostrar aqu todas las posibilidades.
Pero dije antes que quiero mantener las cosas simples, porque mi objetivo es mostrar como hacer facturas con
cuadriculas, y no como se valida el input.
Por lo tanto, voy a usar un simple cdigo numrico, con una longitud de no mas de 6 dgitos, almacenado en un
campo de tipo carcter.
En el mtodo INIT del textbox de esta columna ponemos el cdigo siguiente:
This.value
= ""
This.InputMask = "999999"
This.MaxLength = 6

Este cdigo asegura que solamente se ingresan nmeros de no mas de 6 dgitos.


En el mtodo VALID, el cdigo seria:
Local cCode
cCode = str(val(this.value),6)
select Items
if seek( cCode , "Items", "artnum")
this.value = cCode
with thisform
.nPrice
= items.pesos
.cDescrip = items.descrip
endwith
select invoice
Replace invoice.detail with thisform.cDescrip ,;
invoice.unitprice with thisform.nPrice
,;
invoice.linetotal with (invoice.quantity * invoice.unitprice)
thisform.inv_totals()
return 1
else
this.value = ""
messagebox("CODIGO INVALIDO",48,"ATENCION")
return 0
endif

Muy bien, pero por qu tenemos que reemplazar los campos del cursor subyacente con los valores obtenidos?
Se podra argumentar que, dado que los controlsources de los textboxes estn ligados (bound) a los campos del
cursor, esto no es necesario. Es verdad, pero a veces un poco de redundancia ayuda. En este caso, tengo la
absoluta certeza de que, a pesar de la ley de Murphy, los datos recin hallados en la tabla ITEMS, que fueron
asignados en las respectivas propiedades del formulario por el cdigo del mtodo valid, realmente se graben en
el registro.
Sin embargo, no hice lo mismo con las cantidades ingresadas. Por qu? Porque yo estaba interactuando
directamente sobre el cursor cuando ingresaba un valor en el campo de cantidad, mientras que en el caso de los
valores buscados en la tabla ITEMS, estaba manejando otra tabla, en otra arrea. Yo soy un tipo practico. Esto
funciona as, por lo que un poco de redundancia no hace dao.
Ya estamos listos para ingresar datos en la columna de detalle, pero, un momento, la descripcin del articulo la
obtuvimos de la tabla ITEMS, su valor fue grabado en el cursor INVOICE y, como este campo esta ligado a la
cuadricula, la descripcin del articulo ya esta en la cuadricula. Por lo tanto, no queremos que el usuario pueda
entrar en esta columna y cambiar nada. Cmo podemos hacer esto?
Es muy fcil: ponemos este cdigo en el mtodo WHEN de la textbox de la columna detalle:
Return .f.

As de simple! El mtodo when se dispara antes que el mtodo valid, aun antes de que se pueda entrar en la
columna. Al devolver un falso, simplemente se pasa por alto esta columna y se posiciona en la prxima, la
columna de los precios unitarios.
An cuando el precio lo sacamos de la tabla ITEMS, es posible que la lista de precios no haya sido actualizada
antes de hacer la factura, y que tengamos que cambiar el precio all mismo.
As que permitimos el ingreso en dicha columna para que el usuario pueda cambiar el precio.
Pero aqu podemos tener un problema potencial. Dado que una de las principales razones para el uso de una
cuadricula era el poder navegar de arriba abajo y de izquierda a derecha entre las distintas lneas y columnas,
pudiendo cambiar los valores, si modificamos el precio unitario, ser aceptado y, como mostramos mas
adelante, se harn los clculos de multiplicacin del precio por la cantidad. Sin embargo, si el usuario aprieta la
tecla enter en la columna del artculo, se dispara el evento valid del textbox, se busca nuevamente el precio en
la tabla de artculos, y se reemplaza el valor que el usuario hubiera ingresado manualmente en dicha columna.
Este comportamiento es muy molesto y hara completamente intil nuestra rutina de hacer facturas. Entonces,
cmo lo resolvemos?
Medite sobre esto durante muchas horas, o en realidad, muchos das. Recurdese que cuando se programa, hay
un 10% de tiempo de inspiracin y un 90% de tiempo de transpiracin. Por lo menos, a mi me pasa as.
As que, en algn momento de mi 10% de inspiracin, tuve una idea: usar una propiedad del textbox para
habilitar o deshabilitar un cambio de precios cuando el usuario aprieta la tecla enter en la columna del cdigo
del articulo, o cuando, al usar las teclas de flecha, navega hacia ella.
Por ello, hice los siguientes cambios en la columna de artculos:
Evento Init

This.value
= ""
This.InputMask = "999999"
This.MaxLength = 6
This.addproperty("cOldCode","")

Evento When
this.cOldCode = str(val(invoice.itemcode),6)
return .t.

Antes de entrar en la columna de artculos, leemos el valor almacenado en la tabla (cursor)


Evento Valid
Cambie el cdigo as:
Local cCode
cCode = str(val(this.value),6)
If cCode = this.cOldCode
Return 1
Else

&& ya pasamos por aqu

&& no hacemos nada, sino que salimos

select tems
if seek( cCode , "tems", "artnum")
this.value = cCode
with thisform
.nPrice
= items.pesos
.cDescrip = items.descrip
endwith
select invoice
Replace invoice.detail with thisform.cDescrip ,;
invoice.unitprice with thisform.nPrice
,;
invoice.linetotal with (invoice.quantity * invoice.unitprice)
thisform.inv_totals()
return 1
else
this.value = ""
messagebox("CODIGO INVALIDO",48,"ATENCION")
return 0
endif
endif

(La llamada al mtodo inv_totals se hace para asegurar que se recalculan los totales de la factura cada vez que
cambiamos un articulo que se haba ingresado anteriormente. Ver ms adelante una explicacin de este mtodo)
La columna de precios unitarios
Si bien el precio unitario lo tenemos en la tabla ITEMS, es posible que la misma no este actualizada, o que se
quiera poner un precio distinto al de la tabla para una factura determinada.
Por esa razn permitimos el ingreso de otro precio en esta columna, cuyo cdigo INIT es el siguiente:

this.addproperty("nLastPrice",0.00)
this.value = 0.00

Por que inicializamos aqu la propiedad nLastPrice ? Pues por la misma razn que al navegar hacia cualquiera
de las cuatro direcciones, al dar enter sobre la columna de artculos, el evento VALID se disparaba y nos volva
a traer el precio de la tabla, en lugar de respetar el precio recin ingresado en la columna de precios unitarios,
tenemos que asegurarnos que esto ocurra efectivamente.
Por eso, antes de entrar en esta columna leemos el valor que tenia, mediante el cdigo del evento WHEN:
this.nLastPrice = this.value
return .t.

y validamos el input en el evento VALID con:


if not inlist( lastkey() , 19 , 4 , 5 , 24 )

&& izq, der, arr, abajo

if this.value <> this.nLastPrice


Replace invoice.unitprice with this.value
,;
invoice.linetotal with (invoice.quantity * invoice.unitprice)
endif
endif
thisform.inv_totals()

Lo que estamos haciendo aqu es una ultra redundancia: primero nos aseguramos de que, si estamos pulsando
cualquiera de las 4 flechas de direccin, solamente se recalculen los totales al pie de la factura. Pero adems, si
ninguna de esas flechas se pulso, sino que se apret la tecla enter, solamente se cambia el precio en el cursor
subyacente y se recalcula el total de ese registro, siempre y cuando el valor que tenemos en la columna es
distinto al que haba antes de ingresar en ella.
De esta manera, se evita el problema indicado de que se cambie el precio si nosotros no lo queremos as.
La ltima columna a la derecha
El textbox de la columna de totales debe ser de solo lectura, porque simplemente va a mostrar el valor calculado
por el mtodo inv_totals.
Sin embargo, hay que permitir el ingreso en esta columna, para que el usuario pueda apretar la tecla enter. Al
hacer esto, se lograra que se agregue un nuevo registro al cursor INVOICE, y el foco quedara en la primera
columna, a la espera del ingreso de mas datos.
Cmo se hace? Cuando establecimos las propiedades de la cuadricula, pusimos la propiedad AllowAddNew
en falso. Estuve jugando con esta propiedad durante algn tiempo, pero pronto la deje de lado porque, en mi
opinin, es intil para hacer facturas.
Esta propiedad, cuando la ponemos en verdadero, hace que se agregue un nuevo registro, cuando el usuario
aprieta la tecla flecha abajo. Como nosotros navegamos por la cuadricula en las cuatro direcciones, estaramos
agregando registros que no queremos cada vez que vamos hacia abajo. Por lo tanto, para nuestros fines, esta
propiedad la dejamos en falso.

Usamos el mtodo KEYPRESS otra vez para agregar un nuevo registro y establecemos el foco en la primera
columna de la lnea siguiente, con este cdigo:
LPARAMETERS nKeyCode, nShiftAltCtrl
local N
if nKeyCode = 13

&& se apret la tecla enter

replace invoice.linetotal with (invoice.quantity * invoice.unitprice)


go top in invoice
count to N for not deleted()
if N < thisform.nMaxLinesAllowed
go bottom in invoice
if not empty( invoice.itemcode )
append blank
go bottom in invoice
keyboard '{dnarrow}'
endif
else
delete next 1
messagebox('LA CANTIDAD MAXIMA DE LINEAS PERMITIDA'+CHR(13)+'PARA UNA FACTURA
ES:';
+ str(thisform.nMaxLinesAllowed),16,'ERROR')
endif
endif
This.Parent.Parent.refresh

&& refrescamos la cuadricula

Analicemos este cdigo en detalle:


Cuando el usuario aprieta la tecla enter, esta rutina cuenta el numero de registros no borrados en el cursor y lo
compare con la cantidad mxima de lneas que puede tener una factura. Esto es importante si utilizamos
formularios preimpresos de facturas, cuyo espacio de impresin de lneas de detalle es limitado. Si se excede el
mximo permitido, un mensaje de error le avisa al usuario y se borra el registro recientemente aadido.
Si el limite anterior no se alcanzo, se agrega un nuevo registro al cursor INVOICE. Forzamos el foco en la
ultima lnea de la cuadricula con la combinacin de los comandos GO BOTTOM y KEYBOARD
'{DNARROW}'
Tome nota de la prueba adicional: si no hay ningn cdigo de articulo en la primera columna, entonces se
agrega un registro nuevo, de lo contrario, no pasa nada. Esto evita que se agreguen lneas intiles en la
cuadricula.
El mtodo Inv_totals
Cada vez que agregamos una nueva lnea, borramos una existente, cambiamos un precio, reemplazamos un
articulo por otro o cambiamos las cantidades, las cifras al pie de la factura deben cambiar.

Esta ultima lnea, o conjunto de lneas, segn sea el caso, esta formada por el subtotal de la factura (la suma
total que se obtiene de multiplicar las cantidades por los precios unitarios en todas las lneas), y todos los
impuestos, descuentos globales o tems adicionales que correspondan (fletes, intereses o lo que fuera). La
ultima lnea debe ser el total que el cliente debe pagar.
Ahora, dependiendo de cuanto espacio haya en la pantalla, todos estos valores se pueden colocar en una sola
lnea, abajo de todo, o en forma encolumnada, una encima de otra. En realidad, esto es una eleccin del
programador y no va a afectar los clculos de ningn modo.
Los resultados de los clculos se mostraran en etiquetas o en textboxes, a eleccin del programador.
En mi caso, yo prefiero usar etiquetas. Por ejemplo, si el subtotal de la factura fuera de 2,000.00 dlares y la
etiqueta para mostrar esta cifra tuviera el muy imaginativo nombre de lblSubTotal, utilizaramos la siguiente
lnea para mostrarlo:
Thisform.lblSubTotal.caption = transform(thisform.nSubTotal,"9,999.99")

Para que las cifras que pongamos en estas etiquetas se alineen a la derecha, se debe configurar su propiedad
alignment a 1. Habiendo dicho esto, veamos ahora como funciona el mtodo INV_TOTALS
With thisform
Store 0.00 to .nSubTotal,.nTotal,.nTax1,.nTax2,.nST2 , .nDiscount
Select invoice
Go top
sum (invoice.quantity * invoice.unitprice) to .nSubTotal for not deleted()
.nDiscount = .nSubTotal * -.nPercent / 100
.nST2
= .nSubTotal + .nDiscount
.nTax1 = .nST2 * .nFedTax / 100
.nTax2 = .nST2 * .nProvTax / 100
.nTotal = .nST2 + .nTax1 + .nTax2
.show_labels()
.refresh
endwith

Suponiendo que tenemos una propiedad para el descuento global llamada nDiscount y otra propiedad para el
porcentaje, con el nombre de nPercent, y adems, que el usuario ingreso un 5% de descuento global,
calcularamos el monto del descuento as:
Thisform.nDiscount = thisform.nSubTotal* -thisform.nPercent / 100

(La propiedad nPercent es el controlsource de un textbox usado para ingresar el descuento de 5% deseado)
Lo anterior nos da un valor de descuento negativo. Luego recalculamos el subtotal de la factura, para mostrar el
valor neto, as:
Thisform.nST2 = thisform.nSubTotal + thisform.nDiscount

(Nota: tenemos que restar el descuento global del subtotal, pero, como ya es negativo, lo sumamos. Guardamos
este nuevo subtotal, neto de descuento, en otra propiedad, nST2)

Y ahora estamos listos para calcular los impuestos que se apliquen en su pas. Supongamos que tenemos un
impuesto federal a las ventas del 7% y un impuesto provincial del 8% Entonces tendramos que aplicar estos
porcentajes al subtotal neto.
Thisform.nTax1 = Thisform.nST2 * thisform.nFedTax / 100
Thisform.nTax2 = thisform.nST2 * thisform.nProvTax / 100

Por ultimo, podemos mostrar el total de la factura, sumando el subtotal neto mas los dos impuestos:
Thisform.nTotal = thisform.nST2 + thisform.Tax1 + thisform.Tax2

Y completamos este mtodo con una llamada al mtodo que mostrara todos estos valores calculados en sus
respectivas etiquetas al pie de la factura.
Thisform.show_labels()

El mtodo Show_labels
Como se preanuncio en la seccin anterior, utilizamos una serie de funciones TRANSFORM para colocar los
valores calculados en sus respectivas etiquetas al pie de la factura.
local cPic
cPic = "99,999.99"
with thisform
.lblST1.caption
.lblDisc.caption
.lblST2.caption
.lblFT.caption
.lblPT.caption
.lblTotal.caption
endwith

=
=
=
=
=
=

transform(
transform(
transform(
transform(
transform(
transform(

.nSubTotal
.nDiscount
.nST2
.nTax1
.nTax2
.nTotal

,
,
,
,
,
,

cPic
cPic
cPic
cPic
cPic
cPic

)
)
)
)
)
)

El mtodo Grabar
No voy a mostrar aqu como grabar la factura usando transacciones, porque tomara bastante mas espacio del
que dispongo y, adems, excedera el alcance de este articulo. Pero puedo decir que la grabacin consiste en
usar un loop scan - endscan sobre el cursor INVOICE, grabando todas sus lneas en las tablas correspondientes.
Conclusin
He mostrado la forma de hacer facturas y otras pantallas complicadas de ingreso de datos, usando cuadriculas.
En mis aplicaciones reales agrego mucha mas funcionalidad que la simple metodologa indicada: llamo a una
tabla de consulta para la bsqueda de artculos, apretando una tecla de funcin cuando el foco esta en la
columna de artculos (esto se hace programando el evento keypress del textbox), o muestro la cuenta del
cliente, si la venta es a crdito, por medio de un right click en el formulario y haciendo una llamada al
formulario de cuentas corrientes, o ingreso un nuevo cliente mientras hago la factura, o consulto una tabla de
descuentos, o muestro una foto del tem que estoy facturando, etc., etc.
Las cuadriculas nos permiten mejorar muchsimo nuestras pantallas de ingreso de datos. Con un poco de trabajo
y mucha fe, podemos hacerlo. As que, buena suerte en su tarea!

También podría gustarte