Aplicación web móvil con jQuery Mobile y ASP.NET MVC 2 – parte 5

En esta última parte implementaremos el controlador mVentasController cuyas funciones serán, mostrar la lista de las ventas registradas, mostrar los detalles de la venta y registrar la venta de un auto.

El procedimiento es muy similar a lo ya realizado, empezando por agregar el nuevo controlador llamado mVentasController a Areas/m/Controllers; agregar el usin de Model al controlador y crear la instancia del modelo de entidades.

La acción y vista Index.

Index (mVentas) mostrará la lista de ventas ya registradas, donde al seleccionar una, nos muestre sus detalles.

using Agencia2012.Models;

namespace Agencia2012.Areas.m.Controllers
{
    public class mVentasController : Controller
    {
        Agencia2012Entities BD = new Agencia2012Entities();

        //
        // GET: /m/mVentas/

        public ActionResult Index()
        {
            //Devolver la lista de ventas ordenadas por fecha de forma ascendente.
            return View(BD.Venta.OrderByDescending(v => v.Fecha).ToList());
        }
    }
}

Crear la vista Index tipada de la clase Venta con contenido List.

Img. 1. Crear vista Index.

Como ya se realizó anteriormente, crear la estructura de página jQuery Mobile.

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Agencia2012.Models.Venta>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Ventas
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <div data-role="page" data-theme="c"  id="ventas">
        <script type="text/javascript">
            //Como esta vista contiene una tabla (puede no caber en la pantalla del móvil en posición vertical)
            //por lo tanto notificaremos al usuario cuando tenga su móvil en posición vertical.
            $('#ventas').live('pageshow', function () {
                if (window.orientation == 0)
                    alert('Cambie la vista a horizontal para ver todo el contenido.');
            });
        </script>
        <div data-role="header" data-theme="b">
            <a href="/m/inicio" data-icon="home" data-iconpos="notext" rel="external">Inicio</a>
            <h1>Ventas</h1>
        </div>
        <div data-role="content">
            <table>
                <tr>
                    <th>
                        No.
                    </th>
                    <th>
                        Auto
                    </th>
                    <th>
                        Cliente
                    </th>
                    <th>
                        Fecha
                    </th>
                    <th>
                    </th>
                </tr>
                <% foreach (var item in Model) { %>
                <tr>
                    <td>
                        <%: item.IdVenta %>
                    </td>
                    <td>
                        <%: item.Auto.NoSerie %>
                    </td>
                    <td>
                        <%: item.Cliente.Nombre + " "+ item.Cliente.Apellidos %>
                    </td>
                    <td>
                        <%: String.Format("{0:d}", item.Fecha) %>
                    </td>
                    <td>
                        <a href="/m/mventas/verventa/<%: item.IdVenta %>" data-role="button" data-icon="arrow-r" data-iconpos="notext" data-theme="c">Ver</a>
                    </td>
                </tr>
                <% } %>
            </table>
        </div>
        <div data-role="footer" data-theme="a">
            <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
        </div>
    </div>
</asp:Content>

Por lo que se comenta de la posición horizontal y vertical del móvil se refiere a esto:

Cuando se visualiza esta página con posición vertical en el móvil, no se puede ver todo el contenido.

Img. 1. Visualizando la app en el móvil.

Al cambiar la vista a horizontal, ahora si se ve todo el contenido.

Img. 3. Visualizando la app en el móvil.

La acción y vista VerVenta.

Lo que queremos ahora es que al seleccionar una venta, nos muestre sus detalles, para ello agregaremos la acción:

public ActionResult VerVenta(int id)
{
    //recuperar el objeto Venta
    var venta = BD.Venta.Where(v => v.IdVenta == id).FirstOrDefault();
    if (venta != null)
        //pasar a la vista el objeto venta
        return View(venta);
    else
        return View("Error");
}

Ahora creamos la vista VerVenta tipada de la clase Venta con contenido Details.

Img. 4. Agregar la vista VerVenta.

