• No se han encontrado resultados

DUSA - Sistema de Manejo y Control de

N/A
N/A
Protected

Academic year: 2022

Share "DUSA - Sistema de Manejo y Control de"

Copied!
28
0
0

Texto completo

(1)

DUSA - Sistema de Manejo y Control de Repartos

Documentación Técnica Versión 1.7

Historia de revisiones

Fecha Versión Descripción Autor

11/11/2015 1.0 Creación del documento Mauro Picó

12/11/2015 1.1 Modificaciones Rodrigo Eizmendi

14/11/2015 1.2 Modificaciones Matias Lugli 14/11/2015 1.2 Capa de persistencia Helen Olhausen 15/11/2015 1.3 Agregado de descripción de

operaciones.

Camila Moscatelli

15/11/2015 1.4 Agregado de descripción de operaciones.

Camila Rosso

15/11/2015 1.5 Agregado de descripción de operaciones.

Santiago Noguera

15/11/2015 1.6 Continuación capa de persistencia

Helen Olhausen

21/11/2015 1.7 Se revisa toda el documento buscando y arreglando errores.

Mauro Picó

 

   

(2)

Índice

Introducción………

……… 3 1.0-

Persistencia………

……… 5 2.0-

WebServices………

……… 14 3.0-

AsyncTasks………

……… 20 4.0- Business

………

……… 23 5.0- DUSA

………

……… 27

(3)

INTRODUCCIÓN

La idea de este documento es dar un marco de las clases y métodos más relevantes para  facilitar el entendimiento y potencial modificación de la implementación realizada. 

 

El proyecto fue realizado utilizando el IDE Android Studio 1.3.2, utilizando el jdk1.7.0_65 de  Java.  

 

El proyecto contiene la siguiente estructura, de la cual destacaremos los paquetes más  relevantes para detallarlos en este documento. El grado de detalle con los que explicaremos  cada componente, dependerá de la criticidad y complejidad de los mismos. 

 

El esquema general de la distribución de la aplicación se muestra en la siguiente imagen. 

 

   

De los recursos listados detallaremos en particular los siguientes paquetes: asyncTasks,  Bussiness, dusa, Persistence, webservices. 

 

asyncTasks:​ Contienen las llamadas asíncronas a procesos que utilicen conexiones  remotas, debido a que Android no permite la ejecución de métodos que utilicen conexiones  con internet en el main thread de la aplicación. 

(4)

 

Business:​ ​Contiene la lógica de negocio. 

 

Dusa:​ Contiene las clases Java que se asocian a los xml de UI. 

 

Persistence:​ Contiene los accesos a la base de datos. 

 

Webservices: ​Contiene la interfaz de conexión con el servidor DUSA, así como la lógica de  los web methods. 

               

   

(5)

1.0 Persistencia

 

Para poder gestionar nuestros datos a nivel de base de datos, Android, pone a nuestra  disposición la base de datos relacional SQLite así como una serie de clases mediante las  cuáles podremos crear el esquema de la base de datos, como los métodos para poder  seleccionar registros, insertar, eliminar o actualizar. 

 

Una vez conocidas estas clases, la gestión del SQLite es fácil, sin embargo, encierra una  gran cantidad de código "repetitivo". 

 

GreenDao es un ORM en el que se pretende conservar la simplicidad proporcionando una  interfaz orientada a objetos para el manejo de la base de datos SQLite. 

Es una API sencilla, que está optimizada para Android, permitiendo agilizar tareas comunes  de Base de Datos y es utilizada en importantes aplicaciones de Android. 

 

   

Por lo tanto GreenDao se eligió para la gestión de base de datos de la aplicación. 

Se programó por un lado el modelo de la base de datos y se indicó en qué proyecto  queríamos que nos generarán las clases dao (data access objects) y las entidades (java  data objects), este proyecto es el proyecto dusa. 

 

En resumen, GreenDAO, genera clases java, una clase entity y otra clase dao, por cada  tabla que tengamos en nuestra base de datos y todas las columnas de estas serán 

properties, además de todos los métodos necesarios para poder ejecutar cualquier acción  CRUD (Create Read Update Delete). 

 

 

 

(6)

1.1 Implementando GreenDAO

NOTA: A continuación se explica cómo comenzar a utilizar GreenDao en una aplicación  Android desde cero. El modelo se creó previamente por el equipo técnico, ya listo para su  uso. 

 

Para usar GreenDao en una aplicación Android, es necesario en primer lugar crear las  entidades y los objetos dao. Para ello, es necesario crear un segundo “proyecto generador” 

(proyecto Java, no Android) ,aparte del proyecto Android `​dusa​`, donde se generen todas  las clases necesarias para nuestro modelo.  

Para ésto basta con crear un módulo `​DusaDaoGenerator`​, que se encargará de generar las  entidades, los objetos DAOs y el acceso a la Base de Datos SQLite de Android.  

 

Por lo tanto se realizó un proyecto ​Java​ paralelo al de Android, donde se implementaron las  entidades y los objetos DAO de dichas entidades y se crearon los componentes de manera  automática.  

El archivo mas importante de éste modulo es el DusaDaoGenerator.java el cual es el  archivo generador. 

 

De modo que la estructura de proyecto quedó de la siguiente manera, por un lado el módulo  DusaDaoGenerator y por otro el módulo Android. 

 

   

Para poder usar la clase generadora DusaDaoGenerator.java primero​ se ​importaron  las librerías necesarias para el uso de GreenDAO​.  

Como se indica en el github de greenDao 

https://github.com/greenrobot/greenDAO#add­greendao­to­your­project 

En el archivo ​build.gradle​ de proyecto Android ​‘app’​ y del generador ‘​DusaDaoGenerator’​ se  agregaron en ​dependencies​ las líneas: 

