Results for tag "crm-2011"

CRM 2011 – Doubleclick im Subgrid deaktivieren/verhinden

Nach langer Zeit mal wieder was zum Thema CRM:

Nachdem wir in einem Projekt leider sehr große Hierarchien haben und die Subgrids bekanntermassen ja nur zwei Ebenen anzeigen, haben wir uns eine Art Schattenentität als Lösung einfallen lassen. Diese fungiert quasi als Linker zu den einzelnen Abhängigkeiten. Wie auch immer. Wir zeigen nun auf ein paar Formularen eine Art „Überblick“ an, die Daten in den Subgrids sind vom Typ „cust_linkentity“.

Das hat die negative Folgeerscheinung, dass bei Doppelklick das Standard-Formular der Entität „cust_linkentity“ geöffnet wird, obwohl diese Entität rein aus funktionellen Gründen existiert und für den Kunden eigentlich unsichtbar sein sollte.

Leider gibt es keinen von Microsoft unterstützten Weg, das zu verhindern. Meine Recherche hat mich zur Datei „AppGrid_DefaultData.htc“ gebracht, welche unter anderem die Funktion „handleDblClick()“ enthält.

ACHTUNG: Die nachfolgende Lösung ist von Microsoft explizit nicht supported!! Bitte nur machen, wenn Ihr euch über die Folgen im Klaren seid!

Original

function handleDblClick()
{
o = getSrcRow();
if (!IsNull(o))
if ((event.ctrlKey || event.srcElement.tagName == "INPUT" && event.srcElement.type == "checkbox") && o.selected)
unselectRow(o, true, true);
else
handleSelectRow(o);

var oSelectedRecords = getSelectedRecords();

if (oSelectedRecords.length == 0)
return;

var oSelRec = oSelectedRecords[0], oTr = oSelRec[3];

openRecord(oTr);

}
[/pre]

Wie Ihr seht, wird als Letztes mit "openRecord" das Formular der Entität geöffnet.

Angepasst

[sourcecode language="javascript"]
function handleDblClick()
{
o = getSrcRow();
if (!IsNull(o))
if ((event.ctrlKey || event.srcElement.tagName == "INPUT" && event.srcElement.type == "checkbox") && o.selected)
unselectRow(o, true, true);
else
handleSelectRow(o);

var oSelectedRecords = getSelectedRecords();

if (oSelectedRecords.length == 0)
return;

var oSelRec = oSelectedRecords[0], oTr = oSelRec[3];

var typename = o.getAttribute("otypename");
if (typename != "cust_linkentity") {

openRecord(oTr);
}
}
[/sourcecode]

Ich habe hier eine einfache Condition eingebaut, die das Formular nur öffnet, wenn es sich nicht um diesen einen bestimmten Typ handelt.

Cheers,
Chris

CRM 2011 – Javascript – Attribute setzen (für alle Datentypen)

Hallo Leute,

nach längerer Zeit mal wieder ein feiner Alltags-Helper.
Um in CRM via Javascript einen Wert in ein Attribut schreiben zu können, bedarf es eigentlich je nach Typ (Lookup, Text, Integer, etc.) eine eigene Methode.
Ich habe mir jetzt einen Helper geschrieben, der selbst prüft um was für einen Typ es sich handelt und entsprechend handelt.

[sourcecode language=“javascript“]
this.SetAttributeValue = function (id, value, valueId, logicalName) {

var control = Xrm.Page.getControl(id);
var isDisabledField = Xrm.Page.getControl(id).getDisabled();

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(false);
}

