Empezando con Lightning Components – ¿Qué herramientas usar?

Estoy de vuelta, hoy quiero compartir algo con lo que estuve trabajando el pasado viernes y quizás os pueda servir de ayuda para comenzar a trabajar con … ¡Lightning!

Como sabéis Lightning fue publicado el pasado Octubre en Dreamforce ( os prometo futuros post sobre ello 😉 ) , y que tiene varias partes Lightning Components, Lightning App Builder , Lightning Connect y Ligthning Process Builder. ( Sobre Lightning Process Builder puedes encontrar una serie de posts aquí :

Pero hoy voy a centrarme en empezar con Ligtning Components. Que la vida sea fácil a la hora de crear Lightning Apps y Components.

¿Cómo podemos crear Lightning Apps y Lightning Components?

Podemos escoger entre :

  1. Usar Developer Console
  2. Usar Force.CLI
  3. Usar el plugin que Dave Carroll , @dcarroll , ha creado para sublime: Lightning Sublime

 

¡Vamos a ello!

Developer Console

Para usar developer console solamente ir al menú de la parte superior de nuestra organización y allí podemos encotrar nuestra Developer console.

Screenshot 2015-03-30 13.59.03

Una vez en ella , solamente debemos dirigirnos al menú File-> New -> Lightning Application o Lightining component.

Screenshot 2015-03-30 13.58.53

 

Force CLI

Lo que debemos hacer es descargarnos e instalar dicha herramienta. Para ello podemos hacerlo desde aquí.

(pasos para mac , siento mucho si hay algo diferente en Windows, no puedo comprobar si los pasos son diferentes ya que no tengo en estos momentos una maquina Windows. Pero si hay algo diferente

Una vez que se encuentre en nuestro local , lo siguiente es:

  1. Colocar el archivo force donde se vaya a usar
    1. En mi caso lo he puesto en /Applications/Force-cli/force
  2. Abrir un terminal
  3. Convertirlo en un fichero accesible
    1. $chmod +x /Applications/Force-cli/force
  4. Ahora, debemos añadir la localización a nuestro $PATH , para así poder usarlo desde cualquier fichero
    1. Desde tu directorio “home” editamos el fichero .profile: $open ~/.profile
    2. Esto abrirá en tu editor de texto el fichero y podremos modificarlo
    3. A veces, como en mi caso la variable $PATH se encuentra en el fichero .bash_profile, con lo que será este el que tengamos que editar
    4. Seguidamente añadimos a la variable PATH donde hemos puesto force
      1. export PATH=/Applications/force-cli:$PATH : quedando algo como esto

Screenshot 2015-03-30 13.58.35

  1. Y ya debería estar todo listo para poder usar force CLI desde cualquier terminal.
    1. Prueba a poner : $force version y para ver todos los comandos $force help
  2. Podemos crear ahora los componentes y aplicaciones en local y subirlos a la org mediante comandos
    1. $force login : para registrarnos el la org
    2. $force import : para importar metadata desde el directorio local
  3. Si por el contrario, ya tenemos componentes o aplicaciones que queremos descargar desde nuestra org utilizaremos :
    1. $force fecth aura : para descargar todo lo relacionado con Ligthning Components

 

Usar Lightning Plugin para Sublime Text que Dave Carrol ha creado:

Screenshot 2015-03-30 15.46.39

 

Pues lo único que hemos de hacer es seguir las instrucciones que Dave comenta en el repositorio… pero que pasa si teneis problemas como yo tuve!! Vaya dia me dio…

Comprobé que ya tenia todo lo necesario en mi mac para instalar el plugin: Sublime Text, PAckage control y Force CLI ya que los había usado anteriormente.

Luego me puse manos a la obra en instalar el plugin.

Para ello, como las instrucciones dicen usa Package control que has instalado en tu Sublime.

  1. Abre Package control : cmd+ Shift+P
  2. Selecciona Package Control: Install Package
  3. Y ahora vamos a poner el nombre del plugin- paquete que Dave ha creado: Lightning
  4. Pues bien una vez que lo tuve instalado parecía que todo iba bien… pero no L

 

Cuando intenté hacer login en mi organización como las instrucciones decían tenia un error, ya que Sublime no podía encontrar el fichero force CLI

 

Entonces pensé que quizás no estaba bien instalado, pero iba a mi terminal y funcionaba correctamente.

¿Qué estaba ocurriendo? ¿Por qué no podía usar el plugin correctamente?

Pues bien el problema era que sublime no respectaba mi variable $PATH que se encontraba en el fichero .bash_profile. Quizás si vosotros usais .profile en su lugar no tengáis problemas.

 

Pues lo que hice fue copiar el fichero force CLI en el directorio donde el plugin estaba buscando por el.

Una vez lo copie en los diferentes ficheros que Sublime+plugin me preguntaron … ¡El plugin funcionó!

 

Pero… mi gozo en un pozo! era posible logearse en la organización pero no era posible actualizar el contenido de mi fichero desde la org y tampoco hacer cambios que se guardasen en servidor… ¿Por qué?!!!! Aaaaa!!

No sabía exactamente que podía estar pasando , así que contacté con…¡Super Dave Carroll! Que por supuesto me contestó en question de minutos. ¡Twitter es buen medio!

¡Pues ya tengo la solución para compartirla con vosotros!

Esta solución.. fue instalar la ¡última versión de Force CLI !

Para que el plugin funcione correctamente necesitais tener la ultima versión que arregla unos pequenos defectos que tenia CLI.

¿Cómo sabes la versión de Force CLI que tienes?

$force version

Si esta es mas antigua de v0.22.25 , como era mi caso puede que tengas los mismos problemas que yo tuve.

Luego lo que hice, de nuevo, fue descargarme la ultima version de Force CLI , reemplazar el fichero que tenia en

  1. /Applications/Force-cli/force

Y ahora, me tocaba reemplazar aquellos sitios donde Sublime y el plugin estaba llamando ya que no respetaba mi $PATH. Pero hei hei!!

Esta vez cree Symbolic links!!

$ln -s /Applications/Force-cli/force /usr/bin

$ln -s /Applications/Force-cli/force /usr/bin/force

 

  1. Si tienes problemas dándole permisos, se te deniega el acceso prueba este commando: $sudo ln -s /Applications/Force-cli/force /usr/local/bin/force

Que así solamente he de reemplazarlo una vez cada vez que actualice , mucho más sostenible.

 

¡Ya estamos listos para empezar!¡A crear componentes! 

 Fireworks-1-SublimeConfiguration

 

Crear Registros Usando Process Builder

Hola de nuevo!

Este va a ser el primero de una serie relacionado con Process Builder.

Porque, ¡podemos hacer muchas cosas con Proces Builder! (.. yo creo que pronto hasta una paella jejeje)

 ¡Me gusta! 

megustaBobSponja

Pues bien, hoy vamos a ver como crear oportunidades para una Cuenta, SIN USAR CÓDIGO! 

Las oportunidades se van a crear, cuando la cuenta que se crea o se actualiza y cumpla estos requisitos:

  1. La cuenta tiene que ser de tipo Prospect
  2. La fecha de creación o actualización, va desde el 30 de Marzo al 30 de Diciembre. Para el próximo año ya veremos si seguimos esta regla 🙂

¿Qué necesitamos?

 

Paso por paso:

  1. Entramos a Process builder. Setup–> Build menu: Create Workflow & Approvals–> Process Builder

 Screenshot 2015-03-29 13.58.15

  1. Presionamos New y le damos los datos necesarios como en la fotoScreenshot 2015-03-29 14.00.07
  2. Salvamos y ahora nos redirecciona a una nueva pantalla donde podemos comenzar a trabajar Screenshot 2015-03-29 14.01.12
  3. Lo primero que vamos a hacer es seleccionar el objeto con el cual el proceso de creación de Oportunidades va a estar relacionado. En nuestro caso , queremos crear oportunidades para Cuentas/Accounts. Para ello presionamos sobre +Add Object Screenshot 2015-03-29 14.02.30
  4. Una vez seleccionado el objeto, pasamos a seleccionar el criterio de creación de oportunidades, para ello presionamos sobre +Add Criteria .En nuestro caso vamos a usar Filter conditions, podríamos también usar formulas o si queremos ningún criterio de selección, que siempre se ejecute el proceso( SI o Si 🙂 )                                                   Screenshot 2015-03-29 14.06.17Screenshot 2015-03-29 14.06.28
  5. Una vez guardado el siguiente paso es especificar que queremos que ocurra cuando se cumplen las condiciones que acabamos de especificar                    Screenshot 2015-03-29 14.11.39
  6. Una vez esta todo guardado, lo único que tenemos que hacer es activar el proceso Screenshot 2015-03-29 14.12.37

 

 

 

 

Et voilà! Y así funciona : 

( ya se lo que estais pensando… tengo unas cuantas tabs abiertas 😀 jajaja )

 

A tener en cuenta:

 – Una vez activado el proceso no puedes modificarlo

           – Para borrar un proceso tienes que desactivarlo primero. Una vez desactivado puedes borrarlo pero tardará unas 12 horas en ser eliminado.

 

¡Por hoy os dejo descansar!

¡Nos vemos muy muy pronto! ¡Gracias!

Trailhead – Te gusta Jugar y Aprender?

Screenshot 2015-02-07 12.52.35

Bueno bueno bueno… como se me pudo pasar no contaros antes sobre TRAILHEAD

¿Sabéis que es Trailhead?

Seguramente ya conocéis que es y seguramente ya lo habéis probado pero si no … pues os cuento.

Trailhead es una nueva plataforma interactiva de Salesforce para el aprendizaje. Es totalmente recomendable para todas aquellas personas que están empezando con Salesforce y por supuesto también para aquellas que llevan tiempo usando Salesforce ya que ayuda a recordar conceptos.

Con Trailhead puedes empezar desde algo muy básico e ir incrementado la dificultad por medio de diferentes módulos y niveles dentro de estos módulos.

Pero , no pienses que debes de seguir un orden concreto. Como comentaba Trailhead está distribuida en diferentes módulos y puedes hacer el que mas te apetezca en cada momento.

Además, por cada modulo=reto vas ganando puntos y al final, al completar el determinado modulo consigues medallas, las cuales se mostraran en tu perfil.

Luego podríamos decir que es una herramienta genial a la hora de aprender esta plataforma. Uno de los mejores recursos que he visto desde que comencé a usar Salesforce.

¡Oh! Se me olvidaba, Salesforce va creando y añadiendo nuevos módulos!!

Así que a que esperas para probarlo?

Screenshot 2015-02-07 12.50.11

Aquí teneis los links:

 

La Revolución!! El poder de @Invocable – Spring 15 (Parte 1)

Entre las cositas que nos trae esta nueva reléase, Spring ’15, una de las que más me gusta es la posibilidad de invocar métodos apex desde Process builder.

Pero lo que me gusta aún más, es que estos métodos no solamente se pueden invocar/ ejecutar desde Process Builder sino que también los puedes invocar y ejecutar usando REST APIs

Pues vamos a ver cómos se puede hacer, que debemos tener en cuenta.

Lo primero es implementar la clase que contiene nuestro @InvocableMethod. Dicha clase es una clase normal que puede ser tanto pública  o global pero debe ser externa.

Apunte: Cuando estaba probando la funcionalidad en una org pre-release me dio este error << We can’t find an action with the name and action type that you specified.>> Cambie la clase a global, guardé , volví a editar cambié de nuevo a pública y ya no me ha vuelto a pasar.

 

Una vez tenemos definida nuestra clase , pasamos a implementar los métodos que compondrán esta.

Apunte: De nuevo algo a tener en cuenta es que solamente podemos tener un método con la notación @InvocableMethod

 

¿Cómo debemos definir nuestro método invocable?

Tenemos varias opciones, pero veamos lo que ocurre en cada una de ellas:

  1. @InvocableMethod public static void myInvocableMethod() 
    • Desde Process Builder: Process builder tendrá conocimiento del método, y lo invocara cuando todo cumpla los requistos que se han especificado en dicho proceso.
    • REST calls: Y como comentaba estos métodos también podrán ejecutarse y consultarse a través de REST API, pero para ello necesitas estar definidos de una forma específica. Con lo que ¡OJO! , en esta definición no podremos ejecutar el metodo usando llamadas REST (POST call) . A través de REST API solamente podremos consultarlo ,(GET call) nos devolverá todos los metadatos del método. Algo como esto: ( si os fijaies inputs se refiere a los parametros que se le pasan al metodo y output lo que el metodo devuelve, en este caso nada )
      { "description" : null,
         "inputs" : [ ],
          "label" : "InvocableClass",
          "name" : "InvocableClass",
          "outputs" : [ ],
          "standard" : false,
          "targetEntityName" : null,
          "type" : "APEX" }
      

 

  1.  @InvocableMethod public static List<String> myInvocableMethod() 
    • Desde Process Builder: de nuevo Process Builder vuelver a reconocerlo y ejecutarlo,
    • Desde REST API: aunque si intentamos ejecutarlo a través de Rest API este no será ejecutado, sin embargo como antes tendremos la posibilidad de conseguir los metadatos.
    • Apunte: También me gustaría comentar que aunque he puesto como ejemplo List< String> , la lista puede ser de cualquier tipo. Pero eso si , necesita ser List , no puede ser directamente un tipo de dato simple, lo que quiero decir es que no es posible devolver un simple String, o quizás un Account… debe ser List<String> , List<Account>… Al final del post podreis encontrar todos los tipos permitidos tanto de entrada como de salida.
  2.  @InvocableMethod public static void myInvocableMethod(List<String> myListParam) 
    • Desde Process Builder: de nuevo podemos decir que Process builder es feliz ejecutando este método, y desde process builder podemos establecer el valor del parámetro myList. Lo interesante en este punto es que aunque process builder parece que nos deja añadir más de un parámetro veremos que no está permitido. Y de igual forma si en la definición intentamos añadir mas de un parámetro de nuevo nos darás un error porque no más de un parámetro es permitido. Aquí quizás podríamos ser un poco críticos desde el punto de vista de UX, ¿por qué me dejas añadir mas de un parámetro en UI si mas tarde no me permites guardar? … bueno podríamos argumentar varias razones, pero realmente funciona igual que cuando intentamos implementar el método con mas de un parámetro, podemos añadir todos los que queramos pero cuando llega la hora de guardar entonces es cuando salta la validación. ¿Podríamos decir que Process Builder se ha creado con mentalidad de compilador 😀 ? ( ya lo sé una broma mala 😛 )
    • REST API: De nuevo podremos como antes obtener los metadatos de este método (GET call) Pero ahora por fin tenemos luz verde!! Se invocará el método, donde la llamada será una llamada POST. Es aquií con esta notacion cuando podemos hacer consultas o crear datos , o invocar otros procesos…  El cuerpo de esta llamada será algo como esto:
        {"inputs" : [ {"myListParam" : "param1"} , {"myListParam" : "param2"} ]} 

Luego para poder llamar a estos metodo usando REST APIs necesitamos definir el metodo con un parametro de entrada , sino este no seraá invocado/ejecutado tras la llamada.

 

Lo mejor para el final, quizás lo que mas me atrae de esta funcionalidad es que dentro de los @InvocableMethod podemos hacer llamadas a APIs de otros productos, con lo que las integraciones con otros productos, aplicaciones será mucho más facil. De hecho quizas es un futuro a una nueva forma de definir  e implimentar las APIs que compondran los productos para que vivan o sean usadas dentro de @InvocableMethods.

Para terminar, he aquí el ejemplo que podría resumir este post:


global class CreateInvoicesUsingInvocable {
    @InvocableMethod
    public static List<Invoice__c> InsertInvoices(List <String> vendors) {

       List<Invoice__c> myInvoices = new List<Invoice__c>();
       for (String  invVendor: vendors)
       {
         Invoice__c inv = new Invoice__c(Vendor__c = invVendor);
         myInvoices.add(inv) ;
       }

       insert myInvoices;

       return myInvoices;
    }

  //other methods 
}

Para probar esto mismos desde workbench , usamos una llamada REST  – POST

EndPoint:  /services/data/v33.0/actions/custom/apex/CreateInvoicesUsingInvocable
Method : Post
Request Body:


{
"inputs" : [ {
"vendors" : "v1"
},
{
"vendors" : "v2"
}
]
}

Lo mejor para el final, lo que más más me ha gustado es que dentro de este @InvocableMethod podemos llamar a “terceras” APIs, con lo cual el proceso de integración podría ser mucho mucho mas sencillo.

¡La revolución! Podemos empezar a pensar en una nueva forma de implementar las APIs de nuestro productos para que estas sean usadas dentro de los nuevos y potentes @InvocableMethod , y a la vez llamadas a través de Process builder para hacer la vida más fácil a Consultores, Administradores…

Pero además para desarrolladores de aplicaciones externas que pueden llamar a estos “Cool Methods” 😀

Para terminar una pequeña lista a tener en cuenta.

  1. Solamente 1 metodod the tipo @InvocableMetod se permite por clase
  2. Estos métodos no se pueden ejecutar desde un Trigger
  3. @InvocableMethod tiene que ser static public o static global, y debe estar definido en una clase externa.
  4. Solamente 1 parametro de entrada es permitido y el tipo de este parámetro debe ser :
    1. Lista de datos primitivos o lista de listas de datos primitivos. El tipo Object no se permite.
    2. Lista de tipos Apex ( clases , datos definidos por el usuario ) que contengan la notación @InvocableVariable.
      1. Si la clase que se pasa por parámetro dentro de la lista no continue ninguna variable con esta notación, no se permite
        
        public with sharing class MyClass{
        
             @InvocableVariable
             public String myVar;
        }
        
        public with sharing class MyInvocableClass{
        
            @InvocableMethod
        
            public static void myInvocable( List<MyClass> myclasses )
        
            {
        
              //Do something
        
            }
        }
        
        
    3. List<SObject> no se permite, pero cuando no nos referimos al genérico SObject, entonces sí podemos , List<Account> sí esta permitido.
  1. Si el método nos devuelve algo, debe ser de tipo:
    1. Lista de primitivos, pero no el genérico Object
    2. Lista de un tipo especifico de SObject, como por ejemplo List<Account> pero no el genérico List<SObject>
    3. Lista de objetos definidos por el usuario, Apex type, como clases … pero no el genérico List<Object> De nuevo estas clases deben de tener al menos una variable con la notación @InvocableVariable
  2. Y algo muy bueno!! Se pueden usar @InvocableMethod dentro de packetes!! Ahora sí una vez dentro del paquete para versiones posteriores, este método no podrá ser borrado de dicho paquete.

Algunos links que podrian ser de utilidad:

Un gran y divertido ejemplo de uso por Andy

– Stackexchange 1

– Stackexchange 2

– Invocable Actions

– Invocable Methods Anotation

– REST API 

-Process Builder

 

@testSetup en Spring ’15

Seguramente ya le habéis echado un vistazo a las notas de la última Release de Salesforce, Spring ’15.

En esta reléase aparece una nueva notación @testSetup.

Esta nueva notación va asociada a métodos en los cuales serán creado los datos que mas adelante los diferentes métodos test existentes en la clase pueden utilizar durante su ejecución.

Lo que conseguimos entonces es un código mucho más limpio sin repetición de creación de datos por cada método test ejecutado, y si ya esa creación se había implementado usando un método especifico para dicha de creación de datos siendo llamado desde el método test a ejecutar, ahora esta llamada ya no será necesaria.

Podemos tener mas de un método con @testSetup en una clase, pero lo que no podemos asegurar es el orden en el que se ejecutarán. Luego debemos hacer la creación de datos independiente.

La siguiente pregunta que nos puede cruzar por la mente es volúmenes. ¿Quiere decir esto que se han incrementado?, ¿que la creación de datos no ocurre en el mismo contexto de ejecución de nuestro método test?

Esta vez tengo que decir que tristemente, la creación de datos ocurre en el mismo contexto con lo cual no hay incremento de limites.

Si queremos que estos aumenten debemos como ya hacíamos anteriormente usar Test.startTest() para ello.

Luego la mayor ventaja de esta notación es ayudarnos a tener un código más limpio, no repeticiones de código y hacer una carga de datos compatible para todos los métodos test de la clase.

A continuación os dejo una clase test y su correspondiente log para que veáis el orden de ejecución y limites.

Hasta Pronto!


@isTest

private class MyTest

{

   @testSetup

   static void createData()

   {

      System.debug('Enter in createData Method1: testSetup::'+Limits.getDMLStatements()+'::'+Limits.getLimitDMLStatements());

      Account newAccount = new Account(Name='Account1');

      insert newAccount;

      System.debug('Enter in createData Method2: testSetup::'+Limits.getDMLStatements()+'::'+Limits.getLimitDMLStatements());

   }

   static testMethod void testData1()

   {

      Test.startTest();

      System.debug('Enter in testData1::'+Limits.getDMLStatements()+'::'+Limits.getLimitDMLStatements());

      Account newAccount = [Select Name From Account];

      System.assertEquals('Account1', newAccount.Name);

      System.debug('Enter in testData12::'+Limits.getDMLStatements()+'::'+Limits.getLimitDMLStatements());

      Test.stopTest();

   }

   static testMethod void testData2()

   {

       System.debug('Enter in testData2::'+Limits.getDMLStatements()+'::'+Limits.getLimitDMLStatements());

       Account newAccount = [Select Name From Account];

       System.assertEquals('Account1', newAccount.Name);

       System.debug('Enter in testData22::'+Limits.getDMLStatements()+'::'+Limits.getLimitDMLStatements());

   }

}

El Orden de Ejecución ha sido entonces:

1. createData

2. testData1

3. testData2

Debug Log:


33.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WORKFLOW,INFO
17:14:58.094 (94312697)|EXECUTION_STARTED
17:14:58.094 (94394637)|CODE_UNIT_STARTED|[EXTERNAL]|01pB0000000HolG|myTest.createData
17:14:58.094 (94810652)|METHOD_ENTRY|[2]|01pB0000000HolG|myTest.myTest()
17:14:58.094 (94821557)|METHOD_EXIT|[2]|myTest
17:14:58.094 (94952980)|SYSTEM_METHOD_ENTRY|[7]|Limit.getDmlStatements()
17:14:58.095 (95001929)|SYSTEM_METHOD_EXIT|[7]|Limit.getDmlStatements()
17:14:58.095 (95048420)|SYSTEM_METHOD_ENTRY|[7]|String.valueOf(Object)
17:14:58.095 (95070797)|SYSTEM_METHOD_EXIT|[7]|String.valueOf(Object)
17:14:58.095 (95085582)|SYSTEM_METHOD_ENTRY|[7]|Limit.getLimitDmlStatements()
17:14:58.095 (95097514)|SYSTEM_METHOD_EXIT|[7]|Limit.getLimitDmlStatements()
17:14:58.095 (95114023)|SYSTEM_METHOD_ENTRY|[7]|String.valueOf(Object)
17:14:58.095 (95129705)|SYSTEM_METHOD_EXIT|[7]|String.valueOf(Object)
17:14:58.095 (95151621)|SYSTEM_METHOD_ENTRY|[7]|System.debug(ANY)
17:14:58.095 (95160524)|USER_DEBUG|[7]|<b>DEBUG|Enter in createData Method1: testSetup::0::150</b>
17:14:58.095 (95167447)|SYSTEM_METHOD_EXIT|[7]|System.debug(ANY)
17:14:58.096 (96543628)|DML_BEGIN|[9]|Op:Insert|Type:Account|Rows:1
17:14:58.183 (183682155)|DML_END|[9]
17:14:58.183 (183738203)|SYSTEM_METHOD_ENTRY|[10]|Limit.getDmlStatements()
17:14:58.183 (183780544)|SYSTEM_METHOD_EXIT|[10]|Limit.getDmlStatements()
17:14:58.183 (183815295)|SYSTEM_METHOD_ENTRY|[10]|String.valueOf(Object)
17:14:58.183 (183835500)|SYSTEM_METHOD_EXIT|[10]|String.valueOf(Object)
17:14:58.183 (183847113)|SYSTEM_METHOD_ENTRY|[10]|Limit.getLimitDmlStatements()
17:14:58.183 (183866985)|SYSTEM_METHOD_EXIT|[10]|Limit.getLimitDmlStatements()
17:14:58.183 (183884620)|SYSTEM_METHOD_ENTRY|[10]|String.valueOf(Object)
17:14:58.183 (183900710)|SYSTEM_METHOD_EXIT|[10]|String.valueOf(Object)
17:14:58.183 (183919237)|SYSTEM_METHOD_ENTRY|[10]|System.debug(ANY)
17:14:58.183 (183926903)|USER_DEBUG|[10]|DEBUG|<b>Enter in createData Method2: testSetup::1::150</b><b>
</b>17:14:58.183 (183932859)|SYSTEM_METHOD_EXIT|[10]|System.debug(ANY)
17:14:58.173 (183961154)|CUMULATIVE_LIMIT_USAGE
17:14:58.173|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 0 out of 100
Number of query rows: 0 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 1 out of 150
Number of DML rows: 1 out of 10000
Maximum CPU time: 0 out of 10000
Maximum heap size: 0 out of 6000000
Number of callouts: 0 out of 100
Number of Email Invocations: 0 out of 10
Number of future calls: 0 out of 50
Number of queueable jobs added to the queue: 0 out of 50
Number of Mobile Apex push calls: 0 out of 10

17:14:58.173|CUMULATIVE_LIMIT_USAGE_END

17:14:58.183 (183993101)|CODE_UNIT_FINISHED|myTest.createData
17:14:58.185 (185131883)|EXECUTION_FINISHED
17:14:58.194 (194178721)|EXECUTION_STARTED
17:14:58.194 (194189708)|CODE_UNIT_STARTED|[EXTERNAL]|01pB0000000HolG|myTest.testData1
17:14:58.194 (194429563)|METHOD_ENTRY|[2]|01pB0000000HolG|myTest.myTest()
17:14:58.194 (194439177)|METHOD_EXIT|[2]|myTest
17:14:58.194 (194581006)|SYSTEM_METHOD_ENTRY|[14]|system.Test.startTest()
17:14:58.196 (196410911)|SYSTEM_METHOD_EXIT|[14]|system.Test.startTest()
17:14:58.196 (196436313)|SYSTEM_METHOD_ENTRY|[15]|Limit.getDmlStatements()
17:14:58.196 (196533282)|SYSTEM_METHOD_EXIT|[15]|Limit.getDmlStatements()
17:14:58.196 (196565724)|SYSTEM_METHOD_ENTRY|[15]|String.valueOf(Object)
17:14:58.196 (196584800)|SYSTEM_METHOD_EXIT|[15]|String.valueOf(Object)
17:14:58.196 (196599347)|SYSTEM_METHOD_ENTRY|[15]|Limit.getLimitDmlStatements()
17:14:58.196 (196611356)|SYSTEM_METHOD_EXIT|[15]|Limit.getLimitDmlStatements()
17:14:58.196 (196627750)|SYSTEM_METHOD_ENTRY|[15]|String.valueOf(Object)
17:14:58.196 (196643767)|SYSTEM_METHOD_EXIT|[15]|String.valueOf(Object)
17:14:58.196 (196662091)|SYSTEM_METHOD_ENTRY|[15]|System.debug(ANY)
17:14:58.196 (196668968)|USER_DEBUG|[15]|DEBUG|<b>Enter in testData1::0::150</b><b>
</b>17:14:58.196 (196674433)|SYSTEM_METHOD_EXIT|[15]|System.debug(ANY)
17:14:58.196 (196828389)|SOQL_EXECUTE_BEGIN|[16]|Aggregations:0|SELECT Name FROM Account
17:14:58.207 (207955957)|SOQL_EXECUTE_END|[16]|Rows:1
17:14:58.208 (208227438)|SYSTEM_METHOD_ENTRY|[17]|System.assertEquals(ANY, ANY)
17:14:58.208 (208283470)|SYSTEM_METHOD_EXIT|[17]|System.assertEquals(ANY, ANY)
17:14:58.208 (208299244)|SYSTEM_METHOD_ENTRY|[18]|Limit.getDmlStatements()
17:14:58.208 (208328115)|SYSTEM_METHOD_EXIT|[18]|Limit.getDmlStatements()
17:14:58.208 (208360341)|SYSTEM_METHOD_ENTRY|[18]|String.valueOf(Object)
17:14:58.208 (208379606)|SYSTEM_METHOD_EXIT|[18]|String.valueOf(Object)
17:14:58.208 (208390294)|SYSTEM_METHOD_ENTRY|[18]|Limit.getLimitDmlStatements()
17:14:58.208 (208401225)|SYSTEM_METHOD_EXIT|[18]|Limit.getLimitDmlStatements()
17:14:58.208 (208417086)|SYSTEM_METHOD_ENTRY|[18]|String.valueOf(Object)
17:14:58.208 (208433053)|SYSTEM_METHOD_EXIT|[18]|String.valueOf(Object)
17:14:58.208 (208448370)|SYSTEM_METHOD_ENTRY|[18]|System.debug(ANY)
17:14:58.208 (208455254)|USER_DEBUG|[18]|DEBUG|<b>Enter in testData12::0::150</b>
17:14:58.208 (208460659)|SYSTEM_METHOD_EXIT|[18]|System.debug(ANY)
17:14:58.208 (208476092)|SYSTEM_METHOD_ENTRY|[19]|system.Test.stopTest()
17:14:58.210 (210157421)|SYSTEM_METHOD_EXIT|[19]|system.Test.stopTest()
17:14:58.200 (210186511)|CUMULATIVE_LIMIT_USAGE
17:14:58.200|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 0 out of 100
Number of query rows: 0 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 1 out of 150
Number of DML rows: 1 out of 10000
Maximum CPU time: 0 out of 10000
Maximum heap size: 0 out of 6000000
Number of callouts: 0 out of 100
Number of Email Invocations: 0 out of 10
Number of future calls: 0 out of 50
Number of queueable jobs added to the queue: 0 out of 50
Number of Mobile Apex push calls: 0 out of 10

17:14:58.200|TESTING_LIMITS
17:14:58.200|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 1 out of 100
Number of query rows: 1 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 0 out of 150
Number of DML rows: 0 out of 10000
Maximum CPU time: 0 out of 10000
Maximum heap size: 0 out of 6000000
Number of callouts: 0 out of 100
Number of Email Invocations: 0 out of 10
Number of future calls: 0 out of 50
Number of queueable jobs added to the queue: 0 out of 50
Number of Mobile Apex push calls: 0 out of 10

17:14:58.200|CUMULATIVE_LIMIT_USAGE_END

17:14:58.210 (210235275)|CODE_UNIT_FINISHED|myTest.testData1
17:14:58.211 (211251838)|EXECUTION_FINISHED
17:14:58.260 (260437301)|EXECUTION_STARTED
17:14:58.260 (260456749)|CODE_UNIT_STARTED|[EXTERNAL]|01pB0000000HolG|myTest.testData2
17:14:58.260 (260792341)|METHOD_ENTRY|[2]|01pB0000000HolG|myTest.myTest()
17:14:58.260 (260803454)|METHOD_EXIT|[2]|myTest
17:14:58.260 (260915130)|SYSTEM_METHOD_ENTRY|[24]|Limit.getDmlStatements()
17:14:58.260 (260967160)|SYSTEM_METHOD_EXIT|[24]|Limit.getDmlStatements()
17:14:58.261 (261006042)|SYSTEM_METHOD_ENTRY|[24]|String.valueOf(Object)
17:14:58.261 (261028543)|SYSTEM_METHOD_EXIT|[24]|String.valueOf(Object)
17:14:58.261 (261043955)|SYSTEM_METHOD_ENTRY|[24]|Limit.getLimitDmlStatements()
17:14:58.261 (261058175)|SYSTEM_METHOD_EXIT|[24]|Limit.getLimitDmlStatements()
17:14:58.261 (261074889)|SYSTEM_METHOD_ENTRY|[24]|String.valueOf(Object)
17:14:58.261 (261091042)|SYSTEM_METHOD_EXIT|[24]|String.valueOf(Object)
17:14:58.261 (261113424)|SYSTEM_METHOD_ENTRY|[24]|System.debug(ANY)
17:14:58.261 (261122513)|USER_DEBUG|[24]|DEBUG|<b>Enter in testData2::1::150</b><b>
</b>17:14:58.261 (261128396)|SYSTEM_METHOD_EXIT|[24]|System.debug(ANY)
17:14:58.261 (261325197)|SOQL_EXECUTE_BEGIN|[25]|Aggregations:0|SELECT Name FROM Account
17:14:58.264 (264020860)|SOQL_EXECUTE_END|[25]|Rows:1
17:14:58.264 (264157839)|SYSTEM_METHOD_ENTRY|[26]|System.assertEquals(ANY, ANY)
17:14:58.264 (264206417)|SYSTEM_METHOD_EXIT|[26]|System.assertEquals(ANY, ANY)
17:14:58.264 (264221077)|SYSTEM_METHOD_ENTRY|[27]|Limit.getDmlStatements()
17:14:58.264 (264249128)|SYSTEM_METHOD_EXIT|[27]|Limit.getDmlStatements()
17:14:58.264 (264276258)|SYSTEM_METHOD_ENTRY|[27]|String.valueOf(Object)
17:14:58.264 (264294737)|SYSTEM_METHOD_EXIT|[27]|String.valueOf(Object)
17:14:58.264 (264306009)|SYSTEM_METHOD_ENTRY|[27]|Limit.getLimitDmlStatements()
17:14:58.264 (264316703)|SYSTEM_METHOD_EXIT|[27]|Limit.getLimitDmlStatements()
17:14:58.264 (264332518)|SYSTEM_METHOD_ENTRY|[27]|String.valueOf(Object)
17:14:58.264 (264348607)|SYSTEM_METHOD_EXIT|[27]|String.valueOf(Object)
17:14:58.264 (264363269)|SYSTEM_METHOD_ENTRY|[27]|System.debug(ANY)
17:14:58.264 (264369412)|USER_DEBUG|[27]|DEBUG|<b>Enter in testData22::1::150</b>
17:14:58.264 (264374741)|SYSTEM_METHOD_EXIT|[27]|System.debug(ANY)
17:14:58.254 (264399759)|CUMULATIVE_LIMIT_USAGE
17:14:58.254|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 1 out of 100
Number of query rows: 1 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 1 out of 150
Number of DML rows: 1 out of 10000
Maximum CPU time: 0 out of 10000
Maximum heap size: 0 out of 6000000
Number of callouts: 0 out of 100
Number of Email Invocations: 0 out of 10
Number of future calls: 0 out of 50
Number of queueable jobs added to the queue: 0 out of 50
Number of Mobile Apex push calls: 0 out of 10

17:14:58.254|CUMULATIVE_LIMIT_USAGE_END

17:14:58.264 (264433109)|CODE_UNIT_FINISHED|myTest.testData2
17:14:58.265 (265477515)|EXECUTION_FINISHED

 

 

 

Flexqueue: Liberación de Batch Apex

Hace un tiempo que tuve la oportunidad de trabajar con una nueva funcionalidad aun en “piloto” que posiblemente el año que viene vea la luz ( siempre Safe Harbor 🙂 ) . Dicha funcionalidad es Flexiqueue.

¿Qué es Flexiqueue?

Flexiqueue es el nombre corto para Flexible Queue, cola flexible. Flexiqueue nos va a permitir lanza ¡más de 5 procesos Batch a la vez! Como lo lees, hasta ahora solamente podíamos tener 5 procesos Batch por Organización/ Entorno, sin embargo cuando esta nueva funcionalidad vea la luz podremos lanzar hasta un total de 100 procesos a la vez en la misma organización/entorno.

Pero importante, quizás estamos pensando en estos momentos que dichos ,100 procesos, van a entrar en proceso de ejecución, es aquí donde tengo que matizar, ya que solamente 5 procesos como hasta ahora van a ser ejecutados paralelamente y los restantes 95 se quedarán en estado de espera.

 ¿Qué podemos hacer mientras dichos 95 procesos están en estado de espera?

Como sabemos por ahora los procesos batch se ejecutan en el orden en que son lanzados ( no como @future), sabemos que el primer proceso batch que se ha llamado es el primero en entrar en ejecución ( al menos eso nos han dicho 😉 )

Pues es aquí, en esta espera que cuando … recibimos una llamada y necesitamos que uno de los proceso batch que están en espera sea el siguiente en entrar en ejecución, ¡no podemos esperar! Lo necesitamos ¡YA! , ¡Quiero lanzar mi proceso Batch! ( Exactamente igual que Jonah Jameson cuando dice “I want Spiderman!!” , pues eso nosotros queremos nuestro proceso Batch ya ya ya! )

Con Flexiqueue, podemos tenerlo, podemos re-ordenar los procesos Batch que están en espera para poner al principio de la lista el que deseamos que sea lanzado primero.

Todo ello lo haremos en una nueva UI la cual nos permitirá re-ordenar procesos Batch.

Esta nueva UI que se encuentra bajo el menu: Apex Flex Queue

Screenshot 2014-11-23 15.12.28

¿Entonces, vamos a tener dos UIs y dos tablas para representar los procesos Batch?

Sí, tendremos dos UIs , una para poder re-ordenar los procesos que se encuentran a la espera

Screenshot 2014-11-23 15.13.08

 

Y otra para poder cancelar aquellos procesos que se están ejecutando.

Screenshot 2014-11-23 15.13.29

Y no, solamente tendremos una tabla, exactamente como antes para preguntar y controlar los procesos batch: AsyncApexJobs 

¡Ojo! Como seguramente habéis notado , he dicho que puedo abortar procesos que están ejecutándose en estos momentos… entonces ¿quiere decir eso que no voy a poder parar/ abortar los procesos que están en la cola a la espera de ser ejecutados hasta que no entren en ejecución?

Como digo esto es un piloto, no se me permite abortar procesos a través de la UI pero lo que si puedo es parar/abortar cualquier proceso usando : System.abort(BatchId).

Seguramente para cuando se haga reléase de esta funcionalidad también se pueda abortar a través de UI.

¿Qué nos quedaría por pedir?

¡Queremos poder re-ordenar a través de código!! Queremos tener un método una API, algo que nos permita en nuestros desarrollos poder re-ordenar, tener el control sobre los procesos batch sin necesidad de hacer esta reordenación manualmente. Pues bien, en el piloto que trabajé no me fue posible, pero en conversaciones con Salesforce , dicha llamada/método/API esta en el “roadmap” con lo cual ¡también la tendremos!

 

… pero ¡Atención! ¡Cuidado! Con un gran poder vienen grandes responsabilidades…

 caution

Debemos tener en cuenta cuando se debe hacer dicha reordenación porque podemos caer en el error que re-ordenar y re-ordenar y re-ordenar … (hasta el infinito y mas allá ) y que alguno de los procesos batch que están en la cola nunca se ejecuten. Así que cuidado!

Además podría ocurrir que alguno de los procesos batch sea dependiente de otros en cadena y si re-ordenamos dejaríamos sin terminar un proceso que alguno de nuestros clientes necesite…

Una seria de escenarios que debemos tener en cuenta a la hora de hacer dicha reordenación.

Para terminar os dejo el link de la sesión que hice en Dreamforce donde también hay una pequeña demo de cómo Flexiqueue funciona.

¡Muchas gracias!!¡Y hasta pronto!!

 

QUEUEABLE – Regreso al @future

Anteriormente he hablado de Batch , y seguramente volveré con ello , pero hoy quiero hablar de otro proceso asíncrono al que no le tenia tanto cariño.

 ¿Conocéis @future?

Pues bien este es otro proceso asíncrono que podemos usar dentro de la plataforma Salesforce.

La gran cualidad de los procesos asíncronos es que tienen limites especiales los cuales son mayores a los procesos o transacciones síncronos.

Échale un vistazo a los Governos Limits en esta página: Governor limits

Como ves el número de registros, tanto como el tiempo , número de consultas SOQL es mayor que en una transacción síncrona.

Luego usar procesos asíncronos como @future o Batch Jobs es muy beneficiosos para algunos casos siempre y cuando no necesites el resultado .. ¡Ya!

Hoy me voy a centrar en @future y su “Futuro” la interfaz Queueable.

Pues bien, métodos @future se ejecutan cuando Salesforce tiene recursos disponibles.

Las limitaciones que existen en estos métodos son las siguientes:

  • Deben ser métodos estáticos : static
  • Solamente son de tipo void , no podemos devolver ningún otro tipo
  • No tenemos forma de saber en que estado se encuentran
  • No podemos cancelarlos
  • No son ejecutados en el orden que son llamados
  • No se pueden encadenar
  • Los parámetros que podemos usar son solamente “primitivos” , no podemos usar sObjets por ejemplo.

Esta es la nomenclatura usada :

 


public with sharing class FutureClass {

   @future
   static void myMethod(String a, Integer i) {

        System.debug('Method called with: ' + a + ' and ' + i+'... and I loved the sunny days :)');

       // Aquí va nuestro código, toda la lógica

    }

}

Es en este punto… donde aparece la ¡Interfaz Queueable!!

Que desde Winter 15 nos va a permitir:

  • Poder saber el estado de nuestro proceso asíncrono
  • Poder cancelarlo
  • Poder encadenar procesos
  • Poder usar cualquier tipo de parámetros

Y así es como se puede implementar:


public class AsyncExecutionExample implements Queueable {

   public void execute(QueueableContext context) {

      //escribo mi lógica

   }

}

Como veis el ejemplo nos muestra el método principal que debemos sobreescribir: execute

Dentro de dicho método es donde pondremos toda nuestra lógica, donde procesaremos todos los registros que necesitamos.

¿Cómo deberíamos entonces lanzar or llamar este proceso?


ID jobID = System.enqueueJob(new AsyncExecutionExample());

Entonces es aquí una vez el proceso se ha llamado podemos ver el estado en el que se encuentra haciendo una consulta a la tabla de procesos asíncronos :


AsyncApexJob jobInfo = [SELECT Status,NumberOfErrors FROM AsyncApexJob WHERE Id=:jobID];

Para encadenar procesos asíncronos de tipo Queueable solamente hemos de hacerlo dentro de nuestro execute:

 


public class AsyncExecutionExample implements Queueable {

   public void execute(QueueableContext context) {

      // Your processing logic here

      // Chain this job to next job by submitting the next job

       System.enqueueJob(new SecondJob());

       // o quizás hacer una llamada recursiva a nosotros mismos

   }

}

 

No olvidemos que debemos hacer Unit test a nuestro código con lo que como pasaba con @future , la llamada la haciemos entre las etiquetas Test.startTest()/ Test.stopTest() , en Queueable aún debemos seguir haciéndolo.

Quedaría de la siguiente manera:


@isTest

public class AsyncExecutionExampleTest {

    static testmethod void test1() {

          // startTest/stopTest block to force async processes

          //   to run in the test.

          Test.startTest();

          ID jobID = System.enqueueJob(new AsyncExecutionExample());

          Test.stopTest();

          // Validate that the job has run

          AsyncApexJob jobInfo = [SELECT Status,NumberOfErrors FROM AsyncApexJob WHERE Id=:jobID];

          System.assertEquals('Completed', jobInfo.Status);

          System.assertEquals(0, jobInfo.NumberOfErrors);

      }

}

Y para finalizar un pequeño ejemplo para hacer llamadas externas usando Queueable

public class MyQueueable implements Queueable,Database.AllowsCallouts  {
    public void execute(QueueableContext context) {
        
         HttpRequest req = new HttpRequest();
         req.setMethod('GET');
         req.setHeader('Content-Type', 'application/json');
         req.setEndPoint('https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=API_KEY');
         req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
         HttpResponse response = new Http().send(req);
         ID jobID = System.enqueueJob(new AsyncExecutionExample());
   }
}

Hemos visto lo que ganamos cuando usamos Queueable, pero cuales son sus limitaciones:

  • Podemos encadenar solamente 2 procesos, no podemos encadenar hasta el “Infinito y más allá”

Entonces la siguiente pregunta sería cuando usar Queueable en lugar de Batch, pues bien, podríamos decir que usaremos Queueable cuando el volumen de registros a procesar sea mayor del permitido en un proceso síncrono pero sea proporcionalmente menor que el tamaño máximo de un proceso Batch, digamos para procesos asíncronos que solamente sean necesarios ejecutarse una vez o como máximo 2 , si no entonces necesitaremos implementar un proceso Batch. 🙂

 

 

 

 

 

Dreamforce 14 – Si tienes la oportunidad de ir, ¡No te lo pierdas!

Todos aquellos que trabajamos con la plataforma Salesforce conocemos uno de los mayores eventos que ocurren una vez al año, este es obviamente ¡Dreamforce!

Pues si tenéis la posibilidad de poder asistir no os lo debéis de perder.

No es solamente para clientes, ¡los Desarrolladores somos importantes en Dreamforce!

Te lo creas o no, el mito de dejarnos encerrados en un cuarto oscuro rodeados por nuestros monitores y ordenadores ha desaparecido. ( Aunque bueno, he de decir que yo en casa mi sitio de trabajo esta en el rincón oscuro de la habitación donde tengo mi pantalla y trabajo tan agusto)

Tenemos en Dreamforce una zona dedicada que se llama DEVZONE! Donde puedes ir a workshops, hablar con otros desarrolladores, asistir a sesiones sobre funcionalidad de la plataforma en la que otros han estado trabajando, ver nueva funcionalidad que viene en futuras releases y como se puede usar, puedes poner manos a la obra y construir una pequeña aplicación en uno de los talleres, puedes irte a la biblioteca y coger libros sobre Salesforce que por supuesto puedes llevarte contigo y prestar entre tus compañeros, puedes participar en actividades y llevarte de regalo alguna camiseta, puedes participar en actividades de caridad … realmente son 4 días llenos de movimiento.

Ahora bien , no solamente DevZone, podemos decir que otra de las cosas que realmente disfrutaras en Dreamforce es de la oportunidad de conocer a gente que como tu esta usando la plataforma, poder charlar con ellos y compartir puntos de vista. Pero no solamente gente que trabaja con la plataforma si no a la propia gente de Salesforce, como por ejemplo evangelistas. Están allí entre nosotros repartiendo sabiduría, te puedes acercar preguntarles y te puedo asegurar que te van a ayudar en todo lo que puedan. Realmente vas a sentir que eres parte de la plataforma, no solamente verla desde fuera. Porque aunque una comunidad grande es la de Salesforce, siempre puedes sentir una gran acogida.

Tranquilo, tranquilo seguramente estas pensando que esto es todo, ¡No! Aun queda mucho, mucho más. Acabo de hablar de que puedes aprender, conocer a la comunidad y todo ello además de poder divertirte.

Imagínate una ciudad parada para dar la acogida a unos de los eventos mas importantes como es Dreamforce. San Francisco se llena de gente, y quizás pienses que puedes llegar a perderte? Para nada! Hay gente que durante Dreamforce se dedican a ayudarte para encontrar el centro de conferencias, diferentes eventos y lugares, horarios y mucho mas. Porque no puedes perderte el principal Keynote, no puedes perderte todas las conferencias y por supuesto para relajarte tras un día lleno de agitación Salesforce también se encarga de darte un respiro para que te despejes te invitan a un concierto.

Y por supuesto si eres mujer sabrás que en este mundo tecnológico, somos un numero menor pero en Dreamforce tenemos eventos especialmente dedicados a la mujer en tecnología.

Es muy seguro que muchas cosas que se me hayan pasado, pero realmente es una oportunidad única, y por eso, si podéis no faltéis.

Empieza a concienciar también a tu empresa, no solamente para el que asiste es una gran oportunidad sino para las compañías, ya que ver los diferentes productos que existen, y aplicaciones que están actualmente en el mercado es muy beneficioso, y de nuevo insistir para desarrolladores es una oportunidad única de la cual aprender y beneficiarse para poder aplicar todos los nuevos conceptos en tu trabajo diario.

Os dejo algunos links de lo que podéis encontrar en DF:

Conocer a la comunidad

Conocer a otros organizadores de grupos (encuentra los mas cercanos)

Developers – DevZone

Dreamforce Sessions

DF 13, cómo lo viví

 

Y tú, ¿cómo “Schedule-as” ?

Esta pasada semana tuve una conversación relacionada con “Schedule” Jobs. Creo que si conoces Salesforce quizás estés pensando en estos momentos:

  • Opción 1: Ah ya! Te refieres a cuando se programa para ser ejecutado un proceso Batch
  • Opción 2: No hombre! Te refieres a cuando se programa para que se ejecute una clase
  • Opción 3: Que si! Que te estoy diciendo que es cuando se programa para que se ejecute un proceso batch y lo haces a través de “Developer Console”
  • Opción 4: Que no, que no, que lo que yo digo es programar un proceso cualquiera que no sea Batch para que se ejecute a una hora determinada y lo hago a través de UI

Ya veis por donde va la conversación. Parecía no estar muy claro el concepto y las posibilidades. Pues vamos a verlo

En parte todas las opciones, respuestas son correctas.

Veamos poco a poco todos los casos. Empecemos por:

Apex Scheduler

 Pues este se usa para ejecutar clases a una hora especifica. Y cuando digo clases me refiero a una determinada clase que implemente la interfaz Schedulable. Ahora dentro de esta clase puedes tener un simple código que por ejemplo cree 24 nuevos registros cada día para que así haga referencia a las nuevas 24 horas del día siguiente, o quizás 168 para la semana. Si el proceso es pequeño no tiene sentido llamar a un Batch para que sea ejecutado, seria una mala utilización de los recursos.

Aunque si lo que deseas y procesar todas las cuentas de tu organización y actualizarlas con un nuevo valor quizás Batch sea lo que buscas para no tener problemas de limites.

Luego con Apex Scheduler puedes ejecutar a una determinada hora procesos Batch y procesos que no son Batch.

La clase tendrá esta estructura:


global class ScheduleApexClass implements Schedulable
{
    global void execute(SchedulableContext ctx)
   {

      //aquí es donde se ejecutara lo que desees hacer

   }
}

¿Cómo se programan las ejecuciones?

Hay varias opciones también: ( siempre como Administrador del sistema o con los adecuados permisos)

  1. Puedes programar la ejecución desde la pantalla donde se encuentran todas las clases Apex de tu organización : Setup–> Develop–> Apex Clases.

Screenshot 2014-08-17 09.01.38 Screenshot 2014-08-17 09.02.40

Como ves aquí solamente lo que hacemos es ponerle un nombre a nuestro proceso programado, asociarlo con la clase correspondiente que implementa la interfaz Schedule y el día/hora que queremos se ejecute

  1. La segunda forma es a traves de Developer Console, usando esta línea de código que usa el método System.Schedule:

System.Schedule( “Nombre del proceso”,”cuando”,instancia_de_clase);

Algo como esto:


  ScheduleApexClass s = ScheduleApexClass();

  String sch= ‘0 0 8 13 2 ?’;

  System.Schedule(‘Programado’, sch, s);

A todo esto… ahora qué pasa, ¿Dónde se van mis procesos programados? ¿Cómo los controlo?

 Desde Monitoring–>Schedule Jobs. Aquí es donde aparecen estos procesos programados para que se ejecuten de forma repetitiva en el momento que hemos determinado.

Pero además!! Aquí es donde posiblemente estaba la confusión. Cuando se programa un proceso para que sea ejecutado de forma recurrente, también se crea una entrada en la tabla AsyncApexJob . Dicha tabla es la que muestra procesos Batch.

Importante!! — Sin embargo nuestro proceso que hemos programado puede que sea Batch o puede que no.

Pero el sistema guardara un registro siempre en dicha tabla para saber que tiene que lanzar dicho proceso de forma recurrente y siempre estará en estado “Queued”

Para ver dichos procesos solamente has de dirigirte a Monitoring–>Apex Jobs

Y todo esto era la parte de Apex Scheduler.

 

Pero la segunda parte es:

Programar un proceso batch directamente!!

Para ello podrías usar la versión que acabamos de ver, donde dentro de la clase hacemos la llamaba a nuestra clase Batch


global class ScheduleApexClass implements Schedulable{

    global void execute(SchedulableContext ctx)
    {
       MyBatch b = new MyBatch();
       Database.executeBatch(b,1);
    }
}

Esto lo que hará entonces es ejecutar mi proceso Batch definido en MyBatch, de forma repetitiva recurrente cada vez que yo lo haya especificado a la hora de lanzarlo ( con System.Schedule o a través de UI)

Pero quizás yo no quiera eso, lo que yo quiero es que mi proceso batch sea ejecutado una sola vez y a una hora determinada.

Para ello lo que debo hacer es solamente ejecutar el método a través de Developer Console por ejemplo:

System.ScheduleBatch( batchClass, “nombre del proceso”, intervalo de tiempo para que sea lanzado)

Para este sistema la clase que usamos es una simple clase Batch, no necesita que implemente la interfaz Schedulable ( pero obviamente implementará la interfaz Database.Batchable)

Luego seria algo como esto:


   MyBatch mb = new MyBatch();

   String b1ID = System.scheduleBatch(mb, 'My Batch', 5);

Y lo que tengo es que en  5 minutos mi clase batch será lanzada.

Si te vas Monitoring–> Schedule Jobs, versa que hay un Nuevo registro pero sin embargo no existe Nuevo Registro hasta que se ejecute el proceso Batch en 5 minutos en la table de AsyncApexJobs.

Esta es otra de las diferencias de programar un Batch directamente.

Espero que haya servido de ayuda. Hasta pronto!!

 

Workshop – Primeros pasos en Salesforce

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:

Screenshot 2014-07-01 11.49.02

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) .

Screenshot 2014-07-01 11.49.15

 

Y también vemos en la parte derecha superior nuestro nombre, pues es aquí donde podremos configurar nuestro perfil, nuestra cuenta.

Screenshot 2014-07-01 11.49.38

Seleccionamos la opción My Profile

Screenshot 2014-07-01 11.50.00 Screenshot 2014-07-01 11.50.15

 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

Screenshot 2014-07-01 11.50.42 Screenshot 2014-07-01 11.50.55

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 :

Screenshot 2014-07-01 11.52.31

 

Hola Mundo! clickamos en Share y nuestro post ya es publico.

Screenshot 2014-07-01 11.52.37

¿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&#8230;

Screenshot 2014-07-01 16.32.06

– Lo sustituyo por : http://emea.salesforce.com/one/one.app

Screenshot 2014-07-01 16.34.47

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:

Screenshot 2014-07-01 11.51.51 Screenshot 2014-07-01 11.51.41Screenshot 2014-07-01 11.52.51

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.

Screenshot 2014-07-01 11.53.13

Screenshot 2014-07-01 17.01.33

 

Nos movemos al menú de la izquierda, y debajo del menú Create hay otro que se llama Objects es aquí donde nos dirigimos.

 Screenshot 2014-07-01 11.53.40

Y obviamente lo que vamos a hacer es crear uno nuevo con lo que presionamos el botón New.

Screenshot 2014-07-01 11.55.51

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

Screenshot 2014-07-01 11.56.15

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 )

