Buenas!
Como había comentado en el post que hablaba sobre el pasado dia 13 de Junio donde hicimos una presentación y workshop en Granada. Pues aquí está el workshop paso a paso. (Podéis “clickar” sobre las fotos si queréis verlas ampliadas.)
Empezaremos desde el punto cero, los primeros pasos en la plataforma. Para ello vamos a implementar una pequeña App, es un pequeño ejemplo para guiarnos a través de diferentes características de la plataforma. En dicha app lo que haremos sera implementar un sistema para grabar nuestra jornada laboral, desde que se entra en la oficina hasta que se sale.
Lo primero es obtener un entorno Salesforce, o como a menudo me escuchareis/leeréis llamarlo una organización – org.
¿Dónde?
En este link podréis conseguir todas las orgs de desarrollo que necesitéis.
https://developer.salesforce.com/signup
Una vez que tengamos nuestro entorno, nuestra organización vamos a logearnos en ella.
Lo primero que encontraremos será algo como esto:
Vemos una ventana con bastantes cosas dentro, diferentes menus para admistrar nuestro entorno, de creación, varias pestañas, links…
Por ejemplo veamos el Home tab ( la pestaña Home) .
Y también vemos en la parte derecha superior nuestro nombre, pues es aquí donde podremos configurar nuestro perfil, nuestra cuenta.
Seleccionamos la opción My Profile
Como vemos la interfaz cambia, desde esta podemos modificar nuestra apariencia y datos que compartiremos con los otros usuarios de la organización.
Pero si queremos cambiar nuestros datos personales, como aquellos que no se mostrarán a los otros usuarios, nos dirigiremos a My Settings
Desde aquí podemos modificar nuestros datos personales, nick name , nombre de usuario, etc…
Una vez modificados vamos a pasar a escribir nuestro primer post en la Organización. ¿Dónde?
En chatter! Si queréis conocer más sobre Salesforce Chatter aquí os dejo un link: What is Chatter?
Entonces vamos a la pestaña Home y pasamos a escribir, algo como :
Hola Mundo! clickamos en Share y nuestro post ya es publico.
¿Cómo se vería todo esto en la aplicación móvil? ¿necesito hacer algo para tener chatter, mis post y esta información en la aplicación móvil?
No! Está todo listo.
De echo para ver como se vería en la versión móvil lo que vamos a hacer es:
Cogemos de la URL de nuestro browser y lo copiamos en una nueva pestaña, pero tras el nombre del servidor añadimos /one/one.app.
Quedando de la siguiente manera:
– Tengo en estos momentos : http://emea.salesforce.com/setup…
– Lo sustituyo por : http://emea.salesforce.com/one/one.app
Luego vamos a poder ver directamente sin necesidad de ninguna implementación la versión de escritorio y la versión móvil, solamente añadiendo en la URL /one/one.app
El resultado es:
Pues ahora vamos a pasar a crear un objeto. Los objetos nos ayudaran a guardar información en Salesforce. De echo podríamos comparar con una base de datos y el objeto será esta tabla y los campos de dicho objeto serán las columnas de la tabla.
Una pregunta nos viene a la mente en estos momentos ¿Cuál es el campo clave , único en dicha tabla?
Por defecto todo objeto tendrá un campo llamado Id. El que representará un identificador único para cada registro que se cree.
¿Cómo creo los objetos ( Tablas)?
Vuelvo a la parte superior derecha: Setup. Desde aquí podré realizar todas las tareas de administrador.
Nos movemos al menú de la izquierda, y debajo del menú Create hay otro que se llama Objects es aquí donde nos dirigimos.
Y obviamente lo que vamos a hacer es crear uno nuevo con lo que presionamos el botón New.
Tenemos varias partes en la creación de un objeto.
Label: representa la etiqueta que el usuario verá
Plural Label: es igual pero en plural, normalmente esta etiqueta se mostrará en la pestaña una vez sea creada.
Object Name: este debe ser único para toda tu organización
Desde esta pantalla tenemos la opción de decidir si el nombre de los registros que se creen sea de tipo Texto o auto – numérico.
En nuestro caso, en nuestro ejemplo seleccionaremos auto – numérico y también decidimos el formato. Comenzando desde el registro número 1.
Nuestro objeto:
– Label: Jornada Laboral
– Object Name: Jornada
Desde aquí podemos también seleccionar otras características adicionales como que podemos permitir hacer Reports, crear Tareas …
Una vez salvado el objeto podemos crear sus campos ( columnas )
Pasamos pues mediante clicks a crear los campos.
Primero seleccionamos el tipo del campo, en nuestro caso vamos a crear dos campos (columnas) de tipo DateTime que llamaremos Entrada y Salida.
Tras seleccionar el tipo, le damos nombre al campo, de nuevo tenemos Label y Field Name. En nuestro ejemplo serán:
– Label: Entrada
– Plural label: Entradas
– Field Name: Entrada
Lo fijamos como requerido. Lo que quiere decir que cada vez que se cree un registro de tipo Jornada el campo entrada deberá estar siempre relleno con un valor.
Ahora lo que haremos será darle un valor por defecto. Esto es opcional, no es necesario darle valor por defecto pero para nuestro ejemplo lo que queremos es que cada vez que un registro de tipo jornada sea creado la entrada se registre en el momento en que se crea la jornada con lo que el valor por defecto será Now().
Como henos indicado que sea requerido siempre será visible para todos los usuarios de la organización que tenga acceso a este objeto. De igual manera será requerido en UI ( layout ) para que sea accesible por estos usuarios, y además si se insertase mediante código, dev console, API … seguiría siendo requerido. Esto se conoce como requerido universal. ( Más adelante quizás en otros post veremos que se puede hacer requerido solamente en UI, por ejemplo). Pues bien, retomando el tema, como es requerido por defecto todos los checkboxes aparecerán checkeados y no tendremos posibilidad de desactivarlos.
Una vez creado un campo de igual forma creamos el siguiente campo que guardará la salida, grabando así el final de la jornada laboral
El segundo campo, será igual que el Campo Entrada, mismo tipo Date/Time en este caso, pero ahora no lo haremos requerido ni le daremos valor por defecto.
– Label: Salida
– Plural label: Salidas
– Field Name: Salida
Una vez que tenemos nuestro objeto con sus dos campos lo que nos gustaría es crear nuevos registros. ¿Cómo lo hacemos? Podríamos hacerlo mediante código pero quizás es mucho mas sencillo si creamos una UI para dicha tabla.
Luego vamos a dar acceso a la tabla creando una nueva pestaña que este directamente relacionada con nuestro objeto. Desde dicha pestaña podremos manejar todas las operaciones que podemos hacer con Jornada Laboral.
¿Cómo creamos una pestaña?
De nuevo nos vamos a nuestro menú favorito: Setup , seguidamente en la parte izquierda seleccionamos el menú Create y seguidamente por Tabs.
Llamo Tabs, a las pestañas.
Luego pasamos a crear una nueva pestaña/ Tab. Seleccionamos el tipo de tab a crear, en este caso Custom Objects Tabs, ya que asociaremos una nueva pestaña/tab a nuestro custom object que acabamos de crear.
Seleccionamos el icono que queremos que aparezca asociado a nuestra pestaña, uno de los que hay por defecto o tenemos la posibilidad de crear uno nuevo.
Con lo que ahora aparecerá en el menú superior de pestañas la nuestra, asociada a Jornada.
Pues vamos a ver lo que hay en esta nueva pestaña.
Veamos que pasa si presionamos “Go” , nos muestra una lista vacía. Este “Go” está asociado a diferentes listas que podemos tener en dicha pestaña. Pueden ser listas filtradas y podemos seleccionar ver una serie de campos y otros no. ( En estos momentos no lo veremos aquí pero “Feel Free” de hacer pruebas en tu org.
Voy a pulsar sobre el botón nuevo y obtengo la siguiente pantalla
Aquí es donde puedo crear un nuevo registro, solamente he de rellenar los campos necesarios y opcionales en este caso.
Si me fijo ahora en el resultado, esta es la UI standard de Salesforce. Quizás me gustaría modificar el orden de los campos en pantalla, que quizás también quiera eliminar de esta UI. Todo esto es posible si modificamos el Layout.
La apariencia el layout puede ser modificado desde el registro o desde nuestro maravilloso menú Setup -> Create -> Objects -> Jornada Laboral y una vez en el objeto podemos ir a la sección Page Layout.
Esta pantalla, es donde podemos arrastrar los campos que queremos que aparezcan o no, y de igual forma podemos modificar el orden.
Volviendo entonces a nuestro registro una vez modificado el Layout / la apariencia así es como queda.
¿Qué pasaría si ahora después de crear algunos registros quiero modificar la tabla?
Es posible. Puedo añadir nuevos campos, o eliminar los existentes ( si no están en un paquete managed! Lo veremos en otro post) pero he de tener en cuenta que los nuevos campos no tendrán valor en los registros ya creado con lo que manualmente o mediante script deberé de modificar los registros existentes. Y de igual forma si eliminamos un campo los datos guardados en dicho campo serán perdidos .
Vuelvo mi objeto entonces, De nuevo desde Setup… Para!! Mira una cosita, vemos que en el centro tenemos Recientes, y veo que desde aquí también puedo acceder al objeto porque es uno de mis Recent Items.
Lo dicho, voy a crear un nuevo campo, pero en este caso es de tipo Picklist
Lo voy a llamar acción y le daré dos valores: Entrar, Salir. Valor por defecto será Entrar. Pero recuerda este valor sera por defecto para los nuevos registros no para los existentes.
Ya tengo en mi objeto en total 3 campos.
Vuelvo a mi tab de Jornadas y creo un nuevo registro, Veo que por defecto el valor de mi nuevo campo es Entrar.
Sin embargo, si vuelvo al registro que había creado antes mi nuevo campo no tiene valor, se lo tengo que dar manualment.
Y entonces ¿todo esto se puede ver también en el móvil? ¿Y podemos crear registros desde el móvil? ¿Y la pestaña es accesible desde el móvil? La respuesta es sí! Vamos a verlo.
Selecciono la opción Show More
Selecciono Jornadas Laborales
Desde aquí puedo crear nuevos registros también!!!
De igual manera puedo ver los registros que se han creado anteriormente
¿Qué tal si ahora desde tu pantalla de inicio pudieses crear una Jornada? Sin necesidad de tener que navegar hasta la pestaña Jornadas. Algo que sea rápido, que solamente con un click sea posible de crear mi Jornada con el momento que entro en la oficina. Pues lo vamos a hacer usando Publisher Actions.
Tener una acción en esta pantalla, que nuestra home page, para poder crear nuestra entrada
¿Qué tengo que hacer entonces?
De nuevo me dirijo a Setup, y en este caso a Chatter, me dirijo a Settings y compruebo que en la sección de Publisher Actions el checkbox esté checkeado
Una vez habilitado vamos a crear dichas Acciones. Hay acciones de diferentes tipos, si queréis saber mas sobre ellas aquí os dejo un video sobre como crear y usar Publisher actions: Publisher Actions y Salesforce1
Crearemos una acción Global la cual es accesible desde cualquier parte de nuestra organización.
Setup -> Create -> Global Actions
Desde aquí será posible crear una nueva acción. La acción que voy a crear se llamará entrada, y lo que voy a registrar en dicha acción es mi hora de entrada a la oficina.
Luego selecciono la opción de crear un registro ( create record) en este caso de tipo Jornada laboral.
Puedo escoger los campos que quiero que se muestren en dicha acción. En mi caso quiero que solamente se muestre el campo llamado Entrada de tipo Date/Time que por defecto siempre tendrá el valor Now(). Con lo que realmente no tendré rellenar nada.
Luego mi acción está creada.
Pero
¿Qué debo hacer para que dicha acción se muestre en mis dispositivos? Debo de añadirla al layout, como haciamos con los campos. Esto es similar, solamente que ahora iremos al layout asociado a las acciones globales. Publisher Layouts. Este se encuentra en estos momentos en Setup-> Customize -> Chatter -> Publisher Layout ( cambiará en la próxima release Summer ’14)
Por defecto existe un Global Publisher Layout, pues en este ejemplo vamos a modificar el existente, aunque tambien es posible crear nuevas vistas/ layouts.
Simplemente editamos el existente y podemos ver que nuestra acción, Entrada, la que justo hemos creado aparece aquí. Pues entonces solamente la arrastramos al Layout que es visible para esta organización, y la pongo en primer lugar. ( puedo ponerla en el orden que desee, pero para el ejemplo la pondré en primer lugar)
Una vez están salvados dichos cambios, solamente he de volver a mi Home page y puedo ver que una nueva acción aparece. En este caso es Entrada.
De igual forma si me voy a mi dispositivo móvil puedo comprobar que esta acción está aquí sin necesidad de programar nada. Lista para móvil.
Clicko sobre el símbolo mas que se encuentra en la parte inferior derecha, y una nueva ventana se me abre donde puedo ver todas las acciones disponibles.
Selecciono la acción Entrada. Y una nueva pantalla se me abre mostrándome dicha acción, con el campo que pusimos en su layout.
Una vez terminada la acción, ( realmente no tendría que hacer nada ya que la fecha y la hora se rellenan por defecto gracias al valor introducido por defecto en dicho campo) , presiono el botón Submit que se encuentra en la parte superior derecha.
Y una vez terminada la acción un nuevo registro de tipo Jornada se ha creado, y un nuevo post en el chatter se ha creado también con el link a nuestro nuevo registro. Dicho link y registro están accesibles tanto desde la versión móvil como desde la vista de escritorio.
También en cualquier momento podríamos editar dichos registros.
¿Qué seria lo siguiente que podríamos pedir? Pues creo que lo siguiente podría ser una acción para actualizar el registro jornada con la hora de salida.
Es decir en este caso lo que queremos es actualizar un registro. Para ello, dicha acción debe ser parte del objeto.
Dicha Acción aparecerá como las otras en el feed, pero en este caso al ser una acción asociada estrictamente a un registro de un determinado objeto, entonces aparecerá en el feed de ese objeto.
Para crear acciones relacionadas a un objeto, nos tenemos que dirigir al objeto en si. En nuestro caso nos iremos a Jornada. En dicho objeto nos vamos a la sección: Buttons, Links, and Actions.
Seleccionamos New, y pasamos a crear una nueva acción, que en este caso el tipo será: Update Record
Tenemos de nuevo la opción de escoger los campos que queremos que aparezcan en esta nueva acción. Para nuestro ejemplo vamos a poner solamente el último campo que habíamos creado, el picklist que llamamos acción.
Una vez que tenemos nuestra acción Salida creada, tenemos que ponerla en el Layout / UI de nuestro objeto. Para ello vamos al page layout del objeto Jornada.
Cuando editamos veremos que hay una sección especial para Publisher Actions, es aquí donde vamos a añadir la nuestra. Pero para ello necesitaremos sobre escribir el Global Layout que por defecto existe. Es decir, por defecto siempre el Global layout sera el que esté en todos los objetos.
Una vez sobre escrito podremos modificar y añadir las acciones que deseemos aparezcan en los registros de este tipo.
Para nuestro ejemplo la acción Salida será la primera de la lista.
Si ahora nos vamos a cualquiera de los registros de tipo Jornada, podremos comprobar que nuestra acción no aparece.
¿Por qué? Porque por defecto el feed de dicho custom object, Jornada, no está activado. Pues debemos activarlo. Con lo que volvemos a Setup -> Customize -> Chatter -> Feed tracking
Una vez aquí lo que haremos será buscar nuestro objeto Jornada y habilitar su feed.
Ahora podemos volver a los registros de tipo Jornada y ver que nuestra acción aparece.
Aparecerá también en el dispositivo móvil sin necesidad de ninguna implementación extra.
Vemos que es posible por lo tanto modificar el valor del picklist y que nuestro registro se actualice.
Para actualizar el campo Salida, dependiendo del campo Acción , podríamos hacerlo de diferentes formas. La que vamos a escoger es usando un trigger, sin embargo esto es solamente para conocer qué son los triggers y cuando se ejecutan, ya que como digo podríamos usar otras herramientas que la plataforma nos da sin necesidad de usar código.
De igual forma lo vamos a aplicar solamente al campo salida cuando
Entonces dicho esto, ¿qué es un trigger?
Es código que se ejecuta antes o después de que los registros de un determinado tipo sean insertados, actualizados o eliminados. Cada trigger se ejecuta junto con sus variables de contexto, que facilitan el acceso a los registros que causaron la ejecución de dicho código.
¿Dónde puedo crear el trigger?
Los triggers estarán asociados siempre a un tipo de objeto, para nosotros, nuestro trigger irá asociado al objeto Jornada.Podemos crearlo desde su pantalla de administración: Setup-> Create-> Objects-> Jornada
El código asociado a nuestro trigger de ejemplo es el siguiente
trigger JornadaTrigger on Jornada__c (before insert, before update, before delete, after insert, after update, after delete, after undelete) { if(Trigger.isBefore) // Modifications -- before trigger { if(Trigger.isInsert) {//Trigger.New } else if(Trigger.isUpdate) {//Trigger.new, Trigger.oldMap //Modify the date field Salida for(Jornada__c modifiedJornada:Trigger.new) { if(modifiedJornada.Accion__c == 'Salir') { modifiedJornada.Salida__c= System.Now(); } } } else if(Trigger.isDelete) {//Trigger.oldMap } } else // Validations --- after trigger { if(Trigger.isInsert) {//Trigger.new } else if(Trigger.is Update) {//Trigger.new, Trigger.oldMap } else if(Trigger.isDelete) {//Trigger.oldMap } } }
Lo único que estoy haciendo es comprobar que cuando el picklist es fijado al valor “Salir”, el campo salida sera fijado con el valor Now() en dicho momento.
Nuestro trigger quedará de esta forma:
Ahora lo siguiente y muy importante es crear la clase Test para comprabar que dicho trigger funciona correctamente. ( y cuando digo siguiente no es estrictamente necesario que sea después, este paso podría ser incluso anterior a la implementación de código funcional. TDD )
La clase test, Unit Test, puede ser creado desde : Setup->Develop-> Apex Clases.
El código de dicha clase puede ser como sigue:
@IsTest private class JornadaTest { //hola!!! @isTest static void actualizarJornadaSalida() { //create the Jornada record Jornada__c jornada= new Jornada__c(); // don't need to add specific values becasue the platform will do it for me insert jornada; jornada.Accion__c= 'Salir'; Datetime now = System.now(); update jornada; List<Jornada__c> jornadas = [Select Id, Name, Salida__c, Entrada__c, Accion__c From Jornada__c Limit 1]; System.assert(jornadas[0].Salida__c != null , 'Expected a value however is null'); System.assertEquals(now, jornadas[0].Salida__c); delete jornadas[0]; jornadas = [Select Id, Name From Jornada__c Limit1]; System.assertEquals(jornadas.size() , 0 ); } }
Si hubiese algún problema en el código al guardar te daria un fallo con el que puedes dirigirte al error y solucionarlo.
¿Cómo puedo saber si funciona o no? Una vez guardado el Unit Test ( An Introduction to Apex Code Text Methods) , lo que debemos es lanzar su ejecución. Para ello lo que hacemos es solamente presionar el botón “Run Test” y nos mandará a la pantalla de ejecución de Unit tests.
Donde por fin podremos comprobar si todo ha ido bien.
Vamos a comprobar que realmente todo funciona como hemos previsto.
Vuelvo pues a mis Jornadas
Creo una nueva Jornada para nuestro ejemplo
Una vez creada me dirijo a ella, por ahora la Salida no tiene ningún valor. Lo que haremos entonces es seleccionar desde dicho registro la acción que habíamos creado antes, Salida.
Una vez en dicha acción Salida, lo que haremos será cambiar el valor del picklist to Salir, lo que hará que se lance el trigger cuando guarde- actualice el registro.
Y aquí podemos ver que realmente la hora de Salida se ha rellenado en el correspondiente campo, sin necesidad que el usuario lo haga manualmente ( Esto también podría hacerse con el campo Entrada, y de igual manera poner algunas validaciones alrededor para que el usuario no modifique la hora de entrada o salida manualmente.)
Pues este es nuestro registro actualizado. Fijate que estamos en el “puntito” central . Si me muevo hacia la izquierda…
… hacia el “primer” puntito lo que obtengo es la vista del chatter asociado a ese objeto, su feed. Si me muevo hacia la derecha…
… si me muevo hacia la derecha lo que puedo ver son las listas relacionadas con nuestro objeto. En nuestro caso actividades y el histórico de éste,
Lo siguiente que vamos a hacer es crear un nuevo menú en la parte superior derecha, una nueva App.
Para ello nos dirigimos a Setup-> Customize-> Create -> Apps
Vamos a crear una nueva App. Para ello presionamos el botón New en la sección de Apps
Seleccionamos Custom App
Y pasamos a darle nombre
Podemos seleccionar un icono, imagen que se asocie a nuestra app una vez que sea seleccionada en el menú.
Y ahora lo que haremos será escoger que tabs queremos que formen parte de nuestra app.
Seguidamente decidimos la visibilidad de nuestra app para los diferentes perfiles existentes en la org.
Y una vez guardada, podemos comprobar que ahora nuestra app aparece en el menú superior derecho.
Y que una vez seleccionada solamente los tabs que incluimos en ella aparecen en la lista de tabs.( siempre podremos ir a ver todos los tabs presionando el sÍmbolo + )
Creemos ahora un perfil por defecto para nuestra app ( es recomendable usar permission sets en lugar de perfiles!! , solamente usamos el perfil para este ejemplo 😉 )
Para crear un nuevo perfil especial para mi app me dirijo a Setup -> Manage Users -> Profiles
Una vez aquí puedo crear un nuevo perfil, presiono el botón New Profile.
El proceso de creación de nuevos perfiles se hace siempre a partir de existentes, con lo que como podéis ver tenemos que escoger uno de los que tenemos en nuestra organización, uno como plantilla. Le damos un nombre y seguimos adelante.
Comprobamos ahora que tiene los permisos necesarios que deseamos para nuestra aplicación.
Que nuestra app sea visible
Que nuestra Tab sea visible
Que nuestro objeto tenga los permisos necesarios.
Todo listo … para empaquetar!!! Vamos a crear ahora un paquete para poner todo nuestro desarrollo y distribuirlo.
¿Cómo creamos un paquete?
Lo primero a tener en cuenta son los diferentes tipos de paquetes que podemos crear, en este post solamente trataremos paquetes unmanaged. ( Estoy trabajando en otro post que explica en mas detalle el proceso de creación de paquetes.)
Pues bien para crear un paquete necesitamos ir a Setup-> Create-> Packages
Presionamos el botón New, y creamos un nuevo paquete para ello, necesitamos darle un nombre. Este nombre será el que aparezca en la org de destino y servirá para identificar dichos paquetes.
Llamaremos a nuestro paquete Bolsa de Horas.
Una vez que tenemos el paquete (el contenedor) necesitamos poner cosas dentro de este.
¿Qué es lo que vamos a poner en el paquete? Todo lo que hemos implementando antes:
- App: Bolsa de Horas
- Tab: Jornadas Laborales
- Custom Object: Jornada Laboral
- Trigger: JornadaTrigger
- Class: JornadaTest
- Profile: Standard User – BH ( los perfiles se mapearan en el momento de instalación, no se crean nuevos perfiles. )
- Acciones: Entrada, Salida
Para ello solamente hemos de presionar el botón Add
Repetimos este paso hasta que todos los elementos estén dentro del paquete.
Una vez que tengamos todo lo deseado en nuestro paquete, lo que vamos a pasar es a crear el link que distribuiremos.
Para ello lo único que necesitamos hacer ahora es: Upload
En el proceso de carga- Upload, necesitamos proveer con el nombre de la versión. Vamos a poner Summer ’14. Con lo que cuando el paquete se instale aparecerá algo como esto: Bolsa de Horas, Summer ’14
Lo siguiente, la versión. Por defecto la primera será: 1.0 . Está irá incrementando con el tiempo.
Volviendo a como se veria el paquete en la org de destino, donde se instale será:
Bolsa de Horas, Sumer ’14, v1.0
Tenemos la opción de poner usuario y contraseña con lo que solamente aquellos que tengan estos datos serán capaces de instalar el paquete.
Y cuando todo este listo, volvemos a presionar en Upload.
Internamente se está comprobando que todo esta bien, que existe cobertura para todo el código que esta dentro del paquete ( como mínimo un 75% de cobertura), solamente nos queda esperar.
Una vez terminado este proceso obtendremos una URL que será la que usaremos para distribuir.
Si has llegado hasta aquí esta es la URL del paquete que he creado para este post. Si lo instalas en una de tus orgs/entornos podrás ver y modificar los elementos.
Paquete Bolsa de Horas: https://login.salesforce.com/packaging/installPackage.apexp?p0=04t20000000AefG
Recordad esto es un simple ejemplo, ahora es tu turno de continuar 😉
Hasta la próxima. Gracias!!
En Salesforce se pueden hacer tantas cosas que puede abrumar en un principio. Es una gran guía. Muchas gracias por compartir!
Muy buena guía!!!
Muchas gracias @jg_sandom!!