Estructuramos la vista para jQuery Mobile donde muestre los datos de la venta, del cliente y del auto.

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" Inherits="System.Web.Mvc.ViewPage<Agencia2012.Models.Venta>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Detalles de Venta
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div data-role="page" data-theme="c" id="verventa">
    <div data-role="header" data-theme="b">
        <a data-rel="back" data-icon="arrow-l">Regresar</a>
        <h1>Datos de Venta</h1>
    </div>
    <div data-role="content">
        <h2><% if (ViewData["MensajeVenta"] != null)
           { %>
           <%: ViewData["MensajeVenta"]%>
           <%}
           else
           { %>
        Datos de la venta
        <%} %></h2>
        <div id="divd-a" data-role="fieldcontain">
         <h3>Detalles.</h3>
            <div><strong>Número de venta:</strong> <%: Model.IdVenta %></div>

            <div><strong>Fecha de venta:</strong> <%: String.Format("{0:g}", Model.Fecha) %></div>

            <div><strong>Monto pagado:</strong> <%: String.Format("{0:c}", Model.Monto) %></div>
        </div>

        <div id="div1" data-role="fieldcontain">
         <h3>Datos del auto.</h3>
                 <div><strong>Número de Serie:</strong> <%: Model.Auto.NoSerie%></div>

                <div><strong>Marca:</strong> <%: Model.Auto.Marca.Nombre%></div>

                <div><strong>Modelo:</strong> <%: Model.Auto.Modelo%></div>

                <div><strong>Año:</strong> <%: Model.Auto.Anio%></div>

                <div><strong>Color:</strong> <%: Model.Auto.Color%></div>

                <div><strong>Precio:</strong> <%: String.Format("{0:c}", Model.Auto.Precio)%></div>
        </div>
        <div id="div2" data-role="fieldcontain">
         <h3>Datos del cliente.</h3>
                 <div><strong>Nombre:</strong> <%: Model.Cliente.Nombre %></div>

                <div><strong>Apellidos:</strong> <%: Model.Cliente.Apellidos%></div>

                <div><strong>Edad:</strong> <%: Model.Cliente.Edad%></div>

                <div><strong>Estado:</strong> <%: Model.Cliente.Localidad.Municipio.Estado.Nombre%></div>

                <div><strong>Municipio:</strong> <%: Model.Cliente.Localidad.Municipio.Nombre%></div>

                <div><strong>Localidad:</strong> <%: Model.Cliente.Localidad.Nombre%></div>

                <div><strong>Calle:</strong> <%: Model.Cliente.Calle%></div>

                <div><strong>Numero:</strong> <%: Model.Cliente.Numero%></div>

                <div><strong>Telefono:</strong> <%: Model.Cliente.Telefono%></div>

                <div><strong>Email:</strong> <a href="mailto:<%: Model.Cliente.Email %>"><%: Model.Cliente.Email%></a></div>
        </div>
        <a data-rel="back" data-role="button" data-theme="d">Aceptar</a>
      </div>
        <div data-role="footer" data-theme="a">
        <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
        </div>
   </div>
</asp:Content>

Ahora entonces al comprobar la funcionalidad seleccionando la venta registrada tenemos:

Img. 5. Vista VerVenta.

Acción y vista VenderAuto

Cuando estamos visualizando los datos de un auto y este no ha sido vendido, tenemos el botón “Vender este auto” cuya acción es esta.

Agregamos la acción VenderAuto (con respuesta GET y POST)

public ActionResult VenderAuto(int id)
{
    //Recuperar el objeto Auto con el ID
    var auto = BD.Auto.Where(a => a.IdAuto == id).FirstOrDefault();
    if (auto != null)
    {
        //Comprobar que no esté vendido
        if (!(bool)auto.Vendido)
        {
            //si no está vendido
            //pasar el objeto auto a la vista (se va a vender)
            ViewData["Auto"] = auto;
            //inicializar un objeto de la clase Venta
            //al que asignamos el ID del auto a vender y la fecha actual
            Venta venta = new Venta();
            venta.IdAuto = auto.IdAuto;
            venta.Fecha = DateTime.Now;
            return View(venta);
        }
        else
        {
            //Si ya está vendido, mostrar la vista AutoVendido (aún no se ha creado)
            //y pasarle el objeto auto vendido
            return View("AutoVendido", auto);
        }
    }
    else
        return View("Error");
}