Screenshot 2014-07-01 11.56.35

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.

Screenshot 2014-07-01 11.56.57       Screenshot 2014-07-01 11.57.09

 

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().

Screenshot 2014-07-01 11.57.42

 

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.

Screenshot 2014-07-01 11.57.53

Screenshot 2014-07-01 11.58.06

 

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

Screenshot 2014-07-01 12.02.32 Screenshot 2014-07-01 12.02.14

Screenshot 2014-07-01 12.02.56

 

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.

 

Screenshot 2014-07-01 12.04.12 Screenshot 2014-07-01 12.04.19

 

Llamo Tabs, a las pestañas.

Screenshot 2014-07-01 12.04.06

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.

Screenshot 2014-07-01 12.06.56 Screenshot 2014-07-01 12.07.19

Con lo que ahora aparecerá en el menú superior de pestañas la nuestra, asociada a Jornada.

Screenshot 2014-07-01 12.07.55

Pues vamos a ver lo que hay en esta nueva pestaña.

Screenshot 2014-07-01 12.08.07

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.

Screenshot 2014-07-01 12.08.18

Voy a pulsar sobre el botón nuevo y obtengo la siguiente pantalla

Screenshot 2014-07-01 12.08.29

Aquí es donde puedo crear un nuevo registro, solamente he de rellenar los campos necesarios y opcionales en este caso.