Gradle dependency para Android app: 

compile ('de.greenrobot:greendao:2.0.0') 

(7)

Gradle dependency para el Java generator project: 

   compile ('de.greenrobot:greendao­generator:2.0.0') 

Después de agregar cada uno de las líneas es necesario hacer un ​Sync​ de gradle. 

1.2 Detalle de DusaDaoGenerator.java  

DusaDaoGenerator.java ​es donde se crea el esquema de la base de datos, va a ser la clase  encargada de crear las clases dao y las entidades. 

 

Por lo tanto en ​DusaDaoGenerator​, se define un método estático ​main()​ donde se crea el  esquema mediante la inicialización del objeto Schema. Este objeto va a ser el encargado de  almacenar todas nuestras entidades con sus características. El constructor tiene dos 

parámetros, ​el número de versión y el paquete​. ​Recordemos​ que cada cambio que  realicemos en el modelo de la base de datos, tiene que verse reflejado, en ​el aumento del  número de versión​, para que Android, sepa que tiene que realizar un upgrade de la base  de datos. El ​paquete que indicamos es donde va a crear las entidades y los daos. 

 

Ejemplo: 

publicstaticvoid main(String args[])throwsException { 

Schema schema =newSchema(1, “com.dusa.pis.srcgen.model”); 

Entity chofer = addChoferTo(schema); // agregar alguna entidad 

newDaoGenerator().generateAll(schema,"../app/src/main/java/"); 

}   

Posteriormente, a este Schema se le pueden agregar entidades. 

Por ejemplo de la siguiente manera 

privatestaticEntity addChoferTo(Schema schema) {

Entity chofer = schema.addEntity(CHOFER_TABLE_NAME);

chofer.addIdProperty();

chofer.addStringProperty("usuario").unique();

chofer.addStringProperty("password").notNull();

return chofer;

}

 

Cada entidad está mapeada a una tabla de la base de datos y tiene properties, que se  mapean a las columnas de la tabla. 

El nombre de la tabla lo asignamos mediante el método ​addEntity​ del ​Schema​. Después se  tiene que indicar todas las properties de la entidad. Aunque no es obligatorio, pero sí es una  buena costumbre, se agrega un campo id, que SQLite identifica como clave y es de tipo  Long y auto­generado. Para ello se dispone del método​ addIdProperty()​. 

Para cada uno de los tipos de datos que admite SQLite también tenemos sus métodos  como ​addLongProperty(“”), addStringProperty(“”), addBooleanProperty(“”), etc​. Además 

(8)

podemos indicar si un campo va a ser clave ​unique()​ o no debe ser nulo ​notNull()​. Todo ésto  se puede encontra en la documentacion de greenDao ​http://greendao­orm.com/​ . 

1.3 Relaciones en greenDao

Para la creación de relaciones, tenemos que obtener la property que va a tener la  foreignKey de la relación. Las relaciones se deben crear bidirecionales. 

Por ejemplo un chofer tiene varios repartos y un reparto tiene un único chofer   

// foreign key en reparto con idChofer

addToManyToOneRelationship(chofer, reparto,"repartos","idChofer"); 

 

privatestaticvoid addToManyToOneRelationship(Entity source,Entity destination, String relationshipName,String sourceFKName)

Es un método de utilidad que se creó en DusaDaoGenerator mediante el cual se genera  una relación entre las entidades que se pasan como parámetros, siendo las relaciones entre  estas las siguientes:  

 

● `source` has Many `destination` 

● `destination` has One `source` 

●  `sourceFKName`: es el nombre de la columna que tiene la foreignKey en la tabla 

`destination` 

 

Obteniéndose la relación, la tabla destination tiene una columna con la una foreign key de la  tabla source, teniendo ahora un método de utilidad creado por greendao donde se puede  llamar a source.get"​relationshipName"() que nos retorna una lista de tipo `destination`. 

 

GreenDAO no soporta directamente las relaciones N:M. Para manejar este tipo de 

relaciones se debe crear una nueva entidad que represente una tabla de join, que contiene  la clave de la tabla primaria con la clave de la tabla foránea. En nuestro caso se creó la  tabla de join para el repartoFarmacia, en DusaDaoGenerator se creó el método de utilidad  que realiza esto ​addJoinTableRepartoFarmacia(schema, reparto, farmacia) 

 

Una vez que se creó toda la estructura de la base de datos se ​ejecuta la clase Java   Para correr ​DusaDaoGenerator.java: 

 

● Ir a la sección de ‘Gradle task’  

● Elegir DusaDaoGenerator 

● Seleccionar ​run. 

(9)

   

Esto comienza a generar las clases dao y las clases entity. De esta manera todo el modelo  de datos de la aplicación se crea.  

 

Observaciones importantes:

­ Cuando se modifique el archivo ​DusaDaoGenerator.java​, ya sea para agregar una  nueva entidad al esquema o para agregar una propiedad a alguna de las ya  existente, se debe ejecutar la task ​`run`​ dentro de ​Tasks de 

Gradle­>application­>run​, para que vuelva a regenerar cada una de las entidades,  como se mostró arriba. 

 

­ Las clases generadas por GreenDao no deberían modificarse, ésto se debe hacer  desde el archivo generador​ `DusaDaoGenerator.java` 

1.4 Uso del generador desde proyecto Android

Se creó la clase ​DBUtil.java ​que tiene metodos de útilidad donde se crea la instancia de la  base de datos y se obtienen los “daos” de cada entidad. 

   

Consultas  

Una de las acciones más habituales cuando trabajamos con bases de datos son las  consultas. GreenDAO tiene dos métodos para generar estas consultas SQL que cubren  cualquier necesidad de consulta que se tenga. 

