29/10/07

Prefiltrado en infomes

Aquí va una pequeña advertencia y recordatorio - sobre todo para mi mismo - acerca del prefiltrado de informes en CRM. Esta característica es muy, muy útil, ya que te permite reutilizar un mismo informe de muchas maneras, tan sólo cambiando el filtrado. Este prefiltrado se gestiona desde CRM - no desde el diseño del informe - a través de la interfaz habitual de búsqueda avanzada. De esta forma, un informe de Incidentes cerrados la última semana se puede convertir en Incidentes cerrados para clientes con más de 500 empleados el último año o cualquier otra cosa que nos permita hacer la búsqueda avanzada.

En principio lo unico que hay que hacer (cito el MOC de personalización) es ponerle a las entidades que queramos prefiltrar un alias que comience por CRMAF_ . En el siguiente ejemplo, he escogido como alias CRMAF_FI para representar los casos, o sea la vista FilteredIncident.


SELECT CRMAF_FI.ticketnumber, CRMAF_FI.customeridname
FROM FilteredIncident AS CRMAF_FI


Este informe permitirá prefiltrar por atributos del caso. Lo podemos complicar un poco más añadiendo la tabla de cuentas:


SELECT CRMAF_FI.ticketnumber, CRMAF_FA.name
FROM FilteredIncident AS CRMAF_FI
INNER JOIN FilteredAccountt AS CRMAF_FA
ON FilteredIncident.customerid = FilteredAccount.accountid


Esto nos permitirá prefiltrar por caso y/o cuenta. Bueno, pues aquí viene la advertencia: el editor de informes integrado en Visual Studio añade delante de cada vista o tabla un dbo. que hace que deje de funcionar el prefiltrado. Por ahora, se lo tengo que quitar a mano con un Reemplazar, porque no he encontrado la opción para evitar este comportamiento.

25/10/07

Crear un contrato por código

Admitámoslo, crear un contrato en CRM es una pesadez. Al menos eso es lo que dicen algunos de los usuarios del módulo de servicio que conozco. Hay que seleccionar el cliente, crear el contrato, establecer la plantilla de contrato, fechas de inicio y finalización (cuatro en total), las direcciones (dos), añadir la línea de contrato, volver a poner las fechas de inicio y finalización, establecer el precio...

Bueno, pues todo esto es lo que hace este tocho código que véis aquí debajo. No es perfecto, pero puede ahorrar algo de tecleo.

public static Guid CrearContrato(
Guid idCuenta,
Guid idPlantillaContrato,
int numeroMinutos,
int importe)
{
// Buscar los datos del cliente
ColumnSet cs = new ColumnSet();
cs.Attributes = new string[] { "name", "address1_addressid" };
account cuenta =
crm.Retrieve(EntityName.account.ToString(), idCuenta, cs) as account;

// Buscar los datos de la plantilla
contracttemplate plantilla =
crm.Retrieve(EntityName.contracttemplate.ToString(), idPlantilla,
new AllColumns());

// Crear un título para el contrato
string tituloContrato = String.Format(
"{0} - {1} {2} min ({3:MM/yy})",
cuenta.name, plantilla.name, numeroMinutos, DateTime.Now);

// Establecer fechas (contrato de 10 años de duración)
string fechaInicio =
DateTime.Now.ToString("yyyy/M/ddT00:00:00");
string fechaFin =
DateTime.Now.AddYears(10).ToString("yyyy/M/ddT00:00:00");

// Crear el contrato
contract contrato = new contract();
contrato.contracttemplateid = new Lookup();
contrato.contracttemplateid.Value = idPlantillaContrato;
contrato.customerid = new Customer();
contrato.customerid.Value = idCuenta;
contrato.customerid.type = EntityName.account.ToString();
contrato.billingcustomerid = contrato.customerid;
contrato.activeon = new CrmDateTime();
contrato.activeon.Value = fechaInicio;
contrato.expireson = new CrmDateTime();
contrato.expireson.Value = fechaFin;
contrato.billingstarton = new CrmDateTime();
contrato.billingstarton.Value = fechaInicio;
contrato.billingendon = new CrmDateTime();
contrato.billingendon.Value = fechaFin;
contrato.serviceaddress = new Lookup();
contrato.serviceaddress.Value = cuenta.address1_addressid.Value;
contrato.billtoaddress = new Lookup();
contrato.billtoaddress.Value = cuenta.address1_addressid.Value;
contrato.title = tituloContrato;
Guid idContrato = crm.Create(contrato);

// Crear la línea de detalle del contrato
contractdetail linea = new contractdetail();
linea.title = "Linea " + tituloContrato;
linea.contractid = new Lookup();
linea.contractid.Value = idContrato;
linea.activeon = new CrmDateTime();
linea.activeon.Value = fechaInicio;
linea.expireson = new CrmDateTime();
linea.expireson.Value = fechaFin;
linea.customerid = new Customer();
linea.customerid.type = EntityName.account.ToString();
linea.customerid.Value = idCuenta;
linea.totalallotments = new CrmNumber();
linea.totalallotments.Value = numeroMinutos;
linea.price = new CrmMoney();
linea.price.Value = importe;
Guid idLinea = crm.Create(linea);

return idContrato;
}