Screenshot 2014-07-01 12.08.51

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.

Screenshot 2014-07-01 12.09.13

 

Esta pantalla, es donde podemos arrastrar los campos que queremos que aparezcan o no, y de igual forma podemos modificar el orden.

Screenshot 2014-07-01 12.09.46

Volviendo entonces a nuestro registro una vez modificado el Layout / la apariencia así es como queda.Screenshot 2014-07-01 12.10.14

¿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.

Screenshot 2014-07-01 12.10.51

Lo dicho, voy a crear un nuevo campo, pero en este caso es de tipo Picklist

Screenshot 2014-07-01 12.12.02

 

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.

Screenshot 2014-07-01 12.13.05Ya tengo en mi objeto en total 3 campos.

Screenshot 2014-07-01 12.14.01

 

Vuelvo a mi tab de Jornadas y creo un nuevo registro, Veo que por defecto el valor de mi nuevo campo es Entrar.

Screenshot 2014-07-01 12.14.16 Screenshot 2014-07-01 12.14.43

Screenshot 2014-07-01 12.14.55

Sin embargo, si vuelvo al registro que había creado antes mi nuevo campo no tiene valor, se lo tengo que dar manualment.Screenshot 2014-07-01 12.15.02

Screenshot 2014-07-01 12.15.12