Con ​queryRaw(String where, String ... selectionArg)​ pasaremos directamente la consulta  SQL entera. No es la opción recomendada, pues vamos a tener que trabajar directamente 

(10)

con código SQL que o bien hemos probado en un analizador sintáctico o podemos cometer  errores. ​Sin embargo, nos permite cubrir opciones que no va a cubrir el método  queryBuilder() como joins entre tablas. 

 

queryBuilder nos devuelve un objeto QueryBuilder que es el que permite crear las consultas  sin necesidad de utilizar sql. 

 

Mediante el método where indicamos el campo de consulta. Añadiremos tantos métodos  where como campos formen parte de nuestra consulta. 

 

Una vez creado el modelo de la aplicación, se procedió a crear la interfaz de persistencia,  con la cual la capa lógica de la aplicación se comunicará con la capa de persistencia. 

1.5 Interfaz de persistencia - IPersistance  

La interfaz de persistencia ​IPersistance.java​ provee todos los métodos especificados en la  arquitectura utilizando, el modelo de datos anteriormente generado por el archivo generador  DusaDaoGenerator.java​ de GreenDao. Permitiendo así la comunicación de la capa lógica  con la capa de persistencia. De esta forma la aplicación se mantiene modularizada, no  acoplada y escalable.  

El modelo de la base datos se puede ver en el documento de arquitectura ​Documento de  modelo de diseño v1.4. 

 

A continuación se brinda una documentación de los métodos más relevantes de 

IPersistance.java ​siendo ​Persistance.java ​la clase que brinda la implementacion de cada  uno de ellos. Para la implementación de éstos, se utilizaron métodos de utilidad que  proporciona greenDao para el acceso a la base de datos. 

 

Métodos Create/Insert/Update   

EstadoBusiness setEstadoBusiness(Long idReparto)

EstadoBusiness​ es la entidad que conserva el estado de la aplicación en 

determinado momento. Este método crea, setea y retorna el estado de la capa lógica en un  determinado momento que se basa únicamente en el ​idReparto.

Reparto crearReparto(DataProximoReparto reparto,String choferUsername)

Crea e inserta en la base de datos un reparto a partir del dataType 

DataProximoReparto  y lo asigna al chofer con nombre de usuario ​choferUsername 

RepartoFarmacia crearFarmaciaReparto(DataFciaReparto farmaciaReparto,Long idReparto, Integer posicionOrden)

Crea e inserta en la base de datos la entidad FarmaciaReparto (tabla de join  anteriormente explicada para un reparto y una farmacia) utilizando el dataType 

DataFciaReparto​ que representa la farmacia y el reparto ​idReparto​. ​posicionOrden  representa la posición en que se va a visitar la farmacia para el orden del reparto

(11)

Chofer crearChofer(String usuario,Stringpass)

Crea e inserta en la base de datos la entidad Chofer con el nombre de usuario  usuario​ y la contraseña ​pass

Farmacia crearFarmacia(Integer nroCliente,String nombre)

Crea, inserta o actualiza la Farmacia con numero de cliente ​nroCliente​ y ​nombre

Notificacion crearNotificacion(Integer nroCliente,Long idReparto,String notificacionData)

Crea  y asigna una notificación para la pareja repartoFarmacia con ​nroCliente e 

idReparto y los datos ​notificacionData

 

Métodos Getters

publicDate getEstadoBusinessFechaBackup()

EstadoBusiness ​a su vez conserva la fecha del último backup que se hizo del  archivo sqlite de la base de datos. Con éste método se obtiene dicha fecha 

 

publicArrayList<DataFarmacia> getFarmaciasReparto(Long idReparto)

Retorna la lista de Farmacias del reparto con id ​idReparto, representadas por  DataFarmacia (dataType que representa una farmacia) 

 

publicList<Reparto> getRepartosChofer(String user)

Retorna la lista de repartos del chofer con username ​user

List<String> getCodigosNoEntregados(Integer nroCliente,Long idReparto)

Retorna la ​List​<​String​>​ de código de barras que no se escanearon en la visita a  una determinada farmacia con número de cliente ​nroCliente para el reparto con ​idReparto.

Retorna las notificaciones que tienen 'codBarraNoEntregado:'  

 

Integer getProximaFarmacia(Long idReparto)

Retorna el número de cliente ​Integer ​de​​la próxima farmacia a visitar para el  reparto con ​idReparto ​de la ruta establecida 

   

String getCodigoBarrasReparto(Long idReparto)

Retorna el código de barras que identifica al reparto con ​idReparto

List<DataNotificacionesPendientesFarmacia>

getNotificacionesPendientes(Long idReparto)

Retorna las notificaciones pendientes que quedan para enviar del reparto con 

idReparto. Éste método se utiliza en caso de pérdida de conexión para el envío posterior de  datos.

List<DataOrden> getOrden(Long idReparto)

Retorna el orden de farmacias a visitar para el reparto con ​idReparto.

(12)

List<DataSitioVisitadoPendiente> getSitiosVisitadosPendientes(Long idReparto)

Retorna la lista de sitios que se visitaron para el reparto con id ​idReparto ​pero por  perdida de conexión, no se pudo enviar al servidor los datos.

String getEstadoRepartoActual(Long idReparto)

Retorna estado del reparto con id ​idReparto​ del enumerado​ EnumEstado { ABIERTO, CERRADO, INICIADO, TERMINADO, COMPLETO, INGRESANDOMONTO }

publicboolean getPendiente(Long idReparto)

Retorna true si al menos una de las siguientes variables en el reparto con  idReparto​ son true  

1-isNotifiacionesPendiente 2-isSitioVisitadoPendiente 3-isOrdenPendiente