4/10/07

Accediendo a CRM con ASP.NET 2.0

Se puede (?) ver aquí debajo mi primera prueba de video blogger sobre programación.




Se trata de llamar a los web services de CRM a través de un ObjectDataSource, que permite encapsular los comandos SELECT, UPDATE, INSERT y DELETE típicos en un objeto de lógica de negocio. Para el caso, se han mapeado a los métodos Retrieve, Update, Create y Delete del CrmService. Luego se muestran los resultados con DataBinding a un control DetailsView, que permite ver todos los campos de un registro.

23/7/07

Llamar a web services de CRM desde JavaScript

En este thread de las news de CRM en español firmado por Nuria he encontrado un enlace a un blog donde se cuenta una alternativa cómoda para llamar a los web services de CRM desde JavaScript en cliente. En concreto, se trata de cómo ocultar/mostrar campos de un formulario en función de los roles a los que pertenece el usuario.

La entrada de blog es de Ronald Lemmen y se encuentra aquí.

Muy chulo (¡pero ojo, no soportado!)

25/5/07

Autonumeración "a la carta" en CRM

Todos hemos visto cómo Microsoft CRM genera códigos automáticos para unos pocos tipos de registros: Contratos, Ofertas, Pedidos, Facturas y artículos de la Knowledge Base. ¿Qué podemos hacer si necesitamos que, además de estas entidades, alguna otra se autonumere también? Pues recurrir a la personalización del formulario correspondiente haciendo que llame a un pequeño servicio que programaremos en el servidor.

Empezaremos por programar el servicio de autonumeración. Abrimos Visual Studio y generamos un proyecto web sobre la misma carpeta en la que tenemos el servidor de CRM. En este caso, la he llamado autonum.



Para que nuestro proyecto web conecte con el servidor de CRM, sólo tenemos que añadir una referencia Web a su Web service, que como sabéis está en

http://maquina:puerto/MSCRMServices/2006/crmservice.asmx


Le damos un nombre apropiado a la referencia, por ejemplo CrmSdk.



Ahora, creamos un nuevo WebForm y le damos el nombre default.aspx Esta será la página que nos proporcionará los números correlativos para nuestra entidad. Aquí tenéis el código:


void Page_Load(object sender, System.EventArgs e){
string entidad = Request["e"];
string campo = Request["c"];
string prefijo = Request["p"];
string numCars = Request["n"];
string formato = "{0}{1:D" + numCars + "}";
string anterior = UltimoCodigo(entidad, campo);
string parteNumerica = anterior.Substring(prefijo.Length);
int num = 1 + Int32.Parse(parteNumerica);
Response.Write(String.Format(formato, prefijo, num));
}

string UltimoCodigo(string entidad, string campo) {
string fetch = @"<fetch count='1' mapping='logical'>
<entity name='{0}'>
<order attribute='createdon' descending='true'/>
<attribute name='{1}'/>
</entity>
</fetch>";
fetch = String.Format(fetch, entidad, campo);
CrmService crm = new CrmService();
crm.Credentials = System.Net.CredentialCache.DefaultCredentials;
string res = crm.Fetch(fetch);
XmlDocument dom = new XmlDocument();
dom.LoadXml(res);
XmlNode nodoCuenta = dom.SelectSingleNode("//" + campo);
if (nodoCuenta != null)
return nodoCuenta.FirstChild.Value;
else
return "?";
}