[HttpPost]
public ActionResult VenderAuto(int id, Venta venta)
{
    //recuperar el objeto auto a vender
    var auto = BD.Auto.Where(a => a.IdAuto == id).FirstOrDefault();
    if (auto != null)
    {
        //si el auto no ha sido vendido
        if (!(bool)auto.Vendido)
        {
            if (ModelState.IsValid)
            {
                //registrar la venta
                BD.AddToVenta(venta);
                //actualizar el auto a vendido = si
                auto.Vendido = true;
                UpdateModel(auto);
                //guardar los cambios en la BD
                BD.SaveChanges();
                //pasar el mensaje a la vista VerVenta
                ViewData["MensajeVenta"] = "Venta Registrada";
                //mostrar la vista pasandole la venta registrada (objeto)
                return View("VerVenta", venta);
            }
            else
            {
                //si algún dato no es correcto
                //volver a la vista VenderAuto con los datos ingresados
                ViewData["Auto"] = auto;
                venta.IdAuto = auto.IdAuto;
                venta.Fecha = DateTime.Now;
                return View(venta);
            }
        }
        else
        {
            //si el auto ya ha sido vendido (no se ha registrado la venta)
            //mostrar la vista correspondiente
            return View("AutoVendido", auto);
        }
    }
    else
        return View("Error");
}

Ahora creamos la vista VenderAuto tipada de la clase Venta con contenido Create.

Img. 6. Crear la vista VenderAuto.

En esta vista haremos uso de los archivos JS y CSS de jQuery UI y nuestro script ya implementado para autocomplementar un campo.

Tarea: Por su cuenta implemente los scripts de validación como en Clientes y Autos o modifique los scripts para que sean genéricos y no tenga que estarse realizando uno por cada formulario.

En mi caso lo que hice fue cambiar los archivos: de autovars.js a  variablesg.js y de autovalidador.js a formvalidador.js (actualizando también los nombres donde estos se están enlazando (Editar y Nuevo auto)), el Id del form “datosauto” fue cambiado a “datosform” y de igual forma en la función jQuery que controlaba el evento submit para datosauto ya que el script utiliado para validad Auto solo valida input’s de tipo text a diferencia del script utilizado para validar Cliente (valida también combos y no funcionaría para auto ni vender auto).

La estructura de la vista VenderAuto con jQuery Mobile queda como:

<%@ Import Namespace="Agencia2012" %>
<%@ Import Namespace="Agencia2012.Models" %>
<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" Inherits="System.Web.Mvc.ViewPage<Agencia2012.Models.Venta>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% // aquí utilizamos el widget AutoComplete de jQuery UI %>
    <script src="../../../Content/Scripts/jquery.ui.autocomplete.js" type="text/javascript"></script>    
    <script src="../../../Content/Scripts/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script>
    <link href="../../../Content/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
    <% //utilizamos también los archivos de validación de auto %>
    <script src="../../../Areas/m/Content/scripts/variablesg.js" type="text/javascript"></script>
    <link href="../../../Areas/m/Content/css/estilos.css" rel="stylesheet" type="text/css" />
<!-- Inicio de la página -->
<div data-role="page" data-theme="c" id="venderauto">
<div data-role="header" data-theme="b" id="hdrMain" name="hdrMain" data-nobackbtn="true">
<a href="/m/mautos" data-icon="arrow-l" rel="external">Autos</a>
           <h1>Vender auto</h1>
        <a href="/m/mautos/nuevo" data-icon="plus" rel="external">Agregar</a>
