Antecedentes. Qué tenemos que considerar al hacer facturas

Similar documents
High-Speed INTERNET Modem Installation Instructions

Dell Active Pen. User s Guide PN556W. Regulatory Model: PN556W

OCTOBEAM. LED Lighting Effect USER MANUAL / MANUAL DE USUARIO

X5 QUICK START. Back Panel Diagram DSL ETHERNET 1 ETHERNET 2 ETHERNET 3 ETHERNET 4 RESET POWER SWITCH

Quick Installation Guide TK-208K TK-408K

Quick Installation Guide TK-408K

Msystems Ltd.

b) Use one of your methods to calculate the area of figure c.

Task 5: Dilating Figures

SAP NETWEAVER AS ABAP SYSTEM ADMINISTRATION

Cómo usar dispositivos Bluetooth

Connecting your 7. IT Service

Default Route de la configuración en el EIGRP

Grabador cámaras en red (NVR) Manual usuario

GH A. Get started. All you need to know to get going. Todo lo que tienes que saber para empezar. Vamos.

Anexos. Diseño y construcción de un puente grúa automatizado de precisión

Overview: Multicore Architectures

Propedéutico de Programación

DMX/RDM - ArtNet Wireless node USER MANUAL / MANUAL DE USUARIO

Biocryptology Login. WordPress. Installation and Configuration Instalación y configuración

FAMILY INDEPENDENCE ADMINISTRATION Matthew Brune, Executive Deputy Commissioner

DM6. User Guide English ( 3 10 ) Guía del usuario Español ( ) Appendix English ( 13 ) DRUM MODULE

COMPUTER CLASSES July 2018

AUDACITY SOFTWARE GUIDE

COMPUTER CLASSES April 2019

OpenManage Management Pack for vrealize Operations Manager Version 1.0 Installation Guide

NAVIGATOR SMART RDM. DMX Controller USER MANUAL / MANUAL DE USUARIO

COMPUTER CLASSES May 2018

Identify Three-Dimensional Shapes from Different Views. This is how we will be identifying a three-dimensional shape using different views.

Biocryptology Login. Magento 1.x. Installation and Configuration Instalación y Configuración

4K 2-PORT KVM SWITCH, USB C, DISPLAYPORT

Manual Steps for SAP note

PSEUDOCODE. It is a conventional code to represent an algorithm

Statistics I Practice 2 Notes Probability and probabilistic models; Introduction of the statistical inference

13. Frame Relay. Contents

ENCENDIDO Y LAVADOS DEL CITÓMETRO

PARA FLASHER BIOS DE ASPIRE 5732Z PRODUCT CATALOG PDF

Wall. Publications in my wall. Portfolios

RHCE-PARTE-12 DHCPv6 SLAAC (StateLess Address AutoConfiguration) 1/5. Objetivo:

Documentation for Scanner Tool

FILE / MANUAL DE NOKIA E5 EBOOK

Documentación GT_Complemento_Exportaciones xsd Factura Electrónica en Línea

IvozProvider 2.9 Documentation

Important Notice on Instant Mode

Guidelines. Table of Contents. Welcome Letter

Revisar su balance e historial de transacción Inscribirse en mensajes de texto 1 Open a savings account 2

Creation of new TM segments: Fulfilling translators wishes

INTOSAI EXPERTS DATABASE

USER MANUAL MANUAL DE USUARIO

User Manual / Instrucciones de Usuario Rev

GUIDE to the CONICYT On- line Proposal Submission System for Chilean APEX Proposal

Identificación de prioridades regionales de RRD

GENERALIDADES INICIAR DE LA HOJA DE CALCULO TEMPORIZADOR. This guide is of virtual use, so it is not necessary to print

DISPLAYPORT KVM DESKTOP SWITCHES

Single user Installation. Revisión: 13/10/2014

Quick Start Guide. FreedomPop. Moto E.

NOTA INFORMATIVA 15 de Octubre de 2015 SISTEMA DE REGISTRO DE FABRICANTES EN CHINA

USB Director/USB RS-232 Hub

ServSwitch DT DVI Pro

USB TO RS-232 OR RS-422/485 ISOLATED CONVERTER

tick which says safely remove software. I've removed the drivers and created a archive from the in the event that someone doesn't want to extract it

The Ultimate Grid. Visual FoxPro 6 DORON FARBER. Structure of the Solution. Design Issues