try {
if (control != null) {

if (value == null) {
FormUtils.SetStandardAttributeValue(id, value);
}
else {
var name = id;
var type = control.getAttribute().getAttributeType();

switch (type) {

case "string":
FormUtils.SetStandardAttributeValue(id, value);
break;

case "integer":
var intValue = parseInt(value);
FormUtils.SetStandardAttributeValue(id, intValue);
break;

case "optionset":
FormUtils.SetStandardAttributeValue(id, value);
break;

case "memo":
FormUtils.SetStandardAttributeValue(id, value);
break;

case "boolean":
FormUtils.SetStandardAttributeValue(id, (value == "true"));
break;

case "money":
var intValue = parseFloat(value);
FormUtils.SetStandardAttributeValue(id, intValue);
break;

case "datetime":
// not implemented yet
break;

case "lookup":
FormUtils.SetLookupAttributeValue(id, valueId, value, logicalName);
break;

case "decimal":
var intValue = parseFloat(value);
FormUtils.SetStandardAttributeValue(id, intValue);
break;
}
}

}
}
catch (ex) {

}

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(true);
}

}

// Setzt den Wert eines "normalen" Attributes auf einer Entity-Page.
this.SetStandardAttributeValue = function (id, value) {

var isDisabledField = Xrm.Page.getControl(id).getDisabled();

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(false);
}

try {
Xrm.Page.getAttribute(id).setValue(value);
Xrm.Page.getAttribute(id).setSubmitMode(‚always‘);
}
catch (err) {
alert(err);
}

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(true);
}
}

// Setzt den Wert eines "normalen" Attributes auf einer Entity-Page.
this.SetLookupAttributeValue = function SetLookupAttributeValue(id, valueId, value, entityLogicalName) {

var isDisabledField = Xrm.Page.getControl(id).getDisabled();

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(false);
}

try {
if (value == null) {
Xrm.Page.getAttribute(id).setValue(value);
}
else {
Xrm.Page.getAttribute(id).setValue([{ id: valueId, name: value, entityType: entityLogicalName}]);
}
Xrm.Page.getAttribute(id).setSubmitMode(‚always‘);
}
catch (err) {

}

if (isDisabledField) {
Xrm.Page.getControl(id).setDisabled(true);
}
}
[/sourcecode]

Beim setzen eines Optionsets muss der Integer-Wert gesetzt werden, nicht der Label. Zudem werden für das Setzen des Lookup-Attributes mehrere Parameter benötigt, die bei anderen Datentypen NULL sein können.

Am Error-Handling sollte man noch arbeiten, das ist dann aber euch selbst überlassen 😉

Have Fun!

Cheers,
Christian

CRM 2011 – OnCloseAlert – „Möchten Sie diese Seite wirklich verlassen?“ entfernen/unterdrücken

Hallo Leute,

einigen von euch ist es sicher schon passiert/aufgefallen. Ihr erzeugt eine neue Entität mittels Formular und wollt dieses Fenster dann, ohne es zu speichern, wieder verlassen. Was folgt ist der nachfolgende Dialog:

Das kann durchaus Sinn machen, möchte man z.B. den Benutzer warnen, dass Änderungen verloren gehen würden. Jedoch ist dieser Dialog durchaus problematisch, z.B. wenn man via Script die Seite neu laden/refreshen möchte und bewusst in Kauf nehmen möchte, dass Änderungen verloren gehen können.

Leider beschreibt die SDK keine Möglichkeit, wie man das deaktivieren kann. Sucht man mittels div. Suchmaschinen, bekommt man höchstens von Oberlehrern gebetsmühlenartig vorgekaut, dass man solche Dialoge tunlichst nicht verwenden soll…

Die Lösung ist letztlich recht einfach und besteht aus einer Zeile Code und kann im Javascript aufgerufen werden, nämlich:

[sourcecode language=“javascript“]
this.DetachCloseAlert = function () {
crmForm.detachCloseAlert();
}
[/sourcecode]

Happy Programming!

Cheers,
Chris

CRM 2011 – Required Fields serverseitig Validieren

Hi Leute,