</div>
<div data-role="content" id="contentMain" name="contentMain">
            <% if (ViewData["Auto"] != null)
                 {
                   //recuperar el objeto Auto recibido
                     Auto auto = (Auto)ViewData["Auto"];
                     %>        
                <form id="datosform" action="" method="post">    
                <div id="divd-a" data-role="fieldcontain">
                    <h3>Datos del auto.</h3>
                        <div><strong>Número de Serie:</strong> <%: auto.NoSerie%></div>
                        <div><strong>Marca:</strong> <%: auto.Marca.Nombre%></div>
                        <div><strong>Modelo:</strong> <%: auto.Modelo%></div>
                        <div><strong>Año:</strong> <%: auto.Anio%></div>
                        <div><strong>Color:</strong> <%: auto.Color%></div>
                        <div><strong>Precio:</strong> <%: String.Format("{0:c}", auto.Precio)%></div>
                        <div>
                            <%: Html.HiddenFor(model => model.IdAuto)%>
                        </div>
                </div>
                <div id="div1" data-role="fieldcontain">
                    <h3>Datos del cliente.</h3>
                        <div>
                        <label for="Nombre">*<strong>Nombre:</strong><br /></label>
                       <input id="NombreCliente" class="obligatorio">
                        </div>                       
                        <div><strong><i>Lugar de residencia</i></strong></div>
                        <div>
                            <strong>Calle:</strong> <span id="Calle"></span><br />
                            <strong>Número:</strong> <span id="Numero"></span><br />
                            <strong>Localidad:</strong> <span id="Localidad"></span><br />
                            <strong>Municipio:</strong> <span id="Municipio"></span><br />
                            <strong>Estado:</strong> <span id="Estado"></span><br />
                        </div>                        
                        <div>
                            <%: Html.HiddenFor(model => model.IdCliente)%>
                        </div>
                 </div>
                <div id="div2" data-role="fieldcontain">
                    <div id="Div4" data-role="fieldcontain">
                        <label for="Monto"> <strong>Fecha de venta</strong>:</label>
                        <input type="datetime" name="Fecha" id="Fecha" required="required" readonly="readonly" value="<%: String.Format("{0:g}", Model.Fecha) %>"/>
                    </div>
                    <div id="Div3" data-role="fieldcontain">
                        <label for="Monto">*<strong>Monto pagado por el cliente</strong>:</label>
                        <input type="number" name="Monto" id="Monto" min="10000" pattern="[0-9]*" required="required" />
                    </div>
                 </div>       
                    <p>
                        <button type="submit" data-theme="b" data-inline="true">Vender</button> <a data-rel="back" data-role="button" data-theme="d" data-inline="true">Cancelar</a>
                    </p>
           </form>
            <% }
                 else
                 {
                   //Si el objeto auto no pudo recuperarse                   
           %>
            <p>Error al cargar los datos del auto para registrar la venta.</p>
           <% } %>
</div><!-- Fin del contenído de la página -->
<div data-role="footer" data-theme="a">
                <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
     </div>
    <!-- Contenedor que utilizaremos como cuadro de diálogo al momento de validar -->
  <div align="center" data-role="content" id="contentDialog" name="contentDialog">
<div>Llene todos los campos marcados con * antes de enviar el formulario.</div>
<a id="buttonOK" name="buttonOK" href="#editarcliente" data-role="button" data-inline="true">OK</a>
</div> <!-- fin contenedor de Diálogo -->
     <!-- Enlazar al script de validación y autocomplementar para que pueda ser ejecutado correctamente -->
    <script src="../../../Areas/m/Content/scripts/formvalidador.js" type="text/javascript"></script>
    <!-- Este archivo ya va incluido en el proyecto, solo reviselo para analizar su funcionamiento-->
    <script src="../../../Content/Scripts/cliente.js" type="text/javascript"></script>
</div>  <!-- fin de toda la página jQuery M -->
</asp:Content>

Ya para terminar con las vistas, solo falta agregar la vista AutoVendido, la cual será mostrada cuando se intente editar un auto ya vendido o tratar de vender un auto ya vendido (ingresando por url a vender auto / id del auto).

Entonces en la carpeta de las vistas del controlador mVentas, agregar la vista AutoVendido, tipada de la clase Auto con contenido Details.

Img. 7. Agregando vista AutoVendido.