Otros métodos de utilidad

boolean isFarmaciaVisitada(Long idReparto,Integer nroCliente)

​Retorna true si la farmacia con ​nroCliente para el reparto con ​idReparto ya fue visitada. 

Get de la variable is Visitada en la tabla orden para la faramacia ​nroCliente y el reparto con  id ​idReparto 

Boolean isRepartoTerminado()

Retorna true si el reparto con idReparto que se encuentra apuntado por el  EstadoBusiness es un reparto con estado ​“COMPLETO”

Boolean isUserLoggedIn()

Retorna true si existe un chofer en la base de datos, este chofer en todo momento  es unico, y es el usuario logueado 

 

void cerrarSesion()

Hace un delete del chofer que se encuentra como único usuario de la aplicación   

 

Ejemplos

  

A continuación brindamos un ejemplo de cómo acceder, obtener un atributo, y modificar  un  elemento (en este caso un reparto) de nuestra base de datos a partir del identificador 

"idReparto" utilizando métodos de GreenDao: 

 

1­ Obtenemos el objeto reparto. 

Reparto reparto =DBUtil.getRepartoDao().queryBuilder()

.where(RepartoDao.Properties.Id.eq(idReparto)).unique(); 

 

2­ Obtenemos una propiedad ( columna ) del objeto reparto, en este caso el monto total  cobrado. 

reparto.getMontoTotalCobrado(); 

 

(13)

3 ­ Modificamos dicha propiedad. 

reparto.setMontoTotalCobrado(monto); 

reparto.update(); 

   

Con esto resumimos la documentación de la capa de persistencia de la aplicación. Se  recomienda siempre tomar como referencia la documentación de greenDao 

http://greendao­orm.com/, y la documentación del código de la aplicación. 

                                                                           

(14)

2.0 Paquete Webservices:

 

Conceptos Generales 

Los servicios fueron realizados utilizando la librería “retrofit” asistido mediante las librerías  de “okHttp” para la configuración de los parámetros de conexión, “gson” de Google para el  manejo de los JSON y “GlassFish” para las notaciones correspondientes. 

 

La siguiente imágen muestra las dependencias en el build.gradle de la app. 

 

   

El paquete de servicios está dividido en los siguientes sub­paquetes de acuerdo a la  imagen: 

 

   

El paquete de “RemoteTypes” contiene aquellas clases “POJO” (​Plain Old Java Object)​,  estos son los objetos que con el uso de Retrofit y gson, son populadas mediante la traza de  las respuestas JSON de los servicios. 

 

Así, el paquete “ClasesServices” contiene clases que serán devueltas a la capa lógica  conteniendo la información de llamado a servicios. Estas clases se crean y se construyen  utilizando los datos de las clases descritas en el párrafo anterior, “RemoteTypes”. Si bien,  en algunos casos el contenido y estructura de las clases pertenecientes a “RemoteTypes” y 

“ClasesServices” es prácticamente idéntico, esto se realiza para mantener aún más la  brecha de separación entre la interfaz de conexión y la lógica.  

 

En particular, la clase que es llamada desde la lógica es la clase “WebServices” (singleton)  que implementa los métodos de su interfaz “IWebServices”. La clase “Conector”, contiene la  interfaz de conexión. Es decir, en última instancia, cuando se consulte un servicio se hará  mediante la clase “Conector”, es en esta, donde se definen los métodos, atributos y las  propiedades, generales de los métodos (como puede ser “GET” o “POST”) o particulares de  los atributos de los métodos (como puede ser si son parámetros “QUERY”). 

 

La siguiente imagen muestra parte de la definición de esta clase: 

 

