Programando con Café

Programando con Café es el lugar de encuentro de todos sus tutoriales de programación que usted necesita en diferentes lenguajes de programación y base de datos

sábado, febrero 26, 2011

Java - Aplicación Web, Carrito de Compras Parte III - Creación del Controlador


"Si usa algún código del siguiente tutorial, den el icono de ME GUSTA del Facebook que se encuentra en su mano derecha, para que se vuelva Seguidor del Blog y también comentenos que tal les pareció el tutorial"

1. Entorno


  • MySQL 5.1.
  • NetBeans IDE 6.9.1
  • Apache Tomcat
  • JDK 1.6.0 - 21

2. Introducción


El controlador responde usualmente a acciones del usuario, e invoca peticiones al modelo y, probablemente, a la vista. En Java por lo general los controladores se encuentran en un Servlet y es el que recibe todas las peticiones de la Vista y llama al Modelo. Y es por eso que en esta parte de tutorial vamos a desarrollar lo que corresponde al Controlador. Para no perder la idea del tema les recomiendo que revisen información sobre peticiones Post, Get y sobre manejo de Sesiones con Java.

3. Desarrollo


3.1. Creando el paquete

Lo que primero debemos de hacer es crear un paquete dentro de nuestro proyecto en Netbeans el cual se llamara "Controlador" y en este paquete es donde se almacenara los Servlet que recibirá las peticiones de la Vista y la comunicara con el Modelo. El Servlet que vamos a crear lo llamaremos "Controlador.java". Para crear un Servlet primero debemos hacer clic derecho en nuestro paquete que se llama "Controlador" ir luego a "New" y finalmente "Servlet"
















3.2. La clase Controlador

Como les dije esta clase se encargara de recibir todas las peticiones de la vista, en nuestro caso de los JSP que vamos a crear y se encargara de llamar a los métodos de la base de datos para realizar las inserciones, actualizaciones o consultas del caso.

package Controlador;

import Modelo.DetalleVenta;
import Modelo.Producto;
import Modelo.ProductoBD;
import Modelo.Venta;
import Modelo.VentaBD;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//@author Henry Joe Wong Urquiza

public class Controlador extends HttpServlet {

    //Para controlar peticiones del tipo GET
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    //Para controlar peticiones del tipo POST
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    //Un metodo que recibe todas las peticiones a si sea GET y POST
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //La accion se va a guardar en un caja de texto oculto que nos dira que accion
        //debemos hacer
        String accion = request.getParameter("accion");
        if (accion.equals("RegistrarProducto")) {
            this.registrarProducto(request, response);
        } else if (accion.equals("ModificarProducto")) {
            this.actualizarProducto(request, response);
        } else if (accion.equals("AnadirCarrito")) {
            this.añadirCarrito(request, response);
        } else if (accion.equals("RegistrarVenta")) {
            this.registrarVenta(request, response);
        }

    }
    //Metodo que sirve para registrar un producto 
    private void registrarProducto(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Producto p = new Producto();
        //request.getParameter --> Sirve para obtener los valores de las cajas de texto
        p.setNombre(request.getParameter("txtNombre").toUpperCase());
        p.setPrecio(Double.parseDouble(request.getParameter("txtPrecio")));
        boolean rpta = ProductoBD.insertarProducto(p);
        if (rpta) {
            //Si inserto lo redireccionamos a otra pagina que se llama "mensaje.jsp"
            response.sendRedirect("mensaje.jsp?men=Se registro del producto de manera correcta");
        } else {
            //Si no se inserto lo redireccionamos a otra pagina que se llama "mensaje.jsp"
            response.sendRedirect("mensaje.jsp?men=No se registro el producto");
        }
    }
    //Metodo que sirve para actualizar un producto
    private void actualizarProducto(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Producto p = new Producto();
        p.setCodigoProducto(Integer.parseInt(request.getParameter("txtCodigo")));
        p.setNombre(request.getParameter("txtNombre").toUpperCase());
        p.setPrecio(Double.parseDouble(request.getParameter("txtPrecio")));
        boolean rpta = ProductoBD.actualizarProducto(p);
        if (rpta) {
            response.sendRedirect("mensaje.jsp?men=Se actualizo el producto de manera correcta");
        } else {
            response.sendRedirect("mensaje.jsp?men=No se actualizo el producto");
        }
    }
    //Sirve para añadir un detalle al carrito
    //La informacion del carrito de compras se guarda en una sesion
    private void añadirCarrito(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //Obtenemos la sesion actual
        HttpSession sesion = request.getSession();
        ArrayList<DetalleVenta> carrito;
        //Si no existe la sesion creamos al carrito de cmoras
        if (sesion.getAttribute("carrito") == null) {
            carrito = new ArrayList<DetalleVenta>();
        } else {
            carrito = (ArrayList<DetalleVenta>) sesion.getAttribute("carrito");
        }
        //Obtenemos el producto que deseamos añadir al carrito
        Producto p = ProductoBD.obtenerProducto(Integer.parseInt(request.getParameter("txtCodigo")));
        //Creamos un detalle para el carrtio
        DetalleVenta d = new DetalleVenta();
        //Obtenemos los valores de la caja de texto
        d.setCodigoProducto(Integer.parseInt(request.getParameter("txtCodigo")));
        d.setProducto(p);
        d.setCantidad(Double.parseDouble(request.getParameter("txtCantidad")));
        //Calculamos el descuento, si es sub detalle es mayor a 50 se le hace
        //un descuento del 5% aca es donde se encuentra la logica del negocio
        double subTotal = p.getPrecio() * d.getCantidad();
        if (subTotal > 50) {
            d.setDescuento(subTotal * (5D / 100D));
        } else {
            d.setDescuento(0);
        }
        //Sirva para saber si tenemos agregado el producto al carrito de compras
        int indice = -1;
        //recorremos todo el carrito de compras
        for (int i = 0; i < carrito.size(); i++) {
            DetalleVenta det = carrito.get(i);
            if (det.getCodigoProducto() == p.getCodigoProducto()) {
                //Si el producto ya esta en el carrito, obtengo el indice dentro
                //del arreglo para actualizar al carrito de compras
                indice = i;
                break;
            }
        }
        if (indice == -1) {
            //Si es -1 es porque voy a registrar
            carrito.add(d);
        } else {
            //Si es otro valor es porque el producto esta en el carrito
            //y vamos actualizar la 
            carrito.set(indice, d);
        }
        //Actualizamos la sesion del carrito de compras
        sesion.setAttribute("carrito", carrito);
        //Redireccionamos al formulario de culminar la venta
        response.sendRedirect("registrarVenta.jsp");
    }
    //Metodo que sirve para registrar toda la venta en la base de datos
    private void registrarVenta(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpSession sesion = request.getSession();
        Venta v=new Venta();
        v.setCliente(request.getParameter("txtCliente").toUpperCase());
        ArrayList<DetalleVenta> detalle  = (ArrayList<DetalleVenta>) sesion.getAttribute("carrito");
        boolean rpta=VentaBD.insertarVenta(v, detalle);
        if (rpta) {
            response.sendRedirect("mensaje.jsp?men=Se registro la venta de manera correcta");
        } else {
            response.sendRedirect("mensaje.jsp?men=No se registro la venta");
        }
    }
}

jueves, febrero 24, 2011

Java - Aplicación Web, Carrito de Compras Parte II - Creación del Modelo


"Si usa algún código del siguiente tutorial, den el icono de ME GUSTA del Facebook que se encuentra en su mano derecha, para que se vuelva Seguidor del Blog y también comentenos que tal les pareció el tutorial"