USER MANUAL ME890A-R2 CATX DB9 LINE DRIVER 24/7 TECHNICAL SUPPORT AT OR VISIT BLACKBOX.COM RJ45 RJ45 5 VDC DB9-RS232

CERTIFICACION SAGE Enterprise Management (X3)

PHP PROGRAMMING PATTERN FOR THE PREFERENCES USABILITY MECHANISM

Queries with Multiple Criteria (OR)

FROM DORMITORIOS / BEDROOMS FROM JIMENEZ VISO. Every day is a new beginning

Manual Calculadora Hp 10s Scientific Calculator File Type

Model AC2000A DVI, Audio, RS-232 Extender with EDID Management

EDITRAN/G 5.2. Application Program Interfaces. Application Generic Interface. z/os CICS IMS

Smart Mic. User Guide Master

Class 1 - Foundational. Chromebook Basics

DOC // MANUAL CALCULADORA HP 17BII EBOOK

Copyright Black Box Corporation. All rights reserved.

From the artwork to the demo artwork. Case Study on the conservation and degradation of new media artworks

Operating Instructions

The Virtual EMS Browse Menu How-To Document

The colours used in architecture must be intense, logical and fertile. Los colores usados en arquitectura tiene que ser intensos, logicos y fértiles.

Sincerely, Your child s teacher

An Analysis of Recurrent Neural Networks for Botnet Detection Behavior

Manual Para Android Jelly Bean For Galaxy S I9000

Clip Art. Etapa preliminar. Table of Contents

reality apps. I hope I have helped someone develop their first app, and maybe start them down the path of AR development. What questions do you have?

Departamento de Economía Financiera y Contabilidad I

Mizrahi, Michel Jonathan

Copyright Black Box Corporation. All rights reserved.

Operating Instructions

Concert Attendance. Use the graph to identify between what times the concert attendance increased the most.

Relay Output Card PCI 32 Outputs 32 Outputs/Kit

EDITRAN/EA. User and installation manual. Statistics and monitorization. Windows/Unix INDRA 17/03/17

Personal Audio System

Concert Attendance. Use the graph to identify between what times the concert attendance increased the most.

Express Ethernet Switches

One Port Router. Installation Guide. It s about Quality of life

Manual De Instalar Do Joomla 2.5 Con Xampp

DISPLAYPORT KVM DESKTOP SWITCHES

Escuela Politécnica Superior de Linares

HDMI 2.0 SPLITTER 1X4

R&S FSH Handheld Spectrum Analyzer Quick Start

GUIDELINES FOR ONLINE VISA TO COLOMBIA (Courtesy translation)