Screenshot 2014-07-01 12.15.26

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.

Screenshot 2014-07-01 12.15.35

Selecciono la opción Show More

 

Screenshot 2014-07-01 12.15.42

Selecciono Jornadas Laborales

Screenshot 2014-07-01 12.15.50

Desde aquí puedo crear nuevos registros también!!!

Screenshot 2014-07-01 12.16.39 Screenshot 2014-07-01 12.16.58

De igual manera puedo ver los registros que se han creado anteriormente

Screenshot 2014-07-01 12.17.20 Screenshot 2014-07-01 12.17.44

 

¿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

Screenshot 2014-07-01 12.18.40Screenshot 2014-07-01 12.34.28

 

¿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

Screenshot 2014-07-01 12.35.30 Screenshot 2014-07-01 12.35.58

 

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 

Screenshot 2014-07-01 12.37.16 Screenshot 2014-07-01 12.37.23

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.

 

Screenshot 2014-07-01 12.37.54

 

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.

 

Screenshot 2014-07-01 12.38.06

 

Luego mi acción está creada.

Screenshot 2014-07-01 12.38.37

 

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)

Screenshot 2014-07-01 12.41.17

Por defecto existe un Global Publisher Layout, pues en este ejemplo vamos a modificar el existente, aunque tambien es posible crear nuevas vistas/ layouts.