1. Entorno


  • MySQL 5.1.
  • NetBeans IDE 6.9.1
  • Apache Tomcat
  • JDK 1.6.0 - 21

2. Introducción


El modelo es la representación específica de la información con la cual el sistema opera. En resumen, el modelo se limita a lo relativo de la vista y su controlador facilitando las presentaciones visuales complejas. El sistema también puede operar con más datos no relativos a la presentación, haciendo uso integrado de otras lógicas de negocio y de datos afines con el sistema modelado. Acá crearemos nuestros JavaBeans y las clases que se encargaran de llamar a los procedimientos almacenados que hemos creado en nuestra base de datos BDTutorial que esta en MySQL 5.1.. Para comunicar Java con Base de datos usamos lo que comúnmente se conoce como JDBC y para ejecutar consultas o inserciones a nuestra base de datos usamos las interfaces java.sql.Statement que espera que se le pase una sentencia SQL de forma completa por ejemplo "insert into producto values(1,'valor01')", para ejecutar sentencias preparadas se utiliza la interfaz java.sql.PreparedStatement que en los lugares donde se deben de poner los valores para la sentecia SQL se representan entre ? para después poder asignar sus valores respectivos por ejemplo "insert into producto values(?,?)" y la ultima clase para llamar a procedimientos almacenados es la clase java.sql.CallableStatement que es la que utilizaremos para este tutorial

3. Creando el Proyecto


3.1. Proyecto en Netbeans

Primero debemos de crear un proyecto web en Netbeans y para eso abriremos el Netbeans y buscaremos el menu File y luego New Project. Y seleccionaremos que deseamos crear un proyecto del tipo Web Application
















De ahí presionamos el botón Next y nos pedirá que pongamos un nombre a nuestro proyecto y lo llamaremos CarritoComprasJSP

















Luego nos pedirá que seleccionamos en que servidor queremos que se ejecute nuestro proyecto web. En este caso lo ejecutaremos en el servidor de Apache Tomcat
















3.2. Estructura del proyecto

Para explicar la estructura que vamos a tener del proyecto, les presento el siguiente gráfico que nos indicara como se distribuirá el Modelo, el Controlador y la Vista















4. Desarrollo


4.1. Creando el Paquete

Primero debemos de crear nuestro paquete que se llamara Modelo. En el gráfico se explica como crear el paquete


















Y al paquete lo llamaremos Modelo

4.2. Creando los JavaBeans

Para crear los JavaBeans debemos de crear las clases que tendran la misma estructura de las tablas de nuestra base de datos, eso quiere decir que si por ejemplo en alguna tabla de nuestra base de datos tenemos una columna del tipo Varchar en Java tendriamos una variable del tipo String, si tenemos un variable del tipo Decimal en Java seria double. Para eso hacemos clic derecho en nuestro paquete que se llama Modelo, de ahi nos vamos a New y seleccionamos Java Class

4.2.1. Clase Producto

La clase Producto tiene la misma estructura de nuestra tabla Producto de nuestra base de datos, a continuación les comparto el código fuente de la clase.


package Modelo;

//@author Henry Joe Wong Urquiza
 
public class Producto {
    //Las columnas que tiene la tabla Producto
    private int codigoProducto;
    private String nombre;
    private double precio;
    //Constructor de la clase sin parametros
    public Producto() {
    }
    //Constructor de la clase con parametros
    public Producto(int codigoProducto, String nombre, double precio) {
        this.codigoProducto = codigoProducto;
        this.nombre = nombre;
        this.precio = precio;
    }
    //Metodo toString de la clase que nos retorna
    //el nombre del producto
    @Override
    public String toString() {
        return nombre.toUpperCase();
    }
    //Metodos get y set de la clase
    public int getCodigoProducto() {
        return codigoProducto;
    }

    public void setCodigoProducto(int codigoProducto) {
        this.codigoProducto = codigoProducto;
    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public double getPrecio() {
        return precio;
    }

    public void setPrecio(double precio) {
        this.precio = precio;
    }

}


4.2.2. Clase Venta

La clase Venta tiene la misma estructura de nuestra tabla Venta de nuestra base de datos, a continuación les comparto el código fuente de la clase.

package Modelo;

// @author hwong
import java.sql.Timestamp;

public class Venta {
    //Las columnas que tiene la tabla Venta

    private int codigoVenta;
    private String cliente;
    private Timestamp fecha;
    //Constructor de la clase sin parametros

    public Venta() {
    }
    //Constructor de la clase con parametros

    public Venta(int codigoVenta, String cliente, Timestamp fecha) {
        this.codigoVenta = codigoVenta;
        this.cliente = cliente;
        this.fecha = fecha;
    }
    //Metodos get y set de la clase

    public String getCliente() {
        return cliente;
    }

    public void setCliente(String cliente) {
        this.cliente = cliente;
    }

    public int getCodigoVenta() {
        return codigoVenta;
    }

    public void setCodigoVenta(int codigoVenta) {
        this.codigoVenta = codigoVenta;
    }

    public Timestamp getFecha() {
        return fecha;
    }

    public void setFecha(Timestamp fecha) {
        this.fecha = fecha;
    }

}

4.2.3. Clase DetalleVenta

La clase DetalleVenta tiene la misma estructura de nuestra tabla DetalleVenta de nuestra base de datos, a continuación les comparto el código fuente de la clase.

package Modelo;

// @author hwong
public class DetalleVenta {
    //Las columnas que tiene la tabla DetalleVenta

    private int codigoVenta;
    private int codigoProducto;
    private double cantidad;
    private double descuento;
    private Producto producto;
    private Venta venta;

    //Constructor sin parametros
    public DetalleVenta() {
    }
    //Constructor con parametros

    public DetalleVenta(int codigoVenta, int codigoProducto, double cantidad, double descuento) {
        this.codigoVenta = codigoVenta;
        this.codigoProducto = codigoProducto;
        this.cantidad = cantidad;
        this.descuento = descuento;
    }
    //Metodos Get y Set de la clase

    public double getCantidad() {
        return cantidad;
    }

    public void setCantidad(double cantidad) {
        this.cantidad = cantidad;
    }

    public int getCodigoProducto() {
        return codigoProducto;
    }

    public void setCodigoProducto(int codigoProducto) {
        this.codigoProducto = codigoProducto;
    }

    public int getCodigoVenta() {
        return codigoVenta;
    }

    public void setCodigoVenta(int codigoVenta) {
        this.codigoVenta = codigoVenta;
    }

    public double getDescuento() {
        return descuento;
    }

    public void setDescuento(double descuento) {
        this.descuento = descuento;
    }

    public Producto getProducto() {
        return producto;
    }

    public void setProducto(Producto producto) {
        this.producto = producto;
    }

    public Venta getVenta() {
        return venta;
    }

