protected void Execute(LocalPluginContext context) { #region Get the list of autonumber records applicable to the target entity type var triggerEvent = context.PluginExecutionContext.MessageName; var codegenratList = context.OrganizationDataContext.CreateQuery("molyom_uniquecodegenerator") .Where(a => a.GetAttributeValue <string>("molyom_entityname").Equals(context.PluginExecutionContext.PrimaryEntityName) && a.GetAttributeValue <OptionSetValue>("statecode").Value == 0 && a.GetAttributeValue <OptionSetValue>("molyom_triggerevent").Value == (triggerEvent == "Update" ? 1 : 0)) .OrderBy(a => a.GetAttributeValue <Guid>("molyom_uniquecodegeneratorid")) // Insure they are ordered, to prevent deadlocks .Select(a => a.GetAttributeValue <Guid>("molyom_uniquecodegeneratorid")); #endregion #region This loop locks the autonumber record(s) so only THIS transaction can read/write it foreach (var codeGenerated in codegenratList) { var lockingUpdate = new Entity("molyom_uniquecodegenerator") { Id = codeGenerated, ["molyom_preview"] = "555" }; // Use the preview field as our "dummy" field - so we don't need a dedicated "dummy" context.OrganizationService.Update(lockingUpdate); } #endregion #region This loop populates the target record, and updates the autonumber record(s) if (!(context.PluginExecutionContext.InputParameters["Target"] is Entity target)) { return; } foreach (var codegeneratedId in codegenratList) { var codeGeneratedObj = context.OrganizationService.Retrieve("molyom_uniquecodegenerator", codegeneratedId, new ColumnSet( "molyom_attributename", "molyom_triggerattribute", "molyom_conditionaloptionset", "molyom_conditionalvalue", "molyom_characterlength", "molyom_prefix", "molyom_nextcode", "molyom_suffix")); var targetAttribute = codeGeneratedObj.GetAttributeValue <string>("molyom_attributename"); #region Check conditions that prevent creating an autonumber if (context.PluginExecutionContext.MessageName == "Update" && !target.Contains(codeGeneratedObj.GetAttributeValue <string>("molyom_triggerattribute"))) { continue; // Continue, if this is an Update event and the target does not contain the trigger value } else if ((codeGeneratedObj.Contains("molyom_conditionaloptionset") && (!target.Contains(codeGeneratedObj.GetAttributeValue <string>("molyom_conditionaloptionset")) || target.GetAttributeValue <OptionSetValue>(codeGeneratedObj.GetAttributeValue <string>("molyom_conditionaloptionset")).Value != codeGeneratedObj.GetAttributeValue <int>("molyom_conditionalvalue")))) { continue; // Continue, if this is a conditional optionset } else if (target.Contains(targetAttribute) && !string.IsNullOrWhiteSpace(target.GetAttributeValue <string>(targetAttribute))) { continue; // Continue so we don't overwrite a manual value } else if (triggerEvent == "Update" && context.PreImage.Contains(targetAttribute) && !string.IsNullOrWhiteSpace(context.PreImage.GetAttributeValue <string>(targetAttribute))) { context.TracingService.Trace("Target attribute '{0}' is already populated. Continue, so we don't overwrite an existing value.", targetAttribute); continue; // Continue, so we don't overwrite an existing value } #endregion #region Create the Unique Code char pad = '0'; var charLength = codeGeneratedObj.GetAttributeValue <int>("molyom_characterlength"); var prefix = context.OrganizationService.ReplaceParameters(target, codeGeneratedObj.GetAttributeValue <string>("molyom_prefix")); // var number = charLength == 0 ? "" : autoNumber.GetAttributeValue<string>("molyom_nextcode").ToString("D" + charLength); var number = charLength == 0 ? "" : codeGeneratedObj.GetAttributeValue <string>("molyom_nextcode").PadLeft(charLength, pad); // string number = autoNumber.GetAttributeValue<string>("molyom_nextcode") == null ? new string('0', charLength) : autoNumber.GetAttributeValue<string>("molyom_nextcode").PadRight(charLength).Substring(0, charLength); var postfix = context.OrganizationService.ReplaceParameters(target, codeGeneratedObj.GetAttributeValue <string>("molyom_suffix")); // Generate number and insert into target Record target[targetAttribute] = $"{prefix}{number}{postfix}"; // Increment next number in db var updatedAutoNumber = new Entity("molyom_uniquecodegenerator") { Id = codeGeneratedObj.Id, // Aqui tengo que incrementar un codigo. ["molyom_nextcode"] = Increment(codeGeneratedObj.GetAttributeValue <string>("molyom_nextcode").PadLeft(codeGeneratedObj.GetAttributeValue <int>("molyom_characterlength"), pad), Mode.AlphaNumeric), // + "1", ["molyom_preview"] = target[targetAttribute] }; context.OrganizationService.Update(updatedAutoNumber); #endregion } }
protected void Execute(LocalPluginContext context) { context.Trace("Get Target record"); var target = context.GetInputParameters <CreateInputParameters>().Target; var pluginName = string.Format(PluginName, target.GetAttributeValue <string>("molyom_entityname")); if (target.GetAttributeValue <OptionSetValue>("molyom_triggerevent").Value == 1) { pluginName += " Update"; } context.Trace("Check for existing plugin step"); if (context.OrganizationDataContext.CreateQuery("sdkmessageprocessingstep").Where(s => s.GetAttributeValue <string>("name").Equals(pluginName)).ToList().Any()) { return; // Step already exists, nothing to do here. } context.Trace("Build the configuration"); var config = new UniqueCodeGeneratorPluginConfig() { EntityName = target.GetAttributeValue <string>("molyom_entityname"), EventName = target.GetAttributeValue <OptionSetValue>("molyom_triggerevent").Value == 1 ? "Update" : "Create" }; context.Trace("Get the Id of this plugin"); var pluginTypeId = context.OrganizationDataContext.CreateQuery("plugintype") .Where(s => s.GetAttributeValue <string>("name").Equals(typeof(GetNextUniqueCode).FullName)) .Select(s => s.GetAttributeValue <Guid>("plugintypeid")) .First(); context.Trace("Get the message id from this org"); var messageId = context.OrganizationDataContext.CreateQuery("sdkmessage") .Where(s => s.GetAttributeValue <string>("name").Equals(config.EventName)) .Select(s => s.GetAttributeValue <Guid>("sdkmessageid")) .First(); context.Trace("Get the filterId for for the specific entity from this org"); var filterId = context.OrganizationDataContext.CreateQuery("sdkmessagefilter") .Where(s => s.GetAttributeValue <string>("primaryobjecttypecode").Equals(config.EntityName) && s.GetAttributeValue <EntityReference>("sdkmessageid").Id.Equals(messageId)) .Select(s => s.GetAttributeValue <Guid>("sdkmessagefilterid")) .First(); context.Trace("Build new plugin step"); var newPluginStep = new Entity("sdkmessageprocessingstep") { Attributes = new AttributeCollection() { { "name", pluginName }, { "description", pluginName }, { "plugintypeid", pluginTypeId.ToEntityReference("plugintype") }, // This plugin type { "sdkmessageid", messageId.ToEntityReference("sdkmessage") }, // Create or Update Message { "configuration", config.ToJson() }, // EntityName and RegisteredEvent in the UnsecureConfig { "stage", PipelineStage.PreOperation.ToOptionSetValue() }, // Execution Stage: Pre-Operation { "rank", 1 }, { "impersonatinguserid", context.PluginExecutionContext.UserId.ToEntityReference("systemuser") }, // Run as SYSTEM user. Assumes we are currently running as the SYSTEM user { "sdkmessagefilterid", filterId.ToEntityReference("sdkmessagefilter") }, } }; context.Trace("Create new plugin step"); var sdkmessageprocessingstepid = context.OrganizationService.Create(newPluginStep); // only add the image if the type is update, on create a value cannot be overridden if (target.GetAttributeValue <OptionSetValue>("molyom_triggerevent").Value == 1) { context.Trace("Build new plugin step image"); var newPluginStepImage = new Entity("sdkmessageprocessingstepimage") { Attributes = new AttributeCollection() { { "sdkmessageprocessingstepid", sdkmessageprocessingstepid.ToEntityReference("sdkmessageprocessingstep") }, { "imagetype", 0.ToOptionSetValue() }, // PreImage { "messagepropertyname", "Target" }, { "name", "Image" }, { "entityalias", "Image" }, { "attributes", target.GetAttributeValue <string>("molyom_attributename") }, //Only incluce the one attribute we really need. } }; context.Trace("Create new plugin step image"); context.OrganizationService.Create(newPluginStepImage); } }
protected void Execute(LocalPluginContext context) { context.Trace("Getting Target entity"); var target = context.GetInputParameters <CreateInputParameters>().Target; context.Trace("Validate the Entity name"); context.Trace("Get Attribute List"); var attributeList = GetEntityMetadata(context, target.GetAttributeValue <string>("molyom_entityname")); context.Trace("Validate the Attribute name"); if (!attributeList.Select(a => a.LogicalName).Contains(target.GetAttributeValue <string>("molyom_attributename"))) { throw new InvalidPluginExecutionException("Specified Attribute does not exist."); } context.Trace("Validate the Trigger Attribute (if any)"); if (!string.IsNullOrEmpty(target.GetAttributeValue <string>("molyom_triggerattribute")) && !attributeList.Select(a => a.LogicalName).Contains(target.GetAttributeValue <string>("molyom_triggerattribute"))) { throw new InvalidPluginExecutionException("Specified Trigger Attribute does not exist."); } context.Trace("Validate the Attribute type"); if (attributeList.Single(a => a.LogicalName.Equals(target.GetAttributeValue <string>("molyom_attributename"))).AttributeType != AttributeTypeCode.String && attributeList.Single(a => a.LogicalName.Equals(target.GetAttributeValue <string>("molyom_attributename"))).AttributeType != AttributeTypeCode.Memo) { throw new InvalidPluginExecutionException("Attribute must be a text field."); } #region test parameters #if VALIDATEPARAMETERS var fields = new Dictionary <string, string>() { { "molyom_prefix", "Prefix" }, { "molyom_suffix", "Suffix" } }; foreach (var field in fields.Keys) { if (!target.Contains(field) || !target.GetAttributeValue <string>(field).Contains('{')) { continue; } if (target.GetAttributeValue <string>(field).Count(c => c.Equals('{')) != target.GetAttributeValue <string>(field).Count(c => c.Equals('}'))) { throw new InvalidPluginExecutionException($"Invalid parameter formatting in {fields[field]}"); } if (Regex.Matches(target.GetAttributeValue <string>(field), @"{(.*?)}").OfType <Match>().Select(m => m.Groups[0].Value).Distinct().Any(p => p.Substring(1).Contains('{'))) { throw new InvalidPluginExecutionException($"Invalid parameter formatting in {fields[field]}"); } try { foreach (var param in RuntimeParameter.GetParametersFromString(target.GetAttributeValue <string>(field))) { if (!param.IsParentParameter()) { if (!attributeList.Select(a => a.LogicalName).Contains(param.AttributeName)) { throw new InvalidPluginExecutionException($"{param.AttributeName} is not a valid attribute name in {fields[field]} value"); } } else { if (!attributeList.Select(a => a.LogicalName).Contains(param.ParentLookupName)) { throw new InvalidPluginExecutionException($"{param.ParentLookupName} is not a valid attribute name in {fields[field]} value"); } if (attributeList.Single(a => a.LogicalName.Equals(param.ParentLookupName)).AttributeType != AttributeTypeCode.Lookup && attributeList.Single(a => a.LogicalName.Equals(param.ParentLookupName)).AttributeType != AttributeTypeCode.Customer && attributeList.Single(a => a.LogicalName.Equals(param.ParentLookupName)).AttributeType != AttributeTypeCode.Owner) { throw new InvalidPluginExecutionException($"{param.ParentLookupName} must be a Lookup attribute type in {fields[field]} value"); } var parentLookupAttribute = (LookupAttributeMetadata)GetAttributeMetadata(context, target.GetAttributeValue <string>("molyom_entityname"), param.ParentLookupName); if (!parentLookupAttribute.Targets.Any(e => GetEntityMetadata(context, e).Select(a => a.LogicalName).Contains(param.AttributeName))) { throw new InvalidPluginExecutionException($"Invalid attribute on {param.ParentLookupName} parent entity, in {fields[field]} value"); } } } } catch (InvalidPluginExecutionException) { throw; } catch { throw new InvalidPluginExecutionException($"Failed to parse Runtime Parameters in {fields[field]} value."); } } #endif #endregion if (target.Contains("molyom_conditionaloptionset")) { context.Trace("Validate Conditional OptionSet"); if (!attributeList.Select(a => a.LogicalName).Contains(target.GetAttributeValue <string>("molyom_conditionaloptionset"))) { throw new InvalidPluginExecutionException("Specified Conditional OptionSet does not exist"); } if (attributeList.Single(a => a.LogicalName.Equals(target.GetAttributeValue <string>("molyom_conditionaloptionset"))).AttributeType != AttributeTypeCode.Picklist) { throw new InvalidPluginExecutionException("Conditional Attribute must be an OptionSet"); } context.Trace("Validate Conditional Value"); var optionSetMetadata = (PicklistAttributeMetadata)GetAttributeMetadata(context, target.GetAttributeValue <string>("molyom_entityname"), target.GetAttributeValue <string>("molyom_conditionaloptionset")); //attributeResponse.AttributeMetadata; if (!optionSetMetadata.OptionSet.Options.Select(o => o.Value).Contains(target.GetAttributeValue <int>("molyom_conditionalvalue"))) { throw new InvalidPluginExecutionException("Conditional Value does not exist in OptionSet"); } } #region Duplicate Check #if DUPLICATECHECK context.Trace("Validate there are no duplicates"); // TODO: Fix this. duplicate detection works when all fields contain data, but fails when some fields are empty var autoNumberList = context.OrganizationDataContext.CreateQuery("molyom_uniquecodegenerator") .Where(a => a.GetAttributeValue <string>("molyom_entityname").Equals(target.GetAttributeValue <string>("molyom_entityname")) && a.GetAttributeValue <string>("molyom_attributename").Equals(target.GetAttributeValue <string>("molyom_attributename"))) .Select(a => new { Id = a.GetAttributeValue <Guid>("molyom_uniquecodegeneratorid"), ConditionalOption = a.GetAttributeValue <string>("molyom_conditionaloptionset"), ConditionalValue = a.GetAttributeValue <int>("molyom_conditionalvalue") }) .ToList(); if (!target.Contains("molyom_conditionaloptionset") && autoNumberList.Any()) { throw new InvalidPluginExecutionException("Duplicate Code record exists."); } if (autoNumberList.Any(a => a.ConditionalOption.Equals(target.GetAttributeValue <string>("molyom_conditionaloptionset")) && a.ConditionalValue.Equals(target.GetAttributeValue <int>("molyom_conditionalvalue")))) { throw new InvalidPluginExecutionException("Duplicate Code record exists."); } #endif #endregion context.Trace("Insert the unique code generator Name attribute"); target["molyom_name"] = $"Unique Code for {target.GetAttributeValue<string>("molyom_entityname")}, {target.GetAttributeValue<string>("molyom_attributename")}"; }