La estructura de esta página con jQuery Mobile mostrará los datos de la venta, del auto y los datos del cliente.

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" Inherits="System.Web.Mvc.ViewPage<Agencia2012.Models.Auto>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
AutoVendido
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <div data-role="page" data-theme="c" id="autovendido">
        <div data-role="header" data-theme="b">
            <a data-rel="back" data-icon="arrow-l">Regresar</a>
            <h1>Auto vendido</h1>        
        </div>
        <div data-role="content">
            <h2>Este auto ya ha sido vendido.</h2>
            <div id="divd-a" data-role="fieldcontain">
                <h3>Detalles.</h3>
                 <div><strong>Número de venta:</strong> <%: Model.Venta.FirstOrDefault().IdVenta %></div>
                <div><strong>Fecha de venta:</strong> <%: String.Format("{0:g}", Model.Venta.FirstOrDefault().Fecha)%></div>        
                <div><strong>Monto pagado:</strong> <%: String.Format("{0:c}", Model.Venta.FirstOrDefault().Monto)%></div>
            </div>
            <div id="div1" data-role="fieldcontain">
                <h3>Datos del auto.</h3>
                <div><strong>Número de Serie:</strong> <%: Model.NoSerie%></div>
                <div><strong>Marca:</strong> <%: Model.Marca.Nombre%></div>
                <div><strong>Modelo:</strong> <%: Model.Modelo%></div>
                <div><strong>Año:</strong> <%: Model.Anio%></div>
                <div><strong>Color:</strong> <%: Model.Color%></div>
                <div><strong>Precio:</strong> <%: String.Format("{0:c}", Model.Precio)%></div>
            </div>
            <div id="div2" data-role="fieldcontain">
                <h3>Datos del cliente.</h3>
                <div><strong>Nombre:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Nombre%></div>
                <div><strong>Apellidos:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Apellidos%></div>
                <div><strong>Edad:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Edad%></div>
                <div><strong>Estado:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Localidad.Municipio.Estado.Nombre%></div>
                <div><strong>Municipio:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Localidad.Municipio.Nombre%></div>
                <div><strong>Localidad:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Localidad.Nombre%></div>
                <div><strong>Calle:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Calle%></div>
                <div><strong>Numero:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Numero%></div>
                <div><strong>Telefono:</strong> <%: Model.Venta.FirstOrDefault().Cliente.Telefono%></div>
                <div><strong>Email:</strong> <a href="mailto:<%: Model.Venta.FirstOrDefault().Cliente.Email %>"><%: Model.Venta.FirstOrDefault().Cliente.Email%></a></div>        
            </div>
        </div>
        <a data-rel="back" data-role="button" data-theme="d">Aceptar</a>
        <div data-role="footer" data-theme="a">
            <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
        </div>
    </div>
</asp:Content>

Comprobando funcionalidad.

Ingresar a Autos, seleccionar un auto No vendido, obtendremos la vista VenderAuto.

Img. 8. Vender Auto.

La validación funciona y autocomplementar también funciona, como ya se tienen registrados los clientes, solo basta empezar a escribir el nombre del cliente y seleccionar el correcto de la lista de sugerencia, seleccionado el cliente, sus datos son cargados con jQuery + JSON (función realizada por el script Proyecto/Content/scripts/cliente.js)

Img. 9. Datos del cliente cargados en vender auto.

Al registrar la venta, nos redirecciona a ver los datos registrados.

Img. 10. Venta registrada.

Ahora si intentaramos editar un auto ya vendido, obtendremos la vista AutoVendido.

Img. 11. Auto vendido.

Y bién, después de un largo trabajo y esfuerzo para realizar este pequeño demo, hemos terminado.

Ya por último para visualizar correctamente la app en el móvil y evitar que la escala pueda ser modificada, basta con agregar la siguiente etiqueta a Movil.Master:

<meta name="viewport" content="user-scalable=yes, width=device-width, initial-scale=1.0, maximun-scale=1.0, minimun-scale=1.0" />

¡Se acabó…!

Escribe tu comentario:

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s