    public void setVenta(Venta venta) {
        this.venta = venta;
    }
}

4.2.4. Estructura final

Al finalizar de crear las clases deberiamos tener 3 clases tal como se muestra en el grafico siguiente

















4.3. Creando las clases para comunicar con la base de datos

4.3.1. Cargando el Driver

Para poder comunicar nuestro proyecto con MySQL 5.1. primero debemos de adicionar el driver a nuestro proyecto. Para eso hacemos clic derecho en nuestro proyecto y nos vamos a la opción que dice Properties y seleccionamos de ahí Libraries y presionamos el botón que dice Add Library y buscamos el que dice MySQL JDBC Driver

















4.3.2. Creando la clase Conexion

Esta clase es la que centraliza la conexión con nuestra base de datos que esta en MySQL nos va dar soporte para ciertos métodos que vamos a usar en nuestras demás clases

package Modelo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

//@author Henry Joe Wong Urquiza

public class Conexion {
    //La url con la cual nos conectariamos a la base de datos
    private static String url = "jdbc:mysql://localhost/bdtutorial";
    //El usuario de la base de datos
    private static String user = "root";
    //La clave del usuario de la base de datos
    private static String password = "clave";
    //Metodo para obtener la conexion con la base de datos
    public static synchronized Connection getConexion() {
        Connection cn = null;
        try {
            //Cargamos el driver y le decimos que vamos a usar
            //una conexion con mysql
            Class.forName("com.mysql.jdbc.Driver");
            //Obtenemos la conexion
            cn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            cn = null;
        } finally {
            return cn;
        }
    }
    //Metodo utilizado para cerrar el callablestatemente
    public static synchronized void cerrarCall(CallableStatement cl) {
        try{cl.close();}catch(Exception e){}
    }
    //Metodo utilizado para cerrar el resulset de datos
    public static synchronized void cerrarConexion(ResultSet rs) {
        try{rs.close();} catch (Exception e) {}
    }
    //Metodo utilizado para cerrar la conexion
    public static synchronized void cerrarConexion(Connection cn) {
        try{cn.close();} catch (Exception e) {}
    }
    //Metodo utilizado para deshacer los cambios en la base de datos
    public static synchronized void deshacerCambios(Connection cn) {
        try{cn.rollback();}catch (Exception e){}
    }
}


4.3.3. Creando la clase ProductoBD

Que se encargara de hacer las consultas o inserciones a nuestra tabla Producto


package Modelo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;

// @autor Henry Joe Wong Uruqiza
// Archivo: ProductoBD.java
// Creado: 24FEBRERO2011 12:39:08 PM
public class ProductoBD {
    //Metodo utilizado para insertar un Producto a nuestra Base de datos

    public static synchronized boolean insertarProducto(Producto varproducto) {
        Connection cn = null;
        CallableStatement cl = null;
        boolean rpta = false;
        try {
            //Nombre del procedimiento almacenado y como espera tres parametros
            //le ponemos 3 interrogantes
            String call = "{CALL spI_producto(?,?,?)}";
            //Obtenemos la conexion
            cn = Conexion.getConexion();
            //Decimos que vamos a crear una transaccion
            cn.setAutoCommit(false);
            //Preparamos la sentecia
            cl = cn.prepareCall(call);
            //Como el codigo se autogenera y es del tipo OUT en el procedimiento
            //almacenado le decimos que es OUT y el del tipo Integer en Java
            cl.registerOutParameter(1, Types.INTEGER);
            //El siguiente parametro del procedimiento almacenado es el nombre
            cl.setString(2, varproducto.getNombre());
            //Y por ultimo el precio 
            cl.setDouble(3, varproducto.getPrecio());
            //Ejecutamos la sentencia y si nos devuelve el valor de 1 es porque
            //registro de forma correcta los datos
            rpta = cl.executeUpdate() == 1 ? true : false;
            if (rpta) {
                //Confirmamos la transaccion
                cn.commit();
            } else {
                //Negamos la transaccion
                Conexion.deshacerCambios(cn);
            }
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.deshacerCambios(cn);
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.deshacerCambios(cn);
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        }
        return rpta;
    }

    //Metodo utilizado para insertar un Producto a nuestra Base de datos
    public static synchronized boolean actualizarProducto(Producto varproducto) {
        Connection cn = null;
        CallableStatement cl = null;
        boolean rpta = false;
        try {
            //Nombre del procedimiento almacenado y como espera tres parametros
            //le ponemos 3 interrogantes
            String call = "{CALL spU_producto(?,?,?)}";
            //Obtenemos la conexion
            cn = Conexion.getConexion();
            //Decimos que vamos a crear una transaccion
            cn.setAutoCommit(false);
            //Preparamos la sentecia
            cl = cn.prepareCall(call);
            //El primer parametro del procedimiento almacenado es el codigo
            cl.setInt(1, varproducto.getCodigoProducto());
            //El siguiente parametro del procedimiento almacenado es el nombre
            cl.setString(2, varproducto.getNombre());
            //Y por ultimo el precio
            cl.setDouble(3, varproducto.getPrecio());
            //Ejecutamos la sentencia y si nos devuelve el valor de 1 es porque
            //registro de forma correcta los datos
            rpta = cl.executeUpdate() == 1 ? true : false;
            if (rpta) {
                //Confirmamos la transaccion
                cn.commit();
            } else {
                //Negamos la transaccion
                Conexion.deshacerCambios(cn);
            }
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.deshacerCambios(cn);
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.deshacerCambios(cn);
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        }
        return rpta;
    }
    //Metodo utilizado para obtener todos los productos de nuestra base de datos

    public static synchronized ArrayList<Producto> obtenerProducto() {
        //El array que contendra todos nuestros productos
        ArrayList<Producto> lista = new ArrayList<Producto>();
        Connection cn = null;
        CallableStatement cl = null;
        ResultSet rs = null;
        try {
            //Nombre del procedimiento almacenado
            String call = "{CALL spF_producto_all()}";
            cn = Conexion.getConexion();
            cl = cn.prepareCall(call);
            //La sentencia lo almacenamos en un resulset
            rs = cl.executeQuery();
            //Consultamos si hay datos para recorrerlo
            //e insertarlo en nuestro array
            while (rs.next()) {
                Producto p = new Producto();
                //Obtenemos los valores de la consulta y creamos
                //nuestro objeto producto
                p.setCodigoProducto(rs.getInt("codigoProducto"));
                p.setNombre(rs.getString("nombre"));
                p.setPrecio(rs.getDouble("precio"));
                //Lo adicionamos a nuestra lista
                lista.add(p);
            }
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        }
        return lista;
    }

    //Metodo utilizado para obtener todos los productos de nuestra base de datos
    public static synchronized Producto obtenerProducto(int codigo) {
        Producto p = new Producto();
        Connection cn = null;
        CallableStatement cl = null;
        ResultSet rs = null;
        try {
            //Nombre del procedimiento almacenado
            String call = "{CALL spF_producto_one(?)}";
            cn = Conexion.getConexion();
            cl = cn.prepareCall(call);
            cl.setInt(1, codigo);
            //La sentencia lo almacenamos en un resulset
            rs = cl.executeQuery();
            //Consultamos si hay datos para recorrerlo
            //e insertarlo en nuestro array
            while (rs.next()) {
                //Obtenemos los valores de la consulta y creamos
                //nuestro objeto producto
                p.setCodigoProducto(rs.getInt("codigoProducto"));
                p.setNombre(rs.getString("nombre"));
                p.setPrecio(rs.getDouble("precio"));
            }
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        }
        return p;
    }
}


4.3.4. Creando la clase DetalleVentaBD

Sirve para insertar un detalle de la venta, el metodo insertarDetalle espera obtener la conexion y la transaccion de la clase Venta. Debido a que esta ultima clase es la que inicia la transaccion con la base de datos y es la única encargada de confirmar o negar la transaccion

package Modelo;

// @autor Henry Joe Wong Uruqiza
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;

// Archivo: DetalleVentaBD.java
// Creado: 24FEBRERO2011 12:39:08 PM
public class DetalleVentaBD {