Como véis, lo único que hace es lanzar una consulta FetchXml que busca la última entidad creada (ordenando por el campo createdon) y extrae su código, que puede ser cualquier atributo que escojamos. En el ejemplo, la entidad es account y el campo accountnumber.

Ahora tenemos que personalizar el evento OnLoad de la entidad para que, al cargarse un nuevo formulario, se conecte a nuestra página en el servidor y rellene su valor en el campo que hayamos escogido. Vamos a Configuración - Personalización y escogemos la entidad Cuenta. Luego, escogemos su formulario y abrimos sus propiedades:




El código Javascript utiliza una conexión al estilo AJAX con el objeto XmlHttp. Como la llamada es asíncrona, no bloqueará el formulario para la introducción de otros datos. Aquí tenéis el código:

if (crmForm.FormType != 1)
return;
var entidad = "account";
var campo = "accountnumber";
var prefijo = "A-";
var numCars = 4;
var xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
var params = "?e=" + entidad +"&c=" + campo + "&p=" + prefijo + "&n=" + numCars;

xmlHttp.open("GET", "/autonum" + params, true);
xmlHttp.setRequestHeader('Content-Type', 'text/xml');
xmlHttp.send('');

xmlHttp.onreadystatechange=function() {
if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
{
var objDoc = xmlHttp.responseText;
if( xmlHttp.responseText.length > 0 )
crmForm.accountnumber.DataValue = xmlHttp.responseText;
}
}

A continuación, publicamos las personalizaciones y nos vamos a la lista de cuentas.




La última cuenta cread tiene el código A-0126. Hemos configurado los parámetros de la llamada para que considere el prefijo A- y utilice 4 dígitos en el código. Cuando le damos al botón Nuevo ...



Aparece la ventana de creación de nuevas cuentas, pero esta vez con el siguiente número de cuenta rellenado automáticamente. ¡Conseguido!

18/5/07

Cambiar la vista asociada por defecto

Os redirijo al blog de un colega que ha traducido y adaptado un artículo de stunnware donde se explica cómo cambiar la vista asociada por defecto de cualquier entidad de CRM. Por ejemplo, que muestre todas las actividades del historial de un cliente en lugar de sólo las de los últimos 30 días.

Inglés (Stunnware): http://www.stunnware.com/crm2/topic.aspx?id=JS11
Castellano (Jordi Bilbao): http://bloqjb.blogspot.com/2007/04/cambiar-la-vista-por-defecto-crm-30.html

Es una personalización no soportada, ojo.

23/4/07

Buscador fuentes CRM y gadget

He encontrado esta aplicación que te permite lanzar búsquedas sobre CRM desde el Internet Explorer 7, como una fuente más de información. También incluye un gadget de Vista para lanzar la búsqueda desde el escritorio. Muy chulo.

http://blogs.msdn.com/joris_kalz/pages/Vista-Microsoft-CRM-Search-Gadget.aspx

11/4/07

Cambiar la configuración de correo

Tanto entrante como saliente, si después de instalar CRM necesitamos por el motivo que sea cambiar el servidor de Exchange.

http://support.microsoft.com/kb/906471/

Encontrado en la KB de Microsoft.

10/4/07

Campañas de marketing de correo electrónico

Aquí van un par de trucos del lado oscuro de CRM: el módulo de marketing. Cuando tratamos de distribuir una actividad de campaña de marketing basada en el canal e-mail, podríamos esperar que CRM enviara los correos inmediatamente, ¿no? Pues no. En su lugar, lo que hace es crear actividades de correo electrónico que quedan abiertas y pendientes de enviar. Como no existe el botón Enviar en plan masivo, no queda más remedio que ir abriendo y enviando los correos uno por uno, lo que no parece ser de recibo.

Visto el problema, y a partir del Rollup 1 de CRM, se puede cambiar una clave en el registro que modifica este comportamiento. Si creamos una clave de tipo DWORD en la dirección