Transcription:

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 quería revisar la factura, especialmente si contenía un gran número de líneas que desaparecían de la pequeña superficie de dibujo de la pantalla, yo tenia que crear una matriz en forma dinámica, a partir de los registros guardados en una tabla local, y mostrarlos con un POPUP. Cuando era necesario hacer cambios (borrar una línea, cambiar un código, una cantidad o un precio unitario), yo elegía un ítem del popup, llamaba a una rutina especial, hacia los cambios necesarios, redibujaba la pantalla. Cuánto trabajo..! Si mi cliente deseaba hacer algún cambio minúsculo, 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 permitía tanta facilidad de manipulación, era tan flexible, que descarte todo el código 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 aplicación, como la mayoría en estos días, va a funcionar en un entorno de red: Use un cursor local para ingresar y modificar datos Después 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 números 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 según se necesite, sin comprometer los datos del servidor de ninguna manera. Además, no utilizamos recursos de la red innecesariamente, ni tenemos que lidiar con el síndrome 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, quizás el uso de transacciones no sea necesario. Sin embargo, un posible corte de luz o algún programa que cuelgue la maquina, puede hacer aconsejable su uso. En un sistema en red, por el contrario, pienso que las transacciones son lo más aconsejable. Cuando se emite una factura, intervienen muchas tablas en el proceso: tablas de consulta, tales como las de clientes, artículos y stock, para obtener datos de los clientes, precios y descripciones de los artículos y cantidades en existencia; tablas de servicio, como por ejemplo la tabla de numeración de las facturas o, en algunos países, las tablas de tasas impositivas, y también tablas de grabación, como las de stock, totales de la factura, detalles de las facturas, cuentas a cobrar, valores recibidos y otras mas, según sea el caso. Dado que intervienen varias tablas, especialmente en el proceso de grabación, y debido a que el mismo se efectúa secuencialmente, tabla por tabla, línea por línea, en algún momento algo podría 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, advirtiéndole al programador que lo haga de la manera lo más eficiente posible, para evitar el locking de las tablas y registros durante mucho tiempo. Y también, use una tabla compartida para generar números de facturas. Esto es necesario porque, cuando dos o más usuarios hacen facturas, nunca se sabe quien va a pulsar el botón 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 métodos de pago en la misma factura (cupones, tarjetas de crédito, efectivo, cheques, etc.), descuentos globales o individuales, diferentes tasas impositivas para diferentes tipos de clientes, como puede ser el caso en algunos países, etc. En este articulo, sin embargo, solamente voy a enfocar el tema especifico de la confección de facturas con cuadriculas. Y tratare de hacerlo de manera simple, sin complicar las cosas con descuentos en cada línea, 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 podría usar un formulario para definir los datos del cliente (nombre, dirección, categoría de impuestos en algunos países, si la venta es a crédito o en efectivo, etc.) Se podrían ingresar todos los datos necesarios en dicho formulario y luego llamar a otro formulario con una cuadricula para hacer la factura. O se podría usar un único formulario con un page frame, en una de cuyas paginas se obtendrían los datos necesarios del cliente y en la otra se podría poner la cuadricula. O se podría utilizar un único formulario con el espacio suficiente como para poner los datos del cliente y de la factura. Cualquiera sea el caso, la elección es suya. Además de la cuadricula, se necesitan algunas etiquetas para mostrar los totales de la factura, que se Irán 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 línea no deseada. Si se ingresa un descuento global, como por ejemplo el 10% sobre el monto total, se necesitaría 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 método INV_TOTALS, que ara todos los cálculos, 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 código del articulo Detail C (50) o mayor, dependiendo de la descripción 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 artículos (código, descripción, 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 código del articulo por cada línea de la factura CASH.DBF para el numero de la factura, su fecha, total parcial o general y tipo de pago (efectivo, cheque, cupón, tarjeta de crédito) CREDCARD.DBF si se aceptan tarjetas de crédito para el pago, necesitamos una tabla de consulta con todas las tarjetas posibles. Según sea cada situación en particular, se podrían necesitar otras tablas, pero como mínimo, creo que la lista anterior es suficiente. La cuadrícula 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 quizás 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 cómo se vera la cuadricula en tiempo de ejecución. Pero podríamos simplemente poner la cuadricula encima del formulario y configurar sus propiedades con una llamada al método SET_GRID desde el método INIT del formulario, como se muestra a continuación: Código del método Init (aquí deberíamos inicializar propiedades del formulario según se haga necesario, antes de inicializar la cuadricula). this.nprice = 0.00 this.cdescrip = "" this.nmaxlinesallowed = 30 && numero máximo de líneas que caben en el && formulario preimpreso de la factura this.cdoctype = "FC" this.nfedtax = 7.00 && estos valores se pueden obtener this.nprovtax = 8.00 && de una tabla de impuestos this.npercent = 0.00 this.lblftpctg.caption = "Fed tax "+ alltrim(transform( this.nfedtax ))+" %" this.lblptpctg.caption = "Prov tax "+ alltrim(transform( this.nprovtax ))+" %" this.set_grid() El método Set_grid Como mi intención 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 with.column2.width = 96.controlsource="invoice.itemcode" with.header1.caption = "Code".alignment=2 && centered

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

Si consideramos la lógica natural de la confección de una factura, vemos que el diseño de la cuadricula de la manera indicada se debe a que simplemente contestamos a la pregunta: cómo se hace una factura? La respuesta es muy simple: primero ingresamos las cantidades, luego ingresamos el código del articulo, en cuyo momento el sistema consulta la tabla de artículos, trae la descripción y su precio, pone dicha información en la línea que estamos modificando, luego se efectúa la multiplicación del precio por la cantidad y, finalmente, se calcula el total de la línea. El proceso termina cuando se agrega una nueva línea vacía en la cuadricula y el cursor se enfoca en la primera columna, para recibir un nuevo articulo. Cómo hacemos que todo funcione? Démosle una mirada a la columna de cantidades. Nos gustaría que el usuario ingresara cantidades mayores que cero. En este ejemplo, solamente queremos valores enteros. Así que tendríamos que validar el input, no permitiendo cantidades con decimales o ceros. Sin embargo, deberíamos permitir el ingreso de cantidades negativas, para el caso de que el formulario se use para confeccionar notas de crédito. O hacer el formulario "inteligente", convirtiendo todas las cantidades en negativas en el caso de que estemos confeccionando una nota de crédito. Por lo tanto, pondremos el siguiente código en el método INIT del textbox en la columna 1: This.value = 0 This.InputMask = "99999" This.MaxLength = 5 This.Format = "Z" Con este código nos aseguramos que solamente se ingresen números, 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 podría 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 rápido dejar que el usuario simplemente apriete la tecla enter, cuando esta posicionado en esta columna, lo cual ara que se ingrese un cero automáticamente. Pero el siguiente código en el método 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, podríamos usar el método 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' && CN significa nota de crédito if abs(this.value) > 0 this.value = -this.value

thisform.inv_totals() && este método 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 método KEYPRESS con lo siguiente: Código del método Keypress LPARAMETERS nkeycode, nshiftaltctrl If nkeycode = 27 && apretó la tecla escape Endif Thisform.WhatNow() Se podría poner código en el método WhatNow preguntándole al usuario si desea descartar la factura o grabarla e imprimirla. El código anterior nos llevaría a esa etapa.

La columna del código de artículo El cursor ya esta en la columna del código de articulo. según el tipo de código del articulo, se utilizaría una diferente rutina de validación. Por ejemplo, podríamos tener un código alfanumérico como: DURACELL AA, PKJ/90-85, BAYER ASPIRINE o cualquier otro. También podríamos tener códigos numéricos, una combinación de números, 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 código numérico, con una longitud de no mas de 6 dígitos, almacenado en un campo de tipo carácter. En el método INIT del textbox de esta columna ponemos el código siguiente: This.value = "" This.InputMask = "999999" This.MaxLength = 6 Este código asegura que solamente se ingresan números de no mas de 6 dígitos. En el método VALID, el código 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 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

Muy bien, pero por qué tenemos que reemplazar los campos del cursor subyacente con los valores obtenidos? Se podría argumentar que, dado que los controlsources de los textboxes están 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 recién hallados en la tabla ITEMS, que fueron asignados en las respectivas propiedades del formulario por el código del método 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 daño. Ya estamos listos para ingresar datos en la columna de detalle, pero, un momento, la descripción 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 descripción del articulo ya esta en la cuadricula. Por lo tanto, no queremos que el usuario pueda entrar en esta columna y cambiar nada. Cómo podemos hacer esto? Es muy fácil: ponemos este código en el método WHEN de la textbox de la columna detalle: Return.f. Así de simple! El método when se dispara antes que el método 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 próxima, la columna de los precios unitarios. Aún 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 líneas y columnas, pudiendo cambiar los valores, si modificamos el precio unitario, será aceptado y, como mostramos mas adelante, se harán los cálculos de multiplicación del precio por la cantidad. Sin embargo, si el usuario aprieta la tecla enter en la columna del artículo, se dispara el evento valid del textbox, se busca nuevamente el precio en la tabla de artículos, y se reemplaza el valor que el usuario hubiera ingresado manualmente en dicha columna. Este comportamiento es muy molesto y haría completamente inútil nuestra rutina de hacer facturas. Entonces, cómo lo resolvemos? Medite sobre esto durante muchas horas, o en realidad, muchos días. Recuérdese que cuando se programa, hay un 10% de tiempo de inspiración y un 90% de tiempo de transpiración. Por lo menos, a mi me pasa así. Así que, en algún momento de mi 10% de inspiración, 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 código del articulo, o cuando, al usar las teclas de flecha, navega hacia ella. Por ello, hice los siguientes cambios en la columna de artículos: 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 artículos, leemos el valor almacenado en la tabla (cursor) Evento Valid Cambie el código así: Local ccode ccode = str(val(this.value),6) If ccode = this.coldcode && ya pasamos por aquí Return 1 Else && no hacemos nada, sino que salimos select ítems if seek( ccode, "ítems", "artnum") this.value = ccode with thisform.nprice = items.pesos.cdescrip = items.descrip 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 (La llamada al método inv_totals se hace para asegurar que se recalculan los totales de la factura cada vez que cambiamos un articulo que se había ingresado anteriormente. Ver más adelante una explicación de este método) 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 razón permitimos el ingreso de otro precio en esta columna, cuyo código INIT es el siguiente:

this.addproperty("nlastprice",0.00) this.value = 0.00 Por que inicializamos aquí la propiedad nlastprice? Pues por la misma razón que al navegar hacia cualquiera de las cuatro direcciones, al dar enter sobre la columna de artículos, el evento VALID se disparaba y nos volvía a traer el precio de la tabla, en lugar de respetar el precio recién 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 código 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) 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 dirección, solamente se recalculen los totales al pie de la factura. Pero además, 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 había 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 método 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. Cómo se hace? Cuando establecimos las propiedades de la cuadricula, pusimos la propiedad AllowAddNew en falso. Estuve jugando con esta propiedad durante algún tiempo, pero pronto la deje de lado porque, en mi opinión, es inútil 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, estaríamos 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 método KEYPRESS otra vez para agregar un nuevo registro y establecemos el foco en la primera columna de la línea siguiente, con este código: 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 else go bottom in invoice if not empty( invoice.itemcode ) append blank go bottom in invoice keyboard '{dnarrow}' delete next 1 messagebox('la CANTIDAD MAXIMA DE LINEAS PERMITIDA'+CHR(13)+'PARA UNA FACTURA ES:'; + str(thisform.nmaxlinesallowed),16,'error') This.Parent.Parent.refresh && refrescamos la cuadricula Analicemos este código 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 máxima de líneas que puede tener una factura. Esto es importante si utilizamos formularios preimpresos de facturas, cuyo espacio de impresión de líneas de detalle es limitado. Si se excede el máximo permitido, un mensaje de error le avisa al usuario y se borra el registro recientemente añadido. Si el limite anterior no se alcanzo, se agrega un nuevo registro al cursor INVOICE. Forzamos el foco en la ultima línea de la cuadricula con la combinación de los comandos GO BOTTOM y KEYBOARD '{DNARROW}' Tome nota de la prueba adicional: si no hay ningún código de articulo en la primera columna, entonces se agrega un registro nuevo, de lo contrario, no pasa nada. Esto evita que se agreguen líneas inútiles en la cuadricula. El método Inv_totals Cada vez que agregamos una nueva línea, 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 línea, o conjunto de líneas, según 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 líneas), y todos los impuestos, descuentos globales o ítems adicionales que correspondan (fletes, intereses o lo que fuera). La ultima línea 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 línea, abajo de todo, o en forma encolumnada, una encima de otra. En realidad, esto es una elección del programador y no va a afectar los cálculos de ningún modo. Los resultados de los cálculos se mostraran en etiquetas o en textboxes, a elección del programador. En mi caso, yo prefiero usar etiquetas. Por ejemplo, si el subtotal de la factura fuera de 2,000.00 dólares y la etiqueta para mostrar esta cifra tuviera el muy imaginativo nombre de lblsubtotal, utilizaríamos la siguiente línea 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 método 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 Suponiendo que tenemos una propiedad para el descuento global llamada ndiscount y otra propiedad para el porcentaje, con el nombre de npercent, y además, que el usuario ingreso un 5% de descuento global, calcularíamos 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 país. Supongamos que tenemos un impuesto federal a las ventas del 7% y un impuesto provincial del 8% Entonces tendríamos 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 método con una llamada al método que mostrara todos estos valores calculados en sus respectivas etiquetas al pie de la factura. Thisform.show_labels() El método Show_labels Como se preanuncio en la sección 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 = transform(.nsubtotal, cpic ).lbldisc.caption = transform(.ndiscount, cpic ).lblst2.caption = transform(.nst2, cpic ).lblft.caption = transform(.ntax1, cpic ).lblpt.caption = transform(.ntax2, cpic ).lbltotal.caption = transform(.ntotal, cpic ) El método Grabar No voy a mostrar aquí como grabar la factura usando transacciones, porque tomaría bastante mas espacio del que dispongo y, además, excedería el alcance de este articulo. Pero puedo decir que la grabación consiste en usar un loop scan - endscan sobre el cursor INVOICE, grabando todas sus líneas en las tablas correspondientes. Conclusión 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 metodología indicada: llamo a una tabla de consulta para la búsqueda de artículos, apretando una tecla de función cuando el foco esta en la columna de artículos (esto se hace programando el evento keypress del textbox), o muestro la cuenta del cliente, si la venta es a crédito, 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 muchísimo nuestras pantallas de ingreso de datos. Con un poco de trabajo y mucha fe, podemos hacerlo. Así que, buena suerte en su tarea!