@GET("/estadoReparto"

(15)

ResultGetEstadoReparto estadoReparto(@Query("fecha") String fecha,@Query("reparto" int reparto,@Query("nroSalida"int nroSalida); 

 

@POST("/iniciarReparto"

MensajeError iniciarReparto(@Query("fecha") String fecha, @Query("reparto"int  reparto,@Query("nroSalida"int nroSalida, @Body Object dummuy); 

   

@GET y @POST determina el tipo request, @Query determina el tipo de parámetro, 

@Body es el body requerido por POST. 

 

Dentro de la clase “WebServices” se mantiene, luego de iniciada la sesión, un objeto de la  clase “Conector”. Este conector contiene las propiedades de conexión (así como la URL y el  tipo de autenticación), este conector es creado por un método, llamado ​CreateService   

Los métodos definidos en la clase “IWebService” son trazados uno a uno con los métodos  del conector, esto para simplificar la distribución de la lógica. 

 

El atributo de la clase “WebService”, llamado ​BASE_URL​ define la url base a la cual se  realizará la conexión. Por otro lado, el atributo ​apiServicios ​mantendrá la conexión  realizada el “Conector” creado que se describió en los párrafos anteriores. Éste, será  utilizado por todos los restantes métodos para llamar a los métodos remotos. 

 

Dentro de cada método definido en la clase “WebServices”, además de realizar las 

consultas a los respectivos métodos remotos (mediante el conector), se realiza la gestión de  los posibles errores. Sobre esto, atendemos particularmente los errores que pueden ser  ocurridos durante el proceso de inicio de sesión en el método ​iniciarSesion​, debiendo  distinguir principalmente aquellos errores ocasionados por problemas de autenticación. Esto  se realiza atendiendo al catch de la excepción, verificando el mensaje del error, y 

devolviendo los tipos de mensaje indicados a la lógica.  

 

Simplemente a modo de ejemplo, se deja en la siguiente porción de código, el manejo de  error del método ​iniciarSesion. 

 

if (e.getLocalizedMessage()!=null && e.getLocalizedMessage().equals("401  Unauthorized")) 

   return new MensajeError(­1,"Nombre Usuario o password no son validos"); 

return new MensajeError(404,"No hay conectividad con los servicios"); 

   

Verifica los tiempos de mensajes, distinguiendo aquél que sea generado debido a un error  de autenticación. 

 

El manejo es similar en el resto de los métodos, atendiendo únicamente a los errores del  estilo 404, debido a la no conectividad con los servicios remotos, ya sea por la pérdida de  conexión a internet del dispositivo de la aplicación, o por una baja en los servicios remotos. 

(16)

 

Aquellas respuestas, que están compuestas por varios objetos dentro del objeto de llamado  a los servicios, (como es el ejemplo del método ​obtenerProximoReparto 

que devuelve un objeto de la clase ​ResultGetProximoReparto ​que a su vez está  formado por un objeto del tipo mensaje y un objeto del tipo ​DataProximoReparto​), lo  primero que se verifica es el estado de la respuesta, en caso de que el código sea distinto a 

“­1” (código que implica la ocurrencia de un error), se armarán los objetos de respuesta, que  serán devueltos a la capa lógica. En caso contrario, se armará y enviará el mensaje de error  únicamente, siendo responsabilidad de la lógica distinguir entre posibles escenarios. 

 

Desde la clase bussines, se acceden a los métodos provistos por la clase “WebServices”,  los mismos se detallan a continuación: 

 

Create Service 

Mirando más en detalle este método vemos que contiene la parametrización del timeout de  la conexión, mediante las líneas: 

 

final OkHttpClient okHttpClient = new OkHttpClient(); 

okHttpClient.setReadTimeout(10, TimeUnit.SECONDS); 

okHttpClient.setConnectTimeout(10, TimeUnit.SECONDS); 

 

Con ese cliente http, se realiza la construcción del adaptador rest de “Retrofit”: 

 

RestAdapter.Builder builder = new RestAdapter.Builder()         .setEndpoint(BASE_URL

       .setClient(new OkClient(okHttpClient)); 

 

Para la configuración del Basic Authentication se utiliza un interceptor de retrofit de la  siguiente manera: 

 

final String credentials = user + ":" + password; 

 

builder.setRequestInterceptor(new RequestInterceptor() {     @Override 

   public void intercept(RequestFacade request) { 

       String string = "Basic " + Base64.encodeToString(credentials.getBytes(),  Base64.NO_WRAP); 

       request.addHeader("Accept","application/json"); 

       request.addHeader("Authorization", string); 

   }  }); 

 

Donde se configura el string de “usuario:contraseña”, se especifica que se utilizará “Basic” 

como tipo de autenticación, se concatenan las credenciales en Base 64, se agregan a la  cabeceras lo anterior definido, así como el tipo de respuesta a aceptar (application/json). 

     

(17)

Las siguientes líneas dan la construcción el conector que finalmente el método devolverá y  quedará seteado en el atributo apiServicios de la clase WebServices: 

 

RestAdapter restAdapter = builder.build(); 

Conector apiService = 

       restAdapter.create(Conector.class); 

 

 

Iniciar Sesión 

public MensajeError iniciarSesion(String usuario,String password) 

 

Este método realiza la creación del conector y deja el mismo como atributo de la clase de la  siguiente manera: 

 

this.apiServicios CreateService(usuario,password); 

 

Siendo apiServicios un atributo del tipo Conector. 

 

El llamado al método remoto es mediante la siguiente linea: 

 

com.dusa.pis.webservices.RemoteTypes.MensajeError error =    

this.apiServicios.iniciarSesion(usuario, password); 

 

Esto es análogo en el resto de los métodos de la clase webservices, una vez que fue  ejecutado el iniciarSesión, el resto de los métodos ya estarán en condiciones de acceder a  los servicios remotos. 

 

Obtener Próximo Reparto 

public ResultGetProximoReparto obtenerProximoReparto(String usuario) 

 

La complejidad de este método radica en la gran cantidad de atributos y clases de la clase  ResultGetProximoReparto. Así, el llamado a servicios remotos se utiliza de la misma  manera que el método anterior: 

 

com.dusa.pis.webservices.RemoteTypes.ResultGetProximoReparto reparto =   this.apiServicios.obtenerProximoReparto(usuario); 

 

Obtener Datos Farmacia Reparto: 

public ResultGetFciaReparto obtenerDatosFciaReparto(int nroCliente, String fecha,  int reparto, int nroSalida) 

 

Nuevamente la complejidad de este método, radica en la construcción de las estructuras de  datos a ser devueltas a la capa lógica. 

 

Carece de sentido seguir especificando las llamadas concretas a los servicios remotos, las  mismas se encuentran en el código y además, son de alta similitud a las expuestas en  párrafos anteriores. 

(18)

 

Reordenar lista de farmacias no visitadas 

public MensajeError reordenarListaFarmaciasNoVisitadas(String fecha, int reparto,  int nroSalida, List<Integer> listaReordenada){ 

 

Este es el primer método POST que describimos, basta mirar en el código del Conector  para distinguir el atributo ​objeto 

 

@POST("/reordenarListaFarmaciasNoVisitadas" MensajeError reordenarListaFarmaciasNoVisitadas( 

@Query("fecha") String fecha, 

@Query("reparto"int reparto, 

@Query("nroSalida"int nroSalida, 

@Query("listaReordenada") List<Integer> listaReordenada, 

@Body Object objeto  ); 

 

El mismo está etiquetado con la notación “@Body”, que implica que objeto contendrá el  cuerpo del llamado post. En nuestro caso, pasamos como parámetros de la llamada los  datos necesarios. Así que para respetar el uso de la librería, antes de la llamada a los  métodos remotos (nuevamente en el método “obtenerListaFarmaciasNoVisitadas” de la  clase WebServices”), se crea un “Objeto Dummy” genérico de la siguiente manera, y el  mismo se envía en el POST del método: 

 

Object objeto = new Object(); 

com.dusa.pis.webservices.RemoteTypes.MensajeError mensaje =   this.apiServicios.reordenarListaFarmaciasNoVisitadas( 

fecha,  reparto,   nroSalida,   listaReordenada,   objeto 

); 

 

     

Agregar sitio visitado 

public MensajeError agregarSitioVisitado(String descripcion, String fechaHora, int  reparto, int nroSalida) 

 

Este método es del tipo POST, para más información sobre el comportamiento del llamado,  referirse a la especificación del método “​Reordenar lista de farmacias no visitadas” 

 

Procesar farmacia 

public MensajeError procesarFarmacia(String fecha, int reparto, int nroSalida, int  nroCliente,List <String> listaNotificaciones, String horaProcesada) 

   

(19)

Este método es del tipo POST, para más información sobre el comportamiento del llamado,  referirse a la especificación del método “​Reordenar lista de farmacias no visitadas” 

 

Is reparto terminado 

public ResultIsRepartoTerminado isRepartoTerminado(String fecha, int reparto, int  nroSalida) 

 

En una primera instancia de implementación que utilizó este método, la misma estaba  pensada para recibir como respuesta parámetros booleanos que indicaran si el reparto  estaba en estado “Terminado”. A medida que se avanzó con el proyecto se vió la necesidad  de cambiar el método web por uno que devolviera el estado en el cual se encuentra el  reparto, lo que llevó a la modificación de este método y creación del método 

“estadoReparto” en el Conector. Así mismo, el nombre IsRepartoTerminado del método de  la clase “WebServices” se dejó de igual manera, debido a que ya se encontraban 

implementaciones de lógica, que usaban este método. 

 

Es por esa razón que el llamado a los servicios de este método se realiza según la siguiente  linea: 

 

com.dusa.pis.webservices.RemoteTypes.ResultGetEstadoReparto er =   this.apiServicios.estadoReparto(fecha, reparto, nroSalida); 

 

Terminar Reparto 

public MensajeError terminarReparto(String fecha, int reparto, int  nroSalida,BigDecimal totEfectivo) 

 

Este método es del tipo POST, para más información sobre el comportamiento del llamado,  referirse a la especificación del método “​Reordenar lista de farmacias no visitadas” 

 

Iniciar reparto 

public MensajeError iniciarReparto(String fecha,int reparto, int  nroSalida) 

 

Este método es del tipo POST, para más información sobre el comportamiento del llamado,  referirse a la especificación del método “​Reordenar lista de farmacias no visitadas” 

                       

(20)

     

3.0 Paquete asyncTasks

 

Contiene las clases: 

   

Las clases anteriormente listadas extienden la clase AsyncTask, la necesidad de usar estas  clases está dada por el hecho de que Android no permite la ejecución de métodos que  utilicen conexiones con internet en el main thread de la aplicación. Se decidió usar esta y no  Threads directamente ya que facilita el trabajo y permite una mayor optimización de los  recursos. 

 

Al extender la clase es necesario sobreescribir  e implementar ciertos métodos, estos son  en el caso de esta aplicación: doInBackground() y onPostExecute(). Además se implementa  un método constructor donde se inicializan los atributos de la clase necesarios para realizar  la tarea asincrónica. 

 

En todos los casos en el método doInBackground() se realizan las llamadas a los métodos  pertenecientes al paquete Business que a su vez llaman a los métodos contenidos en el  paquete ​Webservices ​donde finalmente se realiza la llamada al webservice 

correspondiente. 

 

Cuando se termina de procesar el llamado al webservice el flujo continúa en el método  onPostExecute() donde se impactan los resultados en la UI mediante algún método definido  en la Activity correspondiente o en el caso de no ser necesario reflejar los resultados en la  UI se realizan los cambios necesarios en la lógica.  

 

El siguiente diagrama explica el flujo completo de la clase AsyncTask:  

 

(21)

   

A continuación se explica brevemente cada una de las clases y en los casos pertinentes se  detallan los métodos doInBackground y onPostExecute. 

 

3.1 ActualizarRepartoTask  

doInBackground: Aquí se llama al método ​actualizarReparto() presente en la Business, el  mismo obtiene el reparto nuevamente llamando a uno de los webservices y  posteriormente  hace los cambios pertinentes si hay alguno presente . 

 

onPostExecute: En este método se analiza el mensaje obtenido de la Business al actualizar  el reparto y se despliega un mensaje correspondiente en la UI. 

 

3.2 AgregarNotificacionesTask  

doInBackground: se envían las notificaciones pertenecientes a una farmacia a través del  método ​procesarFarmacia() presente en la Business. 

 

onPostExecute: al terminarse los procesamientos en el background se llama al método  desplegarVistaCorrespondiente() de la Activity  AgregarNotificaciones. En el mismo se  despliega la información de la próxima farmacia en la activity InfoFarmacia o en caso de no  existir otra pendiente se llama a la Activity IngresarMonto. 

  

3.3 AgregarSitioVisitadoTask  

Como indica el nombre de esta clase, se agrega un sitio visitado notificando a DUSA  mediante uno de los WS. Al terminarse el procesamiento en el background se redirige a la  activity RepartoCerrado. 

 

3.3 CerrarRepartoTask  

En el background se chequea que el reparto no tenga nada pendiente y luego se  invocan  los ws necesarios para corroborar que el reparto puede ser cerrado para posteriormente  indicar por uno de los ws que el reparto está terminado. 

 

3.4 IniciarRutaTask  

Se realizan los chequeos pertinentes para asegurar que el reparto puede ser iniciado y en  función de esto se despliega en la UI un mensaje adecuado. 

(22)

 

3.5 IniciarSesionOnBackground  

Esta tarea es usada cuando un usuario con una sesión iniciada en el dispositivo abre la  aplicación luego de haberla cerrado. Realiza un login mediante el ws correspondiente con  las credenciales del usuario almacenadas localmente en la BD del dispositivo. Luego de  realizar el login redirige  la activity que corresponda según el estado del reparto cuando el  usuario cerró la aplicación. 

 

3.6 IniciarSesionTask  

doInBackground:  Utilizando el ws de inciar sesion corrobora la password y contraseña  ingresadas en la UI por el usuario. 

 

onPostExecute: Analizando el mensaje obtenido del ws anterior muestra un mensaje de  error correspondiente o en caso de éxito redirige a la activity ObtenerReparto.  

 

3.7 NoVisitarFarmaciaTask   

doInBackground: Se procesa la farmacia en cuestión y se notifica a DUSA mediante un ws. 

 

onPostExecute: Se despliega en la UI la próxima farmacia de existir una o en caso contrario  la vista de ingresar monto. 

 

3.8 ObtenerRepartoTask   

doInBackground: se obtiene mediante ws el próximo reparto del usuario logueado y también  la información de cada farmacia en el reparto persistiendo los datos en la DB local. Este  método se explica más detalladamente en la sección correspondiente al paquete Business. 

 

onPostExecute: se despliega el mensaje adecuado en la UI si ocurre algún error o si el  usuario no tiene un reparto asignado. En caso contrario se llama al método 

showProperList()  de la activity ObtenerReparto el cual desplegará la vista correspondiente  dependiendo del estado del reparto. 

 

3.9 ReordenarFarmaciasTask   

Se actualiza en el background el orden en que se visitaran las farmacias notificando a  DUSA mediante un ws el nuevo orden y persistiendo estos cambios en la DB local. Luego  en onPostExecute se refrescan las listas de farmacias con el nuevo orden asignado. 

           

(23)

 

4.0 PAQUETE BUSINESS:

 

En Business se implementa la lógica de los casos de uso que componen la 

aplicación, con funciones utilizadas por las activities y async Tasks descritas anteriormente. 

Para el acceso a tales funcionalidades desde el resto de los paquetes de la aplicación se  cuenta con la interfaz IBusiness. 

 

4.1 ObtenerReparto 

Esta operación contiene la lógica respecto al caso de uso Obtener Reparto. En  primer lugar obtiene la información del reparto. Si la información recibida es correcta, y el  reparto está abierto o cerrado, se crea un nuevo reparto en la base con la interfaz de  persistencia. Acto seguido, se pide la información de cada una de las farmacias y se  almacenan estas en la base de datos. Luego se setean en la clase Business los atributos  relativos al reparto, como ser, fecha, número de salida, el id del reparto en la base de datos  y el id del reparto en el sistema de DUSA, entre otros.  

 

En caso de que se pierda la conexión mientras se está obteniendo la información de  las farmacias, se hace un rollback eliminando todo lo relativo al reparto para luego retornar  que no hay conexión. 

 

4.2 IniciarSesion 

Recibe las credenciales del usuario, nombre y contraseña. Con estos datos, consulta  la interfaz de web services para autenticarse ante el sistema de DUSA. En caso de éxito, se  almacenan ambos datos en la base, y en la clase Business se guarda el nombre del 

usuario.  

 

En caso de no existir conexión a internet, no se inicia la sesión y se retorna un  mensaje de error acorde. En caso de no autenticar correctamente al pretendido usuario, se  retorna un mensaje de error acorde. 

 

4.3 ActualizarReparto 

En alto nivel, esta operación obtiene nuevamente el reparto de DUSA. Si la hora de  último cambio es distinta de la que el sistema tiene almacenado, se procede a actualizar el  reparto.  

 

Esto implica, impactar el nuevo reparto en la base de datos, obtener la lista de  órdenes del reparto anterior (el usuario pudo haber hecho modificaciones de orden, y  debemos conservarlas). Luego se realiza un merge entre la lista de órdenes del reparto que  existía en el sistema y la lista de farmacias a visitar obtenida en el reparto actualizado. 

 

Para este merge, creamos una lista resultante y en primera instancia recorremos la  lista ordenada que había en la base. Para cada farmacia de esta lista, se pregunta si esta  sigue en la lista actualizada de farmacias a visitar. En caso de estar (la farmacia sigue entre 

(24)

las farmacias que deben ser visitadas) se la agrega a la nueva lista, con el mismo número  de orden que tenía. 

 

Luego, se recorre la lista nueva de farmacias, y se pregunta si cada una de ellas  está en la lista resultante. En caso de no estar, es porque es una farmacia que se agregó al  reparto, por lo que se la agrega al final de la lista. 

 

Acto seguido, se procede a persistir la lista mergeada de farmacias. Para esto  recorremos la lista y para cada farmacia que esté en ella, pedimos la información a DUSA y  la impactamos sobre la base de datos. 

 

En caso de que alguno de estos pedidos de información falle, típicamente por no  tener conexión, se realiza un rollback, dejando la base y los atributos de la clase business  tal y como estaban antes de invocarse la operación, y se comunica el no éxito de la  operación. 

 

En caso de que la operación sea exitosa, se elimina el reparto antiguo y se retorna el  éxito de la operación. 

 

Esta operación se apoya en dos procedimientos auxiliares, uno para persistir la lista  de farmacias merengadas y otra para hacer el merge entre estas listas. 

 

Se retorna un objeto de la clase “MensajeError” que contiene el resultado de la  operación, junto con un DataProximoReparto que está nulo en los casos de no éxito de la  operación, y contiene los datos del reparto en caso de que el reparto haya sido actualizado. 

 

4.4 IniciarRuta 

Si el estado del reparto actual es cerrado, se procede a verificar que el usuario haya  escaneado la salida en planta. El chequeo en planta se hace consultando por el estado del  reparto en D.U.S.A, si éste es ​INICIADO​, es porque el usuario ha marcado la salida en  planta, de lo contrario no lo ha hecho. 

 

Si el usuario escaneo la salida en planta, se invoca a la operación descrita  anteriormente (actualizarReparto()). En caso de que no exista conexión, se retorna un  mensaje de error acorde. Lo mismo sucede en caso de que no se haya escaneado en  planta.  

 

Para los casos en que no hubo problemas de conexión y se ha escaneado en planta,  se retorna un mensaje informando si el reparto cambió o no, junto a la información del  reparto. 

 

4.5 procesarFarmacia 

Esta función se encarga de la lógica general del caso de uso AgregarNotificaciones. 

Para ésto, dado que es en este punto del flujo en el que efectivamente se da por concluida  la visita a una farmacia, se persisten definitivamente en la base de datos tanto los códigos  de paquetes y documentos no entregados como las notificaciones asociadas a la visita. 

(25)

 

Para concretar lo mencionado anteriormente se utilizan dos procedimiento auxiliares,  guardarCodigosNoEntregados() y guardarNotificaciones(), que son los que finalmente  llaman a las funciones correspondientes de la interfaz de persistencia para que los datos  sean guardados en la base. 

 

Adicionalmente es en la operación en cuestión que se realiza el chequeo de la visita  a la primer farmacia, resultando en un cambio de estado a ​INICIADO​ de ser positivo. 

 

Para finalizar el procesamiento se envían los datos recabados de la visita al servidor  de D.U.S.A a través del WS ​procesarFarmacia(fechaRecorrido, idR, nroSalida, 

idInfoFarmacia, notificaciones, hora)​, de no existir conexión al momento de enviar los datos,  se guardan en la base como pendiente de envió todas las notificaciones asociadas a la  farmacia que se estaba intentando procesar, para su posterior envío al recuperar la  conexión. 

 

Esta función retorna la próxima farmacia según la lista ordenada por el usuario, de  no existir próxima farmacia, porque la que se está procesando la última, se retorna null y se  cambia el estado del reparto a ​INGRESANDOMONTO​. 

 

4.6 noVisitarFarmacia 

Esta operación engloba la lógica del caso de uso NoVisitarFarmacia. Siendo su  implementación sumamente similar a la operación procesarFarmacia con la diferenciación  de que al guardar en la base los datos asociados a la no visita de una farmacia guarda  como notificaciones el string “​NO SE VISITO” ​y no persiste los códigos de paquetes y  documentos no escaneados al perder sentido tal acción en este contexto. 

 

4.7 cerrarReparto 

Esta función lleva a cabo los pasos necesarios para culminar un reparto y realizar un  backup del estado de la base de datos en ese momento, implementado así el caso de uso  CerrarReparto y cumpliendo con el requerimiento de mantener un respaldo de los datos. 

 

Particularmente lo primero que se ejecuta al invocarse esta operación es el llamado  al procedimiento auxiliar backupBaseIfNeeded(), el cual chequea si ha pasado una semana  desde el último respaldo de la base de datos y de ser así realiza uno nuevo. 

 

Luego, si no hay datos pendientes de envío, se procede a consultar al servidor de  D.U.S.A (a través de la operación provista por la interfaz de WS 

isRepartoTerminado(fechaRecorrido,idR,nroSalida)) ​si el chofer escaneo el código de  barras marcando su llegada en planta. Si en el sistema existen datos pendientes de envío la  operación retorna que no hay conexión, bajo el entendido de que de haber algo pendiente  se envía automáticamente al recuperar la conectividad. 

 

Si el chofer realizó el escaneo en planta la operación retorna​ OK​, cambia el estado  del reparto a ​COMPLETO, ​e invoca a la operación 

Referencias

Documento similar

Generación de Energía por medio de Hidroeléctrica Aguacapa.. a.) Ley Orgánica del Presupuesto EMPRESA DE GENERACIÓN DE ENERGÍA ELÉCTRICA.

MFCP/51111306/DPM MUEBLE P/ COMPUTADORA COLOR ROJO VINO CON PORTA TECLADO MFCP/51530943/DPM MULTIFUNCIONAL COLOR GRIS MFCP/51530945/DPM NO BREACK COLOR NEGRO MFCP/51530946/DPM

permanencia: El subsidio consistiráen un monto único y total de hasta 170 U.F., su monto mensual a pagar, por concepto de renta de arrendamiento, se irá descontando de acuerdo a

Monto de la transacción es el importe de la contraprestación al que la entidad considera tener derecho a cambio de transferir el control sobre los bienes o servicios acordados con

• Mediante circular SPGP-015-2020, del 7 de junio de 2,020, el Jefe del Gabinete Presidencial instruye a las instituciones de Gobierno dependientes del poder Ejecutivo, en

Si su pedido de OTC excede el monto del beneficio, es posible que algunos artículos de su pedido se cancelen para cumplir con el monto del beneficio del plan.. HealthSun Health

Realiza el análisis para autorizar el monto de caja chica, en función del monto autorizado para el Fondo Rotativo de su Dirección, en virtud que toda restitución de caja

Monto de provisión correspondiente a cartera en incumplimiento (campo 10). MONTO DEDUCIDO DE LA EXPOSICIÓN POR GARANTÍAS REALES Corresponde al monto de las garantías reales