    //Metodo utilizado para insertar un Detalle de Venta a nuestra Base de datos
    //Obtenemos la conexion de Venta debido a que la clase Venta es la que inicia
    //la transaccion
    public static synchronized boolean insertarDetalleVenta(DetalleVenta varDetalle, Connection cn) {
        CallableStatement cl = null;
        boolean rpta = false;
        try {
            //Nombre del procedimiento almacenado y como espera tres parametros
            //le ponemos 3 interrogantes
            String call = "{CALL spI_detalleventa(?,?,?,?)}";
//Preparamos la sentecia
            cl = cn.prepareCall(call);
            //Codigo de la venta
            cl.setInt(1, varDetalle.getCodigoVenta());
            //Codigo del producto
            cl.setInt(2, varDetalle.getCodigoProducto());
            //La cantidad
            cl.setDouble(3, varDetalle.getCantidad());
            //El descuento
            cl.setDouble(4, varDetalle.getDescuento());
            //Ejecutamos la sentencia y si nos devuelve el valor de 1 es porque
            //registro de forma correcta los datos
            rpta = cl.executeUpdate() == 1 ? true : false;
            Conexion.cerrarCall(cl);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
        }
        return rpta;
    }
}

4.3.5. Creando la clase VentaBD

package Modelo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;

// @autor Henry Joe Wong Uruqiza
// Archivo: VentaBD.java
// Creado: 24FEBRERO2011 12:39:08 PM
public class VentaBD {

    //Metodo utilizado para insertar una Venta a nuestra Base de datos
    public static synchronized boolean insertarVenta(Venta varventa, ArrayList<DetalleVenta> detalle) {

        Connection cn = null;
        CallableStatement cl = null;
        boolean rpta = false;
        try {
            //Nombre del procedimiento almacenado y como espera tres parametros
            //le ponemos 3 interrogantes
            String call = "{CALL spI_venta(?,?)}";
//Obtenemos la conexion
            cn = Conexion.getConexion();
            //Decimos que vamos a crear una transaccion
            cn.setAutoCommit(false);
            //Preparamos la sentecia
            cl = cn.prepareCall(call);
            //Como el codigo se autogenera y es del tipo OUT en el procedimiento
            //almacenado le decimos que es OUT y el del tipo Integer en Java
            cl.registerOutParameter(1, Types.INTEGER);
            //El siguiente parametro del procedimiento almacenado es el cliente
            cl.setString(2, varventa.getCliente());
            //Ejecutamos la sentencia y si nos devuelve el valor de 1 es porque
            //registro de forma correcta los datos
            rpta = cl.executeUpdate() == 1 ? true : false;
            //Codigo que se genero producto de la insercion ---> codigoVenta
            varventa.setCodigoVenta(cl.getInt(1));
            if (rpta) {
                for (DetalleVenta det : detalle) {
                    //Establecemos al detalle el codigo genero producto de la venta
                    det.setCodigoVenta(varventa.getCodigoVenta());
                    //Insertamos el detalle y le pasamos la conexion
                    rpta = DetalleVentaBD.insertarDetalleVenta(det, cn);
                    //Si nos devuelve false salimos del for
                    if (!rpta) {
                        break;
                    }
                }
                if (rpta) {
                    //Confirmamos la transaccion
                    cn.commit();
                } else {
                    //Negamos la transaccion
                    Conexion.deshacerCambios(cn);
                }
            } else {
                //Negamos la transaccion
                Conexion.deshacerCambios(cn);
            }
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.deshacerCambios(cn);
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.deshacerCambios(cn);
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        }
        return rpta;
    }

    //Metodo utilizado para obtener todos las ventas de nuestra base de datos
    public static synchronized ArrayList<DetalleVenta> obtenerVentas() {
        //El array que contendra todos nuestros productos
        ArrayList<DetalleVenta> lista = new ArrayList<DetalleVenta>();
        Connection cn = null;
        CallableStatement cl = null;
        ResultSet rs = null;
        try {
            //Nombre del procedimiento almacenado
            String call = "{CALL spF_venta_All()}";
cn = Conexion.getConexion();
            cl = cn.prepareCall(call);
            //La sentencia lo almacenamos en un resulset
            rs = cl.executeQuery();
            //Consultamos si hay datos para recorrerlo
            //e insertarlo en nuestro array
            while (rs.next()) {
                Venta ven=new Venta();
                Producto pro=new Producto();
                DetalleVenta det=new DetalleVenta();
                ven.setCodigoVenta(rs.getInt("CodigoVenta"));
                ven.setCliente(rs.getString("Cliente"));
                ven.setFecha(rs.getTimestamp("Fecha"));
                pro.setCodigoProducto(rs.getInt("CodigoProducto"));
                pro.setNombre(rs.getString("Nombre"));
                pro.setPrecio(rs.getDouble("Precio"));
                det.setCantidad(rs.getDouble("Cantidad"));
                det.setDescuento(rs.getDouble("Parcial"));
                det.setVenta(ven);
                det.setProducto(pro);
                lista.add(det);
            }
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (SQLException e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        } catch (Exception e) {
            e.printStackTrace();
            Conexion.cerrarCall(cl);
            Conexion.cerrarConexion(cn);
        }
        return lista;
    }
}

4.4. Estructura final de todas las clases

Java - Aplicación Web, Carrito de Compras Parte I - Creación de la Base de Datos


"Si usa algún código del siguiente tutorial, den el icono de ME GUSTA del Facebook que se encuentra en su mano derecha, para que se vuelva Seguidor del Blog y también comentenos que tal les pareció el tutorial"

1. Entorno