Screenshot 2014-07-01 12.41.24

 

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)

Screenshot 2014-07-01 12.41.43

Screenshot 2014-07-01 12.41.58

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.

Screenshot 2014-07-01 12.42.13

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.

Screenshot 2014-07-01 12.42.33

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.
Screenshot 2014-07-01 12.42.41

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.

Screenshot 2014-07-01 12.42.55

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.

Screenshot 2014-07-01 12.43.11

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.

Screenshot 2014-07-01 12.43.24 Screenshot 2014-07-01 12.44.57

También en cualquier momento podríamos editar dichos registros.

Screenshot 2014-07-01 12.45.14 Screenshot 2014-07-01 12.45.25

¿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.

Screenshot 2014-07-01 12.46.38 Screenshot 2014-07-01 12.46.49

 

Seleccionamos New, y pasamos a crear una nueva acción, que en este caso el tipo será: Update Record

Screenshot 2014-07-01 12.47.01

 

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.

Screenshot 2014-07-01 12.47.25

Screenshot 2014-07-01 12.48.27

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.

Screenshot 2014-07-01 12.48.41

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.

Screenshot 2014-07-01 12.48.55

 

Una vez sobre escrito podremos modificar y añadir las acciones que deseemos aparezcan en los registros de este tipo.Screenshot 2014-07-01 12.49.25

 