HKLM\Microsoft\MSCRM\CampaignSendEmail

y le ponemos el valor 1, el resultado de distribuir la actividad será el envío directo de los correos. En principio, y para unos pocos destinatarios, funciona. ¿Por qué esta precaución? He visto en este artículo de las news que hay gente a la que le ha fallado de forma aleatoria, presuntamente por incluir un enlace mailto: en el cuerpo del mensaje.

4/4/07

Luchando contra las Actividades de Servicio

Luchando contra una pequeña extensión de CRM que incluye funcionalidades con la parte de programación de actividades de servicio me he encontrado con los siguientes dos problemas, que muestro aquí para no olvidar.

  1. Problema al copiar una entidad que incluya el objeto activityparty
    Los objetos activityparty representan los participantes en una cita o en una actividad de servicio. En el caso de la actividad de servicio, pueden estar tanto entre los clientes (atributo customers) o en los recursos (atributo resources). Bueno, pues si los copiamos de unos previamente existentes, obtendremos un error SOAP SQL integrity violation que no ayuda mucho. Bien, pues el único problema es que hay que crear Guids nuevos para cada activityparty. Se soluciona con un simple

    foreach (activityparty ap in actividad.resources)
    ap.activitypartyid = Guid.NewGuid();

    Gracias a David Jennaway en las news de crm.developer
  2. Problema con el formato de las fechas en CrmDateTime
    Independientemente de lo que diga la SDK, sólo funciona bien con el formato yyyy/M/ddTHH:mm:00 sobre el campo Value. Hay que olvidarse (al menos yo me olvido) de los atributos date y time y especificar de golpe ambas, o sea:

    actividad.scheduledstart.Value =
    fechaInicio.ToString("yyyy/M/ddTHH:mm:00");

24/3/07

Problema con las colas de correo

Si después de haber creado la cola de actividades y casos tal como explica Marco Amoedo en su blog os seguís dando de cabezazos (como yo hice) porque los correos siguen sin entrar. Si os dáis cuenta de que el correo de los usuarios entra sin ningún problema en sus buzones. Si os habéis encontrado con este problema, la solución, en una línea de letra grande y roja es:

El propietario de la cola sí importa

Cuando alguien asigna una actividad o caso a una cola, el propietario sigue siendo el original, el creador del elemento. Ningún problema. Sin embargo, cuando llega un nuevo correo, el router tiene que crear una actividad de la nada, sin propietario (y casi todo en CRM tiene propietario) así que, ¿a quién se la asigna? Pues al propietario de la cola. ¿Y qué pasa si el propietario de la cola no tiene permisos para crear actividades (ejemplo típico: el administrador con acceso restringido) Pues que no funciona.

Moraleja rellenad el propietario de la cola con un usuario con licencia en CRM y permiso para crear e-mails en la unidad de negocio a la que pertenece.

21/3/07

Mostrar el nombre de la organización en Outlook

Esta es sin duda una de las peticiones más solicitadas por los clientes que instalan la versión Outlook de CRM. Aproximadamente cuatro segundos después de la instalación, cuando descubren que sus contactos no se pueden organizar por organización. Es decir, utilizar el campo Organización de Outlook para agrupar sus contactos por empresa. Pues no, no se puede, pero tenemos esta alternativa que nos proponen aquí.

http://www.workopia.com/howto/DisplayParentAccountOutlookContactForm.htm

Sacado del blog del equipo de CRM http://blogs.msdn.com/crm/

8/11/06

Programa de "beta-testers" del Update Rollup 1

Está disponible en el blog de desarrollo de CRM en MS un programa para aquellos partners que quieran participar en las pruebas de la beta del próximo Update Rollup de CRM, que está previsto que salga a "finales de año".

Más información: http://blogs.msdn.com/crm/archive/2006/08/09/693420.aspx

12/9/06

Seguridad en el nivel de campo

Mucha gente cree que sobre el modelo de seguridad de CRM hay mucho que añadir. La prueba está en que partners han empezado a desarrollar extensiones al modelo que permiten, por ejemplo, aplicar una política de seguridad en el nivel del campo (atributo) además de en el nivel de registro (objeto).

Como muestra, fijaos en lo que hacen en c360:

http://www.c360.com/FieldLevelSecurity.aspx

Esta imagen que incluyo es la interfaz de administador, donde se decide qué roles o equipos pueden acceder a un campo de un formulario, y con qué tipo de permisos.

31/8/06

Error al migrar actividades

En la última migración que he tenido que soport... realizar, me he encontado con el siguiente problema, que dejo aquí para no olvidarlo y que, de paso, puede ayudar a alguien, quién sabe.

Cuando se trata de migrar actividades de cualquier tipo (Tareas, Llamada de teléfono etc) tendremos lógicamente que rellenar el campo StateCode para indicar si la tarea está abierta o cerrada. Vale, hasta ahí ningún problema. Sin embargo, si rellenas el campo StatusCode, que permite explicar la Razón del estado de la actividad, la migración falla, con un sencillo mensaje de error : Failed to migrate object: Server was unable to process request. Además, termina el proceso y no migra el resto de actividades.

Solución: si dejamos el campo StatusCode vacío en la CDF, tomará el valor por defecto y la migración funcionará. Si tenemos que cambiar el valor por defecto... entonces tenemos otro problema, para el que no tengo solución.

11/5/06

Hablando de blogs

El equipo de MS-CRM ha lanzado un blog común para todos sus integrantes. Seguro que será una fuente de información fundamental a partir de ahora:

http://blogs.msdn.com/crm/

Por cierto, me he enterado de esto en el blog de Marcos Amoedo:

http://geeks.ms/blogs/marco/

4/4/06

CRM 3.0 Mobile Edition



He encontrado esta noticia en otro blog de CRM. Lo traduzco y lo comparto con quien lo lea...
http://icu-mscrm.blogspot.com/

Parece que la semana que viene estará disponible la versión móvil de CRM 3.0, en inglés, claro.

Actualizo: Ya está disponible aquí

Por lo que dicen en este blog, tendrá sincronización inalámbrica
y estará basada en la última tecnología: Windows Mobile 5.0, .NET Compact Framework 2.0 y base de datos SQL CE 3.0

Permitirá distintas orientaciones (apaisado o vertical) y "nuevas funcionalidades de CRM 3.0". Sé que no es mucho decir, pero algo es algo.

28/3/06

Seminario de CRM

Dentro de nuestra campaña de promoción de CRM estamos realizando seminarios en Barcelona y Madrid para mostrar a nuestros clientes lo que puede (y no puede) hacer el producto. El de Barcelona ha sido un éxito de afluencia: más del 80% de la gente que se inscribió nos ha visitado finalmente. Mañana estaremos en Madrid y esperamos también que se anime bastante gente (alrededor de 45 empresas entre las dos ciudades).

En el seminario mostramos un par de casos reales de clientes y también algunas ideas (en funcionamiento) de extensión e integración de CRM a través de la SDK. Por cierto, las colgaré aquí en breve. No son grandes ni completas, claro, pero dan una idea de lo que se puede llegar a hacer si se tiene la intención (y el tiempo) necesario tomando CRM como plataforma de desarrollo de aplicaciones. Respecto a lo que "no puede" hacer CRM: es interesante ver cómo las expectativas que crea el producto son a veces desmesuradas. Por ejemplo: CRM no puede hacer por sí solo una integración en vivo con una aplicación a medida. Es verdad, pero también es verdad que nadie puede. Más detalles sobre los seminarios en

http://www.addlink-crm.es/seminarios/

21/3/06

Búsqueda avanzada


Parece que a partir de la versión 3.0 las búsquedas avanzadas empiezan a ser avanzadas de verdad... En la versión anterior eran nuestra única herramienta para hacer algo parecido a campañas de marketing, pero todavía tenían muchas carencias. Por poner un ejemplo: las columnas que aparecían sólo se podían configurar desde el módulo de personalización, y eran iguales para todos los usuarios. En la nueva versión todo es más personal: cada usuario puede hacer su búsqueda avanzada, escoger las columnas que quiere y después, (si cree que volverá a hacerla en el futuro) guardarla como una vista almacenada. Esto sí que empieza a ser útil. Además, se puede hacer todo tipo de combinaciones de condiciones y/o, como podéis ver en la imagen.