Is there an event that a plugin can be registered on when a FetchXML (or even SQL) report is run?
RetrieveMultiple and Retrieve do not get fired!
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// The FetchXML report does not fire the plugin on RetrieveMultiple
if (context.InputParameters["Query"] is FetchExpression)
{
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
using (Context linq = new Context(service))
{
// Do the work.
}
}
}
I had the same issue and the solution is the following:
Reports do not trigger plugins. SQL reports bypass CRM completely, and
Fetch reports also bypass the plugin pipeline
More about this here: https://crmbusiness.wordpress.com/2014/11/25/fetchxml-reports-do-not-trigger-retrievemultiple-plugins-in-crm-2011/
I have had similar needs to perform an action when a report is run and the best [supported] way I could find to accomplish this by triggering the report to run via Javascript.
The rough process is this:
In the applicable entity, create ribbon button to launch the report via Javascript. (This post gives a how-to: Trigger a report from a ribbon button)
As part of Javascript function which runs the report, update a field (e.g. "new_ReportRunOn") in the respective record (A standard REST update operation would work here) prior to report run code. The idea here is simply running the report will always cause the "new_ReportRunOn" field to update first.
Have your plugin listen for changes on this field by applying a filter only the "new_ReportRunOn" field. (Alternately, you could just have your logic run in Javascript [as part step 2] instead of updating a field just to trigger a plugin.)
This isn't without drawbacks though:
You would need to "hide" the report in CRM Reports section so it is essentially only accessible via the entity form. It would still be visible in the Reports section if users chose to browse "All Reports" in the reports section. If a "savy" user launched the report this way then it would bypass your Javascript code which fires the plugin.
If the report can be run across multiple records (e.g. the user selects applicable records from the entity grid view) then additional considerations/coding would need to be done. It is possible to handle this case, but based on your question it doesn't appear this would be use case.
I've had process similar to this in place for a while on our 2013 install and it actually works quite well. It isn't ideal but gets the job done.
Related
I want to trigger a NetSuite workflow when the user sets the value of a field, but I don't want to have them submit first. The Workflow state builder looks like it has useful options but I can't get it to work.
There's some useful looking blog posts around but a lot of them seem out of date.
Update - more info
My primary issue is this one: Restrict what customers an employee can see (NetSuite)
The hack I'm currently looking at is populating a custom Transaction Column Field that I've added to a custom Time Recording form. The idea is to load this field on the UI with only valid projects (not customers as well), and this I have been able to do.
The problem is I still (as far as I can tell) still need to populate the "Customer" field, which is mandatory; I'm also assuming that if I don't do that then any time that is recorded won't go against the project. I had thought that if the user selects the project they want then I can populate the customer field with that value. I hate this as an approach but I can't see how else to do it. I do have coding experience (including JavaScript) but haven't made the leap into SuiteScript yet.
You won't be able to do this in a Workflow, as they are currently limited to only work with body level fields and cannot modify Transaction Column Fields (aka sublists).
You should be able to achieve this with a Client Side Script though.
Source (Netsuite login required).
Sublist changes will be available for transactions in the 2018.1 release sometime in Feb/Mar.
I am looking to create a workflow that will populate any mandatory fields that do not contain data when a company is deactivated. The mandatory fields are either not populated because of legacy data or because the mandatory fields have changed since the record was created.
The issue I get is that the mandatory field check takes place before the workflow is triggered.
My workflow looks like follows:
If Status = Inactive
If Company Name does not contain data
Then update to ‘Test’
I have come up with a possible solution:
• Create a new button in the ribbon that runs the workflow on-demand and apply permissions to this button.
If possible though I would like to do this through a workflow that is triggered on clicking of the ‘Deactivate’ button. Any ideas are welcome.
In your situation the record will not be saved until the mandatory fields are filled, and a workflow cannot be triggered if a record is not saved first.
An approach to solve this situation is to write a JavaScript attached to the OnSave event, but you need a rule to be able to identify the legacy records, so this JavaScript (that will fill the mandatory fields for you) will not run for normal records.
This approach is not tested, but with some tweaks should work.
I build a plugin that runs in account update message that makes some validation not to duplicate the record. But if I try to disable that account or merge with another one it triggers the plugin.
How can I tell if the plugin was triggered by an activate/deactivate/merge action, or event mode like the one in CRM Dynamics 2011 Save Event Arguments Reference?
To add to Pete Oakey's answer, you can actually tell your plugin to fire only when certain fields are changed at the registration phase, in addition to testing this in actual code # runtime, by changing the "Filtering Attributes" of the plugin step. So unless your validation logic specifically needs the StateCode/StatusCode attributes to correctly process, you can filter those two attributes out, or just include the attributes your validation logic needs.
Original answer:
I believe you can reference the IExecutionContext.Depth Property of the IPluginExecutionContext object.
Every time a running plug-in or Workflow issues a message request to
the Web services that triggers another plug-in or Workflow to execute,
the Depth property of the execution context is increased. If the depth
property increments to its maximum value within the configured time
limit, the platform considers this behavior an infinite loop and
further plug-in or Workflow execution is aborted. The maximum depth
(8) and time limit (one hour) are configurable by the Microsoft
Dynamics CRM administrator.
So you can use an if statement to check if Depth > 1.
One way would be to compare the before and after values of what's changed on the account entity in the plugin. For example if you have only have active (if that's the right property name) in the values - you can look at the after value to see what's happened.
Another way would be to add a specific plugin for these messages and set some property/flag which you can check in the update plugin. Obviously this would have to run before the update plugin.
I have an SP 2010 external list that is populated with customer names. The list is updated occasionally throughout the day. I would like to automatically copy the newly added names to another SP 2010 list when it is updated or at a set time (hourly).
Is there an easy way to do this? And if not, is there at least a way to do it?
Thank you for your help.
Unfortunately, External Lists do not support workflows. So workflow is not a solution here.
One of the way to do this would be to create a custom timer job to synchronize items & configure to run it periodically. See details about how to create & register custom job here.
But this approach has it's own drawbacks:
it is complex enough
you will need a farm scoped feature +
receiver to register the job. The
reason is that for security purposes
you can't register a custom job from
within the code running in Content
Web application (so it will not work
in site collection level feature
receiver), only from code running in
Central Admin app.
I'd build a windows service or a timed job in SharePoint and then hook up a compatible ado.net connector to my process. This way you can copy or synchronize data between your two lists as if they where ordinary SQL tables.
private void example()
{
// Fetch data from your left sharepoint
SharePointConnection leftConnection = new SharePointConnection(#"
Server=mysharepointserver.com;
Database=mysite/subsite
User=spuser;
Password=******;
Authentication=Ntlm;
TimeOut=10;
StrictMode=True;
RecursiveMode=RecursiveAll;
DefaultLimit=1000;
CacheTimeout=5");
leftConnection.Open();
string leftQuery = "SELECT * FROM LeftList";
SharePointDataAdapter adapter = new SharePointDataAdapter(leftQuery, leftConnection);
DataTable dt = new DataTable();
adapter.Fill(dt);
// Insert data in right sharepoint
SharePointConnection rightConnection = new SharePointConnection(#"
Server=anothersharepointserver.com;
Database=whateversite
User=spuser;
Password=******;
Authentication=Ntlm;
TimeOut=10;
StrictMode=True;
RecursiveMode=RecursiveAll;
DefaultLimit=1000;
CacheTimeout=5");
rightConnection.Open();
// build your rightQuery here
string rightQuery = "Insert into"...
SharePointCommand command = new SharePointCommand(rightQuery, rightConnection);
command.ExecuteNonQuery();
}
You could try this one http://www.bendsoft.com/net-sharepoint-connector/. This ado.net connector uses the API of SharePoint so you can run the service in a third machine and as long as it has access you'll be fine.
There are some examples and howto's at http://blog.bendsoft.com
A workflow can be created for this easily. The workflow can be triggered when an item is newly created or updated. The workflow can create the same item into another list.
Try creating the workflow using SharePoint designer, which is quiet straight forward.
Hope this helps. :)
Why do you want to copy the external data to another list? What is the purpose of doing this? Perhaps there is a better solution for what you are trying to do.
The reason I say this, is because you are going to be replicating data and could get a bit tricky, especially if you allow the data to be updated in both places.
I'd use MS access in order to copy data out of one table, and push it into another table. I've used Access/Sharepoint extensively, and it's a little bit buggy.. but it generally works. It might crash- but for 90% of small tables, heck yes you can do this!
I need to copy the item that user just added (for example. myresume.doc or financial.xls) with the metadata (doc lib obtains the columns from content type, ct obtains the columns from site colum) and copy the item with metadata in a folder called "NativeFile". Every doc library has this folder.
I know itemadded can be used but then I heard itemadded fires before user have a chance to complete the metadata for the item they just added.
What are my options? (new to sp, so some sample code would greatly help. or some good link similar to this issue)
Sharepoint 2007, itemadded or itemadding or itemupdating or itemupdated....
ccomet is correct. With item event receivers alone there's no good way to duplicate the metadata. If you can get away with just copying the file here's whatcha do:
public class MyItemEventReceiver : SPItemEventReceiver
{
public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdded(properties);
SPListItem item = properties.ListItem;
item.File.CopyTo(item.ParentList.RootFolder.Name + "/NativeFile/" + item.File.Name, true);
}
}
What you've heard is correct: ItemAdded will fire before metadata completion because in a document library, ItemAdded fires when the document is first uploaded and not when the form is filled out.
I think the smoothest option is to use a workflow that fires off when an item is created in the document library. Specifically a code-based one using Visual Studio 2008, if you have it. A general walkthrough of making workflows using Visual Studio 2008 can be found here: Wrangling SharePoint Workflows with Visual Studio. That article is actually how I learned to make workflows from in the first place, so I hope it helps you as well as it has me. I would recommend this approach over an event handler because you would have to use ItemUpdated, which means you'd have to do tricky checks just to ensure this only runs once. And the other option I can think of, which is making a custom form, is not very comfortable to have to do all of the time.
Now, make your own workflow. Make sure to initialize the workflowProperties like shown in the link for the onWorkflowActivated_Invoked. This is a very simple workflow if you make all of the necessary fields Required in the form. In designer view, all you need are 2 additional activities besides the OnWorkflowActivated activity: an OnWorkflowItemChanged activity, and a Code activity. For the OnWorkflowItemChanged activity, the only property you need to set is the CorrelationToken, which will be the same as the OnWorkflowActivated activity's CorrelationToken. Now, double click the Code activity to generate its codeActivity_Executed method. In this method, the following should suffice:
SPListItem item = workflowProperties.Item;
if (!item.File.Url.Contains("/NativeUrl/"))
{
string destination = item.ParentList.RootFolder.Name + "/NativeFile/" + item.File.Name;
item.File.CopyTo(destination, true);
}
I added the if statement because the workflow will probably trigger on the new item you created by using CopyTo, so this will check to see if the file is already in the folder, and if it is, it won't do a copy. Now, associate the workflow with your website like demonstrated in the link, and associate it with item creation. It should work, then.
Even though this workflow techinically waits for ItemUpdated, because that is when the fields are filled out, by associating it with creating an item, you ensure that it only runs once. I do admit that I've only used item.File.MoveTo(destination, true), but that does retain all of the fields and metadata, so I'm pretty sure the CopyTo should as well. If not, just give a holler.
Just copying the Properties collection from a source item to a destination item doesn't work for all file types, especially when they are located in different document libraries.
See this thread for more details: https://sharepoint.stackexchange.com/questions/1151/what-is-the-best-way-to-copy-a-files-column-data-in-sharepoint