Para nuestro ejemplo la acción Salida será la primera de la lista.

Screenshot 2014-07-01 12.49.37

Si ahora nos vamos a cualquiera de los registros de tipo Jornada, podremos comprobar que nuestra acción no aparece.

Screenshot 2014-07-01 12.50.27

¿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.

Screenshot 2014-07-01 12.50.41 Screenshot 2014-07-01 12.50.56 Screenshot 2014-07-01 12.51.41 Screenshot 2014-07-01 12.57.20

Ahora podemos volver a los registros de tipo Jornada y ver que nuestra acción aparece.

Screenshot 2014-07-01 12.57.32

 

Aparecerá también en el dispositivo móvil sin necesidad de ninguna implementación extra.

Screenshot 2014-07-01 12.57.46 Screenshot 2014-07-01 12.57.52 Screenshot 2014-07-01 12.58.00

Vemos que es posible por lo tanto modificar el valor del picklist y que nuestro registro se actualice.

Screenshot 2014-07-01 14.15.51

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

 

Screenshot 2014-07-01 14.24.39

Screenshot 2014-07-01 14.24.48

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:

Screenshot 2014-07-01 14.25.36

Screenshot 2014-07-01 14.25.57

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.

Screenshot 2014-07-01 14.26.17

Screenshot 2014-07-01 14.26.47

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 );
        
        
    }
}