man kennt doch das Problem, ab und zu schlägt irgendein Javascript fehlt (Syntax-Error, neue Browser-Version, etc.) und schon hat man den Salat, dass z.B. ein User eine Funktion auslöst, zu der er eigentlich nicht berechtigt wäre. Oder dass Felder, welche als Muss bzw. Required definiert sind, nicht ausgefüllt werden. Auf letzteren Punkt möchte ich den Fokus in diesem Blog-Entry legen. Nämlich wie man diese Required-Fields z.B. in einem eigenen WS oder in einem Plugin checkt.

CRM agiert hier, wie so oft, etwas eigenartig. Denn, wenn man sich die SDK so durchsieht, findet man ausschließlich einen ValidateRequest, welcher dann aber leider nur für div. Aktivitäten funktioniert, nicht für „normale“ Entitäten.

Contains the data that is needed to verify that an appointment or service appointment (service activity) has valid available resources for the activity, duration, and site as appropriate.

Warum das so ist, immerhin ist eine Aktivität nichts anderes als eine Entität, ist leider unklar. Also blieb nichts anderes übrig, selbst einen Weg zu finden, was auch relativ einfach gelang.

Zunächst mussten wir alle Required-Fields für eine Entität an Hand der Metainformationen herausfiltern:

[sourcecode language=“csharp“]
public static List<KeyValuePair<string, string>> GetRequiredFields(contracts.IOrganization organization, Entity entity)
{
try
{
List<KeyValuePair<string, string>> reqFields = new List<KeyValuePair<string, string>>();

var metadata = GetAllEntityAttributes(organization, entity.LogicalName);
foreach (var attribute in metadata)
{
if (attribute.RequiredLevel.Value == AttributeRequiredLevel.ApplicationRequired)
{
string label = attribute.AttributeOf;
if (attribute.DisplayName.UserLocalizedLabel != null)
label = attribute.DisplayName.UserLocalizedLabel.Label;

reqFields.Add(new KeyValuePair<string, string>(attribute.LogicalName, label));
}
}
return reqFields;
}
catch(Exception ex)
{
throw ex;
}
}
[/sourcecode]

Wir prüfen dabei nur auf „ApplicationRequired„-Fields, was den selbst definierten Pflichtfeldern entspricht. Man könnte noch „SystemRequired“ verwenden, was dann auch Felder wie „ownerid“, etc. zurückliefert. Es gibt noch weitere Optionen, hier lohnt sich der Blick ins CRM-SDK.

ApplicationRequired – The attribute is required to have a value.
None – No requirements are specified.
Recommended – It is recommended that the attribute has a value.
SystemRequired – The attribute is required to have a value.

Letztlich fehlt nur noch jene Methode, die überprüft, ob die gewünschte Entität auch die als required definierten Felder hält. Das funktioniert in CRM sehr einfach, denn die Attribute-Collection eines Microsoft.Xrm.Sdk.Entity-Objekts hält prinzipiell nur jene Attribute, die gesetzt wurden. Fehlt in dieser Collection also ein required Field, wurde es nicht gesetzt:

[sourcecode language=“csharp“]
public static List<KeyValuePair<string, string>> ValidateEntity(IOrganization organization, Entity entity)
{
try
{
// List<KeyValuePair<string, string>> listOfAlerts = new List<KeyValuePair<string, string>>();
List<ValidationResult> listOfErrors = new List<ValidationResult>();
// get required fields
var reqFields = GetRequiredFields(organization, entity);

// get displayname from entitymeta
var meta = GetEntityMeta(organization, entity.LogicalName);
var displayName = meta.DisplayName.UserLocalizedLabel.Label;

foreach(var field in reqFields)
{
if(!entity.Attributes.ContainsKey(field.Key))
listOfErrors.Add(new ValidationResult(field.Value, displayName, field.Key)); // Entitäts-Label | attribute-label | prefix_attributename
}

return listOfErrors;
}
catch (Exception ex)
{
throw ex;
}
}
[/sourcecode]

Die Methode liefert eine Generic-List retour, welche jene Felder beinhält, welche zwar required sind aber nicht gesetzt wurden.

Happy Programming 😉

Cheers,
Chris