  • MySQL 5.1.
  • NetBeans IDE 6.9.1
  • Apache Tomcat
  • JDK 1.6.0 - 21

2. Introducción


En el siguiente tutorial vamos a desarrollar un carrito de compras o lo que seria un sistema de ventas usando el lenguaje de programación Java, como base de datos vamos a usar el MySQL 5.1. y tambien crearemos un reporte con JasperReports. La base de datos que vamos a crear es una base de datos con fines didácticos, dado que nos faltaría crear mas tablas, como la tabla cliente, categoría, unidad de medida, etc pero si nos explayamos mucho nos quedaría muy largo el tutorial. Ademas en el ejemplo no se explica como disminuir un stock, debido a que eso siempre se les pide a los alumnos universitario que investiguen como hacerlo, pero si quieren una ayudita lo pueden hacer creando Triggers en la base de datos. La aplicación se desarrollara en un formato web usando el patrón de Modelo, Vista y Controlador. Y para la parte de la vista usaremos lo que se conoce como Java Server Page (JSP). Espero que el ejemplo se de su utilidad.

3. Desarrollo


En esta primera parte del tutorial vamos a crear la base de datos en MySQL 5.1., la base de datos se llamara BDTutorial

3.1. Diseño de la base de datos

La base de datos tendría las siguientes tablas: Venta, Producto y DetalleVenta















A continuación les paso el script de creación de la base de datos

-- CREADO POR: HENRY JOE WONG URQUIZA
-- FECHA: 24FEB2011
-- ------------------------------------------------------
-- TUTORIAL DE COMO HACER UN CARRITO DE COMPRAS USANDO
-- EL MODELO VISTA CONTROLADOR
--
-- Creando la base de datos
--

CREATE DATABASE IF NOT EXISTS bdtutorial;
USE bdtutorial;

--
-- Creando la tabla `detalleventa`
--

DROP TABLE IF EXISTS `detalleventa`;
CREATE TABLE `detalleventa` (
  `codigoVenta` int(11) NOT NULL,
  `codigoProducto` int(11) NOT NULL,
  `cantidad` decimal(18,2) NOT NULL,
  `descuento` decimal(18,2) NOT NULL,
  PRIMARY KEY  (`codigoVenta`,`codigoProducto`),
  KEY `FK_DetalleVenta_Producto` (`codigoProducto`),
  CONSTRAINT `FK_DetalleVenta_Producto` FOREIGN KEY (`codigoProducto`) REFERENCES `producto` (`codigoProducto`),
  CONSTRAINT `FK_DetalleVenta_Venta` FOREIGN KEY (`codigoVenta`) REFERENCES `venta` (`codigoVenta`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Creando la tabla `producto`
--

DROP TABLE IF EXISTS `producto`;
CREATE TABLE `producto` (
  `codigoProducto` int(11) NOT NULL,
  `nombre` varchar(100) NOT NULL,
  `precio` decimal(18,2) NOT NULL,
  PRIMARY KEY  (`codigoProducto`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Creando la tabla venta
--

DROP TABLE IF EXISTS `venta`;
CREATE TABLE `venta` (
  `codigoVenta` int(11) NOT NULL,
  `cliente` varchar(100) NOT NULL,
  `fecha` datetime NOT NULL,
  PRIMARY KEY  (`codigoVenta`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

3.2. Creando los procedimientos almacenados

Según Wikipedia un procedimiento almacenado (stored procedure en inglés) es un programa (o procedimiento) el cual es almacenado físicamente en una base de datos. Su implementación varía de un manejador de bases de datos a otro. La ventaja de un procedimiento almacenado es que al ser ejecutado, en respuesta a una petición de usuario, es ejecutado directamente en el motor de bases de datos, el cual usualmente corre en un servidor separado. Como tal, posee acceso directo a los datos que necesita manipular y sólo necesita enviar sus resultados de regreso al usuario, deshaciéndose de la sobrecarga resultante de comunicar grandes cantidades de datos salientes y entrantes.

3.2.1. Procedimientos almacenados para la tabla Producto

-- CREADO POR: HENRY JOE WONG URQUIZA
-- FECHA: 24FEBRERO2011
-- ------------------------------------------------------
-- TUTORIAL DE COMO REALIZAR UN CARRITO DE COMPRAS CON JAVA Y JSP
-- APLICANDO MODELO, VISTA Y CONTROLADOR

-- Procedimiento almacenado para insertar un producto
DROP PROCEDURE IF EXISTS `spI_producto`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spI_producto`(
   INOUT _codigoProducto  int ,
   _nombre  varchar(100) ,
   _precio  decimal(18, 2)
)
BEGIN
-- Genera una especie de autoincremental pero yo controlo los codigos
-- que genero
SELECT IFNULL(MAX(codigoProducto),0)+1 into _codigoProducto FROM `producto`;
INSERT INTO `producto`(
   `codigoProducto`,
   `nombre`,
   `precio`
)
VALUES (
   _codigoProducto,
   _nombre,
   _precio
);
END $$
DELIMITER ;

-- Procedimiento almacenado para actualizar un producto
DROP PROCEDURE IF EXISTS `spU_producto`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spU_producto`(
   _codigoProducto  int ,
   _nombre  varchar(100) ,
   _precio  decimal(18, 2)
)
BEGIN

UPDATE producto
SET 
   `nombre` = _nombre,
   `precio` = _precio
WHERE
    `codigoProducto` = _codigoProducto
;
END $$
DELIMITER ;

-- Procedimiento almacenado para obtener todos los productos
DROP PROCEDURE IF EXISTS `spF_producto_all`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spF_producto_all`(
)
BEGIN

SELECT
    p.codigoProducto,
    p.nombre,
    p.precio
FROM
    producto p
ORDER BY
    P.nombre

;
END $$
DELIMITER ;


-- Procedimiento almacenado para obtener todos los productos
DROP PROCEDURE IF EXISTS `spF_producto_one`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spF_producto_one`(
_codigoProducto  int 
) BEGIN SELECT p.codigoProducto, p.nombre, p.precio FROM producto p
WHERE
    p.codigoProducto = _codigoProducto
ORDER BY P.nombre ; END $$ DELIMITER ;


3.2.2. Procedimientos almacenados para la tabla venta

-- CREADO POR: HENRY JOE WONG URQUIZA
-- FECHA: 24FEBRERO2011
-- ------------------------------------------------------
-- TUTORIAL DE COMO REALIZAR UN CARRITO DE COMPRAS CON JAVA Y JSP
-- APLICANDO MODELO, VISTA Y CONTROLADOR

-- Procedimiento almacenado para insertar una venta
DROP PROCEDURE IF EXISTS `spI_venta`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spI_venta`(
   INOUT _codigoVenta  int ,
   _cliente  varchar(100) 
)
BEGIN
-- Codigo autogenerado
SELECT IFNULL(MAX(codigoVenta),0)+1 into _codigoVenta FROM `venta`;
INSERT INTO `venta`(
   `codigoVenta`,
   `cliente`,
   `fecha`
)
VALUES (
   _codigoVenta,
   _cliente,
   CURDATE()
);
END $$
DELIMITER ;

-- Procedimiento almacenado para obtener todas las ventas
DROP PROCEDURE IF EXISTS `spF_venta_All`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spF_venta_All`(
)
BEGIN
SELECT
    v.codigoVenta AS CodigoVenta,
    v.cliente AS Cliente, 
    v.fecha AS Fecha,
    d.codigoProducto AS CodigoProducto, 
    p.nombre AS Nombre,
    p.precio AS Precio, 
    d.cantidad AS Cantidad,
    d.descuento AS Descuento,
    p.precio*d.cantidad AS Parcial,
    ((p.precio*d.cantidad)-d.descuento) AS SubTotal,
    (
    SELECT     
        SUM((dT.cantidad * pT.precio)-dT.descuento) AS TotalPagar
    FROM         
        DetalleVenta AS dT INNER JOIN
        Producto AS pT ON dT.codigoProducto = pT.codigoProducto
    WHERE
        dT.codigoVenta=v.codigoVenta
    ) AS TotalPagar
FROM 
    Venta AS v INNER JOIN
    DetalleVenta AS d ON v.codigoVenta = d.codigoVenta INNER JOIN
    Producto AS p ON d.codigoProducto = p.codigoProducto
ORDER BY
    CodigoVenta, Nombre
 ;
END $$
DELIMITER ;

-- Procedimiento almacenado para obtener una venta
DROP PROCEDURE IF EXISTS `spF_venta_one`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spF_venta_one`(
  _codigoVenta  int
)
BEGIN
SELECT
    v.codigoVenta AS CodigoVenta,
    v.cliente AS Cliente, 
    v.fecha AS Fecha, 
    d.codigoProducto AS CodigoProducto, 
    p.nombre AS Nombre,
    p.precio AS Precio, 
    d.cantidad AS Cantidad, 
    d.descuento AS Descuento,
    p.precio*d.cantidad AS Parcial,
    ((p.precio*d.cantidad)-d.descuento) AS SubTotal,
    (
    SELECT     
        SUM((dT.cantidad * pT.precio)-dT.descuento) AS TotalPagar
    FROM         
        DetalleVenta AS dT INNER JOIN
        Producto AS pT ON dT.codigoProducto = pT.codigoProducto
    WHERE
        dT.codigoVenta=v.codigoVenta
    ) AS TotalPagar
FROM 
    Venta AS v INNER JOIN
    DetalleVenta AS d ON v.codigoVenta = d.codigoVenta INNER JOIN
    Producto AS p ON d.codigoProducto = p.codigoProducto
WHERE
    v.codigoVenta=_codigoVenta
ORDER BY
    Nombre
;
END $$
DELIMITER ;

3.2.3. Procedimientos almacenados para la tabla DetalleVenta

-- CREADO POR: HENRY JOE WONG URQUIZA
-- FECHA: 24FEB2011
-- ------------------------------------------------------
-- TUTORIAL DE COMO HACER UN CARRITO DE COMPRAS USANDO
-- EL MODELO VISTA CONTROLADOR

-- Procedimiento almacenado para insertar un detalle de venta
DROP PROCEDURE IF EXISTS `spI_detalleventa`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `spI_detalleventa`(
   _codigoVenta  int ,
   _codigoProducto  int ,
   _cantidad  decimal(18, 2) ,
   _descuento  decimal(18, 2)
)
BEGIN

INSERT INTO `detalleventa`(
   `codigoVenta`,
   `codigoProducto`,
   `cantidad`,
   `descuento`
)
VALUES (
   _codigoVenta,
   _codigoProducto,
   _cantidad,
   _descuento
);
END $$
DELIMITER ;

miércoles, febrero 23, 2011

Sistemas de Ventas con C# - Resumen


"Si usa algún código del siguiente tutorial, den el icono de ME GUSTA del Facebook que se encuentra en su mano derecha, para que se vuelva Seguidor del Blog y también comentenos que tal les pareció el tutorial"


Espero que el tutorial que les prepare de como hacer un mini sistema de ventas con C# y SQL Server 2008 les haya servido mucho para su aprendizaje. Como vieron el tutorial se desarrollo pensando en una programación en capas pasando por:


Y fue creado solamente para fines didácticos, es por eso que la base de datos solo tiene tres tablas, cuando debería tener mas.

La solución del tutorial lo pueden bajar de AQUI

Muchas gracias por sus visitas y no se olviden de volverse fan de mi blog por Facebook para que esten al dia con los tutoriales que subo :) .... Y me despido con una buena canción que estaba escuchando por mientras preparaba el tutorial.





C# - Aplicación de Escritorio, Sistemas de Ventas Part V - Creación de Reporte (Crystal Report)


"Si usa algún código del siguiente tutorial, den el icono de ME GUSTA del Facebook que se encuentra en su mano derecha, para que se vuelva Seguidor del Blog y también comentenos que tal les pareció el tutorial"

1. Entorno


  • SQL Server 2008
  • Visual Studio 2008

2. Introducción


Continuando con el tutorial del sistemas de ventas con C#, ahora crearemos el reporte con Crystal Report que nos mostrara el documento creado producto de la venta que hemos realizado. Como este es un tutorial con un fin didáctico, para crear el reporte nosotros escribiremos el código auto generado por la inserción de la venta, aunque lo ideal seria que desde la capa de negocios obtengamos el código que se genero y se muestre el reporte de forma automática.

3. Desarrollo


3.1. Diseño del formulario

Ahora debemos de modificar nuestro formulario que hemos creado anteriormente que se llamaba frmRegistrarVenta. Si recordamos este formulario tenia un Tab Control con dos pestañas, una que se llamaba Registrar y otro que se llama Reporte. Y justo en esta ultima pestaña es donde vamos a poner nuestro reporte. Para poder mostrar los reportes en C#, debemos de agregar un componente que se llama CrystalReportViewer que se encuentra en nuestro cuadro de herramientas en el grupo de Informe



















Y luego en nuestro formulario hacemos el siguiente diseño
















3.2. Creando nuestro DataSet

Para poder crear un reporte para Crystal Reporte, lo primero que se tiene que hacer es crear un DataSet en donde se almacenara todo nuestro datos de la consulta y nos servira para llenar nuestro reporte y para eso les prepare el siguiente video para que puedan crear un DataSet de la manera mas rápida y sencilla.




3.3. Creando nuestro Reporte en Crystal Report

Ahora debemos de diseñar nuestro reporte con la información que obtenemos de nuestro DataSet que hemos creado anteriormente, para eso también les prepare otro vídeo.



El reporte al final debería tener el siguiente diseño











3.4. Código Fuente

Ahora nos vamos a ir al evento clic del botón "btnReporte" de nuestro formulario "frmRegistrarVenta" y pondremos el siguiente código fuente

private void btnReporte_Click(object sender, EventArgs e)
{
  try
  {
    //Creamos el documento 
    CrystalDecisions.CrystalReports.Engine.ReportDocument rpt=new CrystalDecisions.CrystalReports.Engine.ReportDocument();
    //Obtenemos el documento que se encuentra en nustra carpeta bin\debug\crReporte.rpt
    rpt.Load( Application.StartupPath + "\\crReporte.rpt");
    //Lleanamos el reporte con la información que obtenemos de la base de datos
    rpt.SetDataSource(NegVenta.ObtenerVenta(Convert.ToInt32(this.txtCodigoVenta.Text)));
    //Establecemos los datos al reporte
    this.crvReporte.ReportSource=rpt;
    //Refrescamos nuestro reporte
    this.crvReporte.RefreshReport();
  }
  catch (Exception ex)
  {
  }
}

4. Ejemplo de la Aplicación


C# - Aplicación de Escritorio, Sistemas de Ventas Part IV - Creación de la Capa de Presentación


"Si usa algún código del siguiente tutorial, den el icono de ME GUSTA del Facebook que se encuentra en su mano derecha, para que se vuelva Seguidor del Blog y también comentenos que tal les pareció el tutorial"

1. Entorno


  • SQL Server 2008
  • Visual Studio 2008


2. Introducción


En el tutorial en la Parte II y en la Parte III hemos creado la capa de datos y la capa de negocios respectivamente de nuestra aplicación, ahora solo nos falta crear nuestra capa de presentacion. La capa de presentacion puede ser una aplicacion del tipo escritorio, web, etc. Es por eso que se utiliza la programacion en capas, debido a que no importa que cambiamos hagamos en alguna capa no se sentira en las otras de manera brusca.

3. Desarrollo


3.1. Creando el Proyecto

Ahora debemos de agregar a nuestro proyecto un proyecto del tipo biblioteca de clases que se llamara "CapaPresentacion". No olvidar que debemos de ir a "Archivo-->Agregar-->Nuevo Proyecto"

















3.2. Agregando la referencia con la Capa de Negocios

Debemos de establecer la comunicacion entre la Capa de Presentación y la Capa de Negocios. Para eso hacemos clic derecho en nuestro proyecto que se llama "CapaPresentacion" y nos vamos a la opcion de "Agregar Referencia"























Y de ahí seleccionamos que se comunica con la Capa de Negocios




















3.3. Formulario frmMantenimientoProducto

En este formulario vamos a realizar un mantenimiento a la tabla Producto de nuestra Base de Datos que se llama BDTutorial y que esta diseñada en SQL Server 2008. A mantenimiento me refiero a los procesos de inserción, actualización y consulta de datos de la tabla Producto. Debemos de diseñar el siguiente formulario

















En el cual el componente dgvProducto tiene los siguientes valores de propiedades:

  • AllowUserToAddRows = False
  • AllowUserToDeleteRows = False
  • MultiSelect = False
  • ReadOnly = True
  • SelectionMode = FullRowSelect

Y como columnas tiene

  • codigoProducto
    • DataPropertyName = codigoProducto
    • HeaderText = codigoProducto
    • Visible = False
  • Nombre
    • DataPropertyName = nombre
    • HeaderText = Nombre
    • Visible = True
  • Precio
    • DataPropertyName = precio
    • HeaderText = Precio
    • Visible = True

En la otra pestaña del tabControl tenemos los siguientes componentes:
















Y su código fuente del formulario seria

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//Comunicarse con la Capa de Negocios
using CapaNegocios;
//Desarollado por Henry Joe Wong Urquiza
namespace CapaPresentacion
{
  public partial class frmMantenimientoProducto : Form
  {
    //Variable que nos indica si vamos a insertar un nuevo producto
    private bool nuevo = false;
    //Variable que nos indica si vamos a modificar un producto
    private bool modificar = false;
    //Constructor del formulario
    public frmMantenimientoProducto()
    {
      InitializeComponent();
    }
    //Evento que se lanza cuando se va a mostrar el formulario
    private void frmMantenimientoProducto_Load(object sender, EventArgs e)
    {
      //Para ubicar al formulario en la parte superior del contenedor
      this.Top = 0;
      this.Left = 0;
      //Le decimos al DataGridView que no auto genere las columnas
      this.dgvProductos.AutoGenerateColumns = false;
      //Llenamos el DataGridView con la informacion de todos nuestros 
      //productos
      this.dgvProductos.DataSource = NegProducto.ObtenerProducto();
      //Deshabilita los controles
      this.habilitar(false);
      //Establece los botones
      this.botones();
    }
    //Para mostrar mensaje de confirmacion
    private void mOK(string men)
    {
      MessageBox.Show(men, "MENSAJE", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    //Para mostrar mensaje de error
    private void mError(string men)
    {
      MessageBox.Show(men, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    //Limpia los controles del formulario
    private void limpiar()
    {
      this.txtNombre.Text = string.Empty;
      this.nudPrecio.Value = 0;
    }
    //Habilita los controles de los formularios
    private void habilitar(bool valor)
    {
      this.txtNombre.ReadOnly = !valor;
      this.nudPrecio.Enabled = valor; 
    }
    //Habilita los botones
    private void botones()
    {
      if (this.nuevo || this.modificar)
      {
        this.habilitar(true);
        this.btnNuevo.Enabled = false;
        this.btnGuardar.Enabled = true;
        this.btnModificar.Enabled = false;
        this.btnCancelar.Enabled = true;
      }
      else
      {
        this.habilitar(false);
        this.btnNuevo.Enabled = true;
        this.btnGuardar.Enabled = false;
        this.btnModificar.Enabled = true;
        this.btnCancelar.Enabled = false;
      }
    }
    //Evento clic del boton btnNuevo
    private void btnNuevo_Click(object sender, EventArgs e)
    {
      this.nuevo=true;
      this.modificar=false;
      this.botones();
      this.limpiar();
      this.txtCodigo.Text = string.Empty;
      this.txtNombre.Focus();
    }
    //Evento clic del boton btnGuardar
    private void btnGuardar_Click(object sender, EventArgs e)
    {
      //La variable que almacena si se inserto o se modifico la tabla
      string rpta = "";
      if(this.nuevo)
      {
        //Vamos a insertar un producto 
        rpta=NegProducto.Insertar(this.txtNombre.Text.Trim().ToUpper(),
        this.nudPrecio.Value);

      }else
      {
        //Vamos a modificar un producto
        rpta=NegProducto.Actualizar(Convert.ToInt32(this.txtCodigo.Text),
        this.txtNombre.Text.Trim().ToUpper(),
        this.nudPrecio.Value);
      }
      //Si la respuesta fue OK, fue porque se modifico o inserto el Producto
      //de forma correcta
      if (rpta.Equals("OK"))
      {
        if (this.nuevo)
        {
          this.mOK("Se inserto de forma correcta al Producto");
        }
        else 
        {
          this.mOK("Se actualizo de forma correcta al Producto");
        }

      }
      else
      {
        //Mostramos el mensaje de error
        this.mError(rpta);
      }
      this.nuevo=false;
      this.modificar=false;
      this.botones();
      this.limpiar();
      this.dgvProductos.DataSource = NegProducto.ObtenerProducto();
      this.txtCodigo.Text="";
    }
    //Evento clic del boton btnModificar
    private void btnModificar_Click(object sender, EventArgs e)
    {
      //Si no ha seleccionado un producto no puede modificar
      if(!this.txtCodigo.Text.Equals(""))
      {
        this.modificar=true;
        this.botones();
      }
      else
      {
        this.mError("Debe de buscar un producto para Modificar");
      }
    }
    //Evento clic del boton btnCancelar
    private void btnCancelar_Click(object sender, EventArgs e)
    {
      this.nuevo=false;
      this.modificar=false;
      this.botones();
      this.limpiar();
      this.txtCodigo.Text=string.Empty;
    }
    //Evento double clic del DataGridView de Productos
    private void dgvProductos_DoubleClick(object sender, EventArgs e)
    {
      this.txtCodigo.Text = Convert.ToString(this.dgvProductos.CurrentRow.Cells["codigoProducto"].Value);
      this.txtNombre.Text = Convert.ToString(this.dgvProductos.CurrentRow.Cells["nombre"].Value);
      this.nudPrecio.Value = Convert.ToDecimal(this.dgvProductos.CurrentRow.Cells["precio"].Value);
      this.tabControl.SelectedIndex = 1;
    }

  }
}

3.4. Formulario frmSeleccionarProducto

Es un formulario del tipo modal que nos permitirá seleccionar un producto de nuestra base de datos para registrar la venta. Y tiene el siguiente diseño





















En el cual el componente dgvProducto tiene los siguientes valores de propiedades:

  • AllowUserToAddRows = False
  • AllowUserToDeleteRows = False
  • MultiSelect = False
  • ReadOnly = True
  • SelectionMode = FullRowSelect

Y como columnas tiene

  • codigoProducto
    • DataPropertyName = codigoProducto
    • HeaderText = codigoProducto
    • Visible = False
  • Nombre
    • DataPropertyName = nombre
    • HeaderText = Nombre
    • Visible = True
  • Precio
    • DataPropertyName = precio
    • HeaderText = Precio
    • Visible = True
Y como código fuente tiene lo siguiente

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//Comunicarse con la Capa de Negocios
using CapaNegocios;
//Desarollado por Henry Joe Wong Urquiza
namespace CapaPresentacion
{
  public partial class frmSeleccionarProducto : Form
  {
    //El formulario padre
    private frmRegistrarVenta frame;
    //El constructor del formulario
    public frmSeleccionarProducto()
    {
      InitializeComponent();
    }
    //Establece los valores del formulario padre
    public void estableceFormulario(frmRegistrarVenta frame)
    {
      this.frame = frame;
    }
    //Evento que se ejecuta cuando se muestra el formulario
    private void frmSeleccionarProducto_Load(object sender, EventArgs e)
    {
      //Que no se genere las columnas de forma automatica
      this.dgvProducto.AutoGenerateColumns = false;
      //Obtiene todos los productos y lo asigana al DataGridView
      this.dgvProducto.DataSource = NegProducto.ObtenerProducto();
    }
    //Evento double clic del DataGridView
    private void dgvProducto_DoubleClick(object sender, EventArgs e)
    {
      //Estableciendo los datos a las cajas de texto del formulario padre
      this.frame.codigoProductoSeleccionado = Convert.ToInt32(this.dgvProducto.CurrentRow.Cells["codigoProducto"].Value);
      this.frame.txtProducto.Text = Convert.ToString(this.dgvProducto.CurrentRow.Cells["nombre"].Value);
      this.frame.txtPrecio.Text = Convert.ToString(this.dgvProducto.CurrentRow.Cells["precio"].Value);
      //Cerrando el formulario
      this.Hide();
    }

  }
}

3.5. Formulario frmRegistrarVenta

Este formulario es la parte principal del sistema en cual se registra la venta. Tener en cuenta que si el detalle de la venta es mayor a 50 soles, dolares, euros, etc se le aplica un descuento del 5% del sub total de la venta.  Debemos realizar el siguiente diseño del formulario















En cual la accesibilidad de los controles txtProducto y txtPrecio es del tipo Internal y tiene la propiedad ReadOnly en True.

Y ademas el componente dgvDetalle tiene los siguientes valores de propiedades:
  • AllowUserToAddRows = False
  • AllowUserToDeleteRows = False
  • MultiSelect = False
  • ReadOnly = True
  • SelectionMode = FullRowSelect
Y como columnas tiene
  • codigoProducto
    • DataPropertyName = codigoProducto
    • HeaderText = codigoProducto
    • Visible = False
  • Producto
    • DataPropertyName = Producto
    • HeaderText = Producto
    • Visible = True
  • Cantidad
    • DataPropertyName = cantidad
    • HeaderText = Cantidad
    • Visible = True
  • PU
    • DataPropertyName = PU
    • HeaderText = PU
    • Visible = True
  • Descuento
    • DataPropertyName = Descuento
    • HeaderText = Descuento
    • Visible = True
  • SubTotal
    • DataPropertyName = subTotal
    • HeaderText = SubTotal
    • Visible = True

Y como código fuente tiene lo siguiente:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//Comunicarse con la Capa de Negocios
using CapaNegocios;
//Desarollado por Henry Joe Wong Urquiza
namespace CapaPresentacion
{
  public partial class frmRegistrarVenta : Form
  {
    //DataTable que se encargara de guardar el detalle de la venta
    //de forma temporal
    private DataTable dtDetalle;
    //Codigo del producto seleccionado
    internal int codigoProductoSeleccionado = -1;
    //Variable que almacena el total de la venta
    private decimal totalPagar = 0;
    //El constructor de la clase
    public frmRegistrarVenta()
    {
      InitializeComponent();
    }
    //Metodo que se ejecuta al cargar el formulario
    private void frmRegistrarVenta_Load(object sender, EventArgs e)
    {
      this.Top = 0;
      this.Left = 0;
      this.crearTabla();
      this.WindowState = FormWindowState.Maximized;
    }
    //Limpia todos los controles del formulario
    private void limpiarControles()
    {
      this.txtCliente.Text = string.Empty;
      this.codigoProductoSeleccionado = -1;
      this.txtProducto.Text = string.Empty;
      this.txtPrecio.Text = string.Empty;
      this.nudCantidad.Value = 1;
      this.crearTabla();
      this.lblTotalPagar.Text = "Total Pagar: S/. 0.00";

    }
    //Crea la tabla de Detalle 
    private void crearTabla()
    {
      //Crea la tabla con el nombre de Detalle
      this.dtDetalle = new DataTable("Detalle");
      //Agrega las columnas que tendra la tabla
      this.dtDetalle.Columns.Add("codigoProducto", System.Type.GetType("System.Int32"));
      this.dtDetalle.Columns.Add("Producto", System.Type.GetType("System.String"));
      this.dtDetalle.Columns.Add("cantidad", System.Type.GetType("System.Decimal"));
      this.dtDetalle.Columns.Add("PU", System.Type.GetType("System.Decimal"));
      this.dtDetalle.Columns.Add("Descuento", System.Type.GetType("System.Decimal"));
      this.dtDetalle.Columns.Add("subTotal", System.Type.GetType("System.Decimal"));
      //Relacionamos nuestro datagridview con nuestro datatable
      this.dgvDetalle.DataSource = this.dtDetalle;

    }
    //Para mostrar mensaje de error
    private void mError(string mensaje)
    {
      MessageBox.Show(this, mensaje, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    }
    //Para mostrar mensaje de confirmación
    private void mOk(string mensaje)
    {
      MessageBox.Show(this, mensaje, "MENSAJE", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    //Evento del clic del boton btnBuscar
    private void btnBuscar_Click(object sender, EventArgs e)
    {
      //Creamos una variable del tipo del formulario que deseamos abrir
      frmSeleccionarProducto frame = new frmSeleccionarProducto();
      //Le pasamos como datos la información de nuestro formulario
      frame.estableceFormulario(this);
      //Mostrar el formulario que tiene los productos que hemos seleccionado
      frame.ShowDialog();
    }
    //Evento clic del boton agregar
    private void btnAgregar_Click(object sender, EventArgs e)
    {
      //Valida que hemos seleccionado algun producto
      if (this.codigoProductoSeleccionado == -1)
      {
        this.mError("No ha seleccionado aun ningun producto");
      }
      else
      {
        //Variable que va a indicar si podemos registrar el detalle
        bool registrar = true;
        foreach (DataRow row in dtDetalle.Rows)
        {
          if (Convert.ToInt32(row["codigoProducto"]) == this.codigoProductoSeleccionado)
          {
            registrar = false;
            this.mError("Ya se encuentra el producto en el detalle");
          }
        }
        //Si podemos registrar el producto en el detalle
        if (registrar)
        {
          //Calculamos el sub total del detalle sin descuento
          decimal subTotal = Convert.ToDecimal(this.txtPrecio.Text) * nudCantidad.Value;
          //Obtenemos el descuento
          decimal descuento = NegDetalleVenta.ObtenerDescuento(
          nudCantidad.Value,
          Convert.ToDecimal(this.txtPrecio.Text));
          //Actualizamos el sub total con el descuento correspondiente
          subTotal = subTotal - descuento;
          //Aumentamos el total a pagar
          this.totalPagar += subTotal;
          this.lblTotalPagar.Text = "Total Pagar: S/." + totalPagar.ToString("#0.00#");
          //Agregamos al fila a nuestro datatable
          DataRow row = this.dtDetalle.NewRow();
          row["codigoProducto"] = this.codigoProductoSeleccionado;
          row["Producto"] = this.txtProducto.Text;
          row["cantidad"] = this.nudCantidad.Value;
          row["PU"] = this.txtPrecio.Text ;
          row["Descuento"] = descuento;
          row["subTotal"] = subTotal;
          this.dtDetalle.Rows.Add(row);
        }
      }
    }
    //Evento click del boton quitar
    private void btnQuitar_Click(object sender, EventArgs e)
    {
      try
      {
        //Indice dila actualmente seleccionado y que vamos a eliminar
        int indiceFila = this.dgvDetalle.CurrentCell.RowIndex;
        //Fila que vamos a eliminar
        DataRow row = this.dtDetalle.Rows[indiceFila];
        //Disminuimos el total a pagar
        this.totalPagar = this.totalPagar - Convert.ToDecimal(row["subTotal"].ToString());
        this.lblTotalPagar.Text = "Total Pagar: S/." + totalPagar.ToString("#0.00#"); 
        //Removemos la fila
        this.dtDetalle.Rows.Remove(row);
      }
      catch (Exception ex)
      {
        mError("No hay fila para remover");
      }
    }

    private void btnGuardar_Click(object sender, EventArgs e)
    {
      //Debe de tener por almenos un detalle para poder registrar
      if (this.dtDetalle.Rows.Count > 0)
      {
        string rpta = NegVenta.Insertar(this.txtCliente.Text, this.dtDetalle);
        if (rpta.Equals("OK"))
        {
          mOk("Se inserto de manera correcta la venta");
          this.limpiarControles();
        }
        else
        {
          mError(rpta);
        }
      }
      else
      {
        mError("No agregado ningun detalle");
      }
    }


  }
}

4. Ejemplo de la Aplicación