Screenshot 2014-07-01 14.27.18

Screenshot 2014-07-01 14.27.33

Si hubiese algún problema en el código al guardar te daria un fallo con el que puedes dirigirte al error y solucionarlo.

Screenshot 2014-07-01 14.30.12

¿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.

Screenshot 2014-07-01 14.32.49

Screenshot 2014-07-01 14.32.55

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

Screenshot 2014-07-01 14.30.50

 

Creo una nueva Jornada para nuestro ejemplo

 

Screenshot 2014-07-01 14.31.00 Screenshot 2014-07-01 14.31.09

 

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.

Screenshot 2014-07-01 14.31.21 Screenshot 2014-07-01 14.31.31 Screenshot 2014-07-01 14.31.41

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.

Screenshot 2014-07-01 14.31.50

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…

Screenshot 2014-07-01 14.32.05

… hacia el “primer” puntito lo que obtengo es la vista del chatter asociado a ese objeto, su feed. Si me muevo hacia la derecha…Screenshot 2014-07-01 14.32.20

… 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,
Screenshot 2014-07-01 14.32.30

 

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

Screenshot 2014-07-01 14.34.01

Seleccionamos Custom App

Screenshot 2014-07-01 14.34.24

Y pasamos a darle nombre

Screenshot 2014-07-01 14.35.40

Podemos seleccionar un icono, imagen que se asocie a nuestra app una vez que sea seleccionada en el menú.

Screenshot 2014-07-01 14.35.47

 

Y ahora lo que haremos será escoger que tabs queremos que formen parte de nuestra app.

Screenshot 2014-07-01 14.36.14

 

Seguidamente decidimos la visibilidad de nuestra app para los diferentes perfiles existentes en la org.

 

Screenshot 2014-07-01 14.36.24

Y una vez guardada, podemos comprobar que ahora nuestra app aparece en el menú superior derecho.

Screenshot 2014-07-01 14.36.39

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 + )Screenshot 2014-07-01 14.36.49

 

 

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

Screenshot 2014-07-01 14.37.20

 

Una vez aquí puedo crear un nuevo perfil, presiono el botón New Profile.

Screenshot 2014-07-01 14.37.35

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.

 

Screenshot 2014-07-01 14.37.53

Comprobamos ahora que tiene los permisos necesarios que deseamos para nuestra aplicación.

Screenshot 2014-07-01 14.38.24

Que nuestra app sea visible

 

Screenshot 2014-07-01 14.38.46

 

Que nuestra Tab sea visible

Screenshot 2014-07-01 14.39.16

 

Que nuestro objeto tenga los permisos necesarios.

Screenshot 2014-07-03 09.41.09

 

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

Screenshot 2014-07-01 14.40.15

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.

 

Screenshot 2014-07-01 14.40.30

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

 

 

Screenshot 2014-07-01 14.41.08

Screenshot 2014-07-01 14.41.44

Repetimos este paso hasta que todos los elementos estén dentro del paquete.

Screenshot 2014-07-01 14.41.33 Screenshot 2014-07-01 14.42.08

Screenshot 2014-07-01 14.42.38

Screenshot 2014-07-01 14.42.51 Screenshot 2014-07-01 14.44.30 Screenshot 2014-07-01 14.44.57 Screenshot 2014-07-01 14.45.32 Screenshot 2014-07-01 14.46.19

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 

Screenshot 2014-07-01 14.46.31

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

 Screenshot 2014-07-01 14.47.18

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.

Screenshot 2014-07-01 14.47.32

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.

Screenshot 2014-07-01 14.47.49

Screenshot 2014-07-01 14.48.06

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!!