I am attempting to use the SharedVariable of the IPluginExecutionContext between different calls to the same plugin. I have the following scenario:
The user is attempting to create a new Entity Record and the plugin has been triggered on the Pre stage. Based on some logic, I am setting a SharedVariable like so:
var context = (IPluginExecutionContext) serviceProvider.GetService(typeof (IPluginExecutionContext));
context.SharedVariables.Add("MySharedVariable", true);
I then attempt to update other records of the same entity like so:
var qe = new QueryExpression("new_myentity");
qe.Criteria.AddCondition("ecs_myfield", ConditionOperator.Equal,"someValue");
var results = service.RetrieveMultiple(qe);
foreach (var foo in results.Entities)
{
//Do something to foo
service.Update(foo);
}
I also have a plugin registered for Update on the Pre stage, however, I want to check MySharedVariable and do something else based on whether or not it is set.
In the Update, the context does not contain the key for 'MySharedVariable'. I have confirmed this by using the ITracingService.
Is there some restriction on passing shared variables between plugins that are run on different records?
The plugin execution mode for both the Create and Update is set to Synchronous and as already explained, both are registered on the Pre Operation stage
I don't use SharedVariables often, but I'm sure they are available in the same Execution Context (for example from a Pre Event to a Post Event for the same message on the same record).
They can't be used to share values between different plugins on different messages on different records (as in your case: set the value inside the Create of one record and retrieve the value inside the Update message of a different record)
For your situation I think it is preferable to use a custom entity to store the values, or create an additional attribute to the entity.
Hi by looking at the scenario you explained.
I will not be able to test this my self. But If you change the Update plugin from Pre to Post.
If you change the update plugin from PRE-Operation to Post Operation. You will definitely get the SharedVariable in the execution context.
Pass Data Between Plug-Ins
CRM 2011 Plugins – Shared Variables
Related
I have this error when I try to add a line of package
Error : Another process has added the "SOPackagedetail" record. Your changes will be lost.
error
My c# code is this :
protected virtual void creationColis()
{
SOShipment ship=Base.CurrentDocument.Select();
SOPackageDetailEx colis = new SOPackageDetailEx();
colis.BoxID="COLIS";
colis.PackageType="M";
colis.ShipmentNbr=ship.ShipmentNbr;
SOShipmentEntry graph = PXGraph.CreateInstance<SOShipmentEntry>();
graph.Packages.Insert(colis); //insertion de l'enregistrement
graph.Packages.Update(colis);
graph.Actions.PressSave();
graph.Clear();
}
Do you know what I must to change please ?
Thanks so much
Xavier
Your question needs more context. For starters, where does your code reside? Given that you reference Base.CurrentDocument.Select, I'm going to assume you are extending SOShipmentEntry to add your code.
In this case, you would just use the Base.Packages view rather than initializing your own instance of SOShipmentEntry where your example goes into trying to use graph.Packages. Regardless, there are 2 parts here that need to be addressed.
Packages is not the primary view of SOShipmentEntry. When you create an instance of a graph, you must tell the graph what record is needed in the primary view. In your example where you create a new instance of a graph, you might do something like this:
graph.Document.Current = graph.Document.Search<SOShipment.shipmentNbr>(myShipmentNbr);
If you are working on a graph extension of SOShipmentEntry, then you probably don't need to create a new instance of the graph. Just make sure graph.Document.Current isn't null before you add your package record - see bullet 2.
Once you have a shipment selected, you can then insert your package information. However, the way you have done it here effectively is trying to add a random package to a null shipment (by the structure of the views) but forcing the record to attach to the right shipment by sheer brute force. The views don't like to work that way.
A better way to add your package once you have a current shipment (Document) is like this:
// Find the current shipment (from the primary view Document)
SOShipment ship = Base.Document.Current();
if(ship?.ShipmentNbr != null) {
// Insert a record into the Packages view of the current shipment and return the record into colis
SOPackageDetailEx colis = Base.Packages.Insert(colis);
// Set the custom values
colis.BoxID="COLIS";
colis.PackageType="M";
// Update the Packages cache with the modified fields
Base.Packages.Update(colis);
// If more fields need to be updated after those changes were applied, instead do this...
colis = Base.Packages.Update(colis);
colis.FieldA = ValueA;
colis.FieldB = ValueB;
Base.Packages.Update(colis);
// If a save is needed, now is the time
Base.Save.Press();
}
Notice that I didn't assign ShipmentNbr. That is because the DAC has that field defined to pull the ShipmentNbr from SOShipment through these 2 attributes.
[PXParent(typeof(FK.Shipment))]
[PXDBDefault(typeof(SOShipment.shipmentNbr))]
This means that when the record is created, Acumatica should lookup the parent SOShipment record via the Key and do a DBDefault on the field to assign it to the SOShipment.ShipmentNbr value (from the parent). Important side note: PXDefault and PXDBDefault are NOT interchangeable. We use PXDefault a lot, but off the top of my head I can't think of a case of PXDBDefault outside of defaulting from a database value like this specific usage.
I'm currently working one a custom CRM-style solution (EF/Winforms/OData WebApi) and I wonder how to implement a quite simple requirement:
Let's say there is a simple Project entity. It is possible to assign Tasks to it. There is a DefaultTaskResponsible defined in the Project. Whenever a Task is created, the Project's DefaultTaskResponsible is used as the Task.Responsible. But it is possible change the Task.Responsible and even set it to null.
So, in a 'normal' programming world, I would use a Task constructor accepting the Project and set the Responsible there:
public class Task {
public Task(Project p) {
this.Responsible = p.DefaultTaskResponsible;
...
}
}
But how should I implement something like this in a CRM-World with Lookup views? In Dynamics CRM (or in my custom solution), there is a Task view with a Project Lookup field. It does not make sense to use a custom Task constructor.
Maybe it is possible to use Business Rules in Dynamics CRM and update the Responsible whenever the Project changes (not sure)?! But how should I deal with the WebApi/OData Client?
If I receive a Post to the Task endpoint without a Responsible I would like to use the DefaultTaskResponsible, e.g.
POST [Organization URI]/api/data/tasks
{
"project#odata.bind":"[Organization URI]/api/data/projects(xxx-1)"
}.
No Responsible was send (maybe because it is an older client), so use the default one. But if a Responsible is set, the passed value should be used instead, e.g.
POST [Organization URI]/api/data/tasks
{
"project#odata.bind":"[Organization URI]/api/data/projects(xxx-1)",
"responsible#odata.bind": null
}.
In my TaskController I only see the Task model with the Responsible being null, but I don't know if it is null because it was set explicitly or because it wasn't send in the request.
Is there something wrong with my ideas/concepts? I think it is quite common to initialize properties based on other objects/properties, isn't it?
This question is probably out of scope for this forum, but it is a subject I am interested in. A few thoughts:
A "Task" is a generic construct which traditionally can be associated with many different types of entities. For example, you might not only have tasks associated with Projects, but also with Customer records and Sales records. To run with your code example it would look like:
public Task(Entity parent) {}
Then you have to decide whether or not your defaulting of the Responsible party is specific to Projects, or generic across all Entities which have Tasks. If the latter, then our concept looks like this:
public Task(ITaskEntity parent)
{
this.Responsible = parent.DefaultResponsible; //A property of ITaskEntity
}
This logic should be enforced at the database "pre operation" level, i.e. when your CRM application receives a request to create a Task, it should make this calculation, then persist the task to the database. This suggests that you should have a database execution pipeline, where actions can be taken before or after database operations occur. A standard simple execution pipeline looks like this:
Validation -> Pre Operation -> Operation (CRUD) -> Post Operation
Unless you are doing this for fun, I recommend abandoning the project and using an existing CRM system.
I am trying to import cases from our old ticketing system into Acumatica using a C# console application. I have the old tickets loaded, and I am trying to use the REST API to create the cases.
I created a custom web service endpoint to load the cases, but I would also like to create message activities from the posts in our old system. If I use the Cases screen under Organization, I can add a Detail entity for activities. However, there does not appear to be a way to add the Activity Details field, which is the body of the activity. Here is a screenshot of the current endpoint setup showing the top-level Case entity I created:
The above image shows the Case entity, which does not appear to have the ActivityDetails field. However, if I use the Activity screen from the Hidden site map folder, the ActivityDetails is present. Here is a screenshot of the Activity entity I created, which does have ActivityDetails:
I hope this makes sense, but I would like the ActivityDetails field to be available from the Cases top-level entity so I can insert a complete case including activities and their detail. Any help would be greatly appreciated.
Thank you.
This is not a behavior that is possible.
The reason for this is that when you go on that screen using the UI, there is no possibility to add new Event, Task or Activity directly from that screen. The action button that are there only serve as to open the other screens a already create a link to the case from where the action was clicked on.
Since the APIs work by dealing with one screen at the time, it is not possible from the Case screen to create an Activity.
So to create an Activity for a Case, you will first have to create the Activity and then link it to the Case.
In order to do so, you must first add some field to both the Case entity and the Activity Entity.
These fields must be added manually as they are not part of the autocompletion.
For the Case entity, you need to add the following field:
Field Name = "NoteID"
Mapped Object = "Case Summary"
Mapped Field = "NoteID"
Field Value = "GuidValue"
enter code here
For the Activity entity please add the following field:enter code here
Field Name = "RefNoteID"
Mapped Object = "Activities"
Mapped Field = "RefNoteID"
Field Value = "GuidValue"
Once these two fields have been added, you can start adding the activity to the case.
In order to do so:
1) Retrieve the Case on which you want to add the activity using A GET call. You will need to use the value from the NoteID field that has just been added.
2) Create the Activity like you normally would, using a PUT call, but instead of trying to add a value in the RelatedEntityDescription field, add the NoteID value you just retrieved from the Case to the RefNoteID field you just added to the Activity entity. In the response you will be able to see that the Activity was linked to the case by checking the RelatedEntityDescription field.
I am currently working on a site to allow users to search through a custom product catalog. I have been looking around and would love to leverage Orchard CMS to help me develop this site. I have currently gone through Ron Petersons youtube series on custom Orchard Modules and the Skywalker blog series.
I feel like my goal is possible, but I'm looking for some validation on whether my strategy will work within the Orchard framework.
This is my current situation:
I have an default Orchard configuration pointing to a SQL DB (named
Product-Orchard)
I have a custom DAL that points to another SQL DB (named Products).
Products are made up of your typical information (Product Name,
Description, Price, etc).
The custom DAL has a POCO model called Product (with a Repository to
interact with) with the properties Name, Description, Price.
Now, based on the information I read about creating Orchard modules it seems like the method of creating a custom module with custom content is to:
Create a Module through code gen tools (We'll call it ProductModule)
Create a custom Content Part (ProductPart)
Create a custom Content Part Record (ProductPartRecord) to act as the data model for the part.
Create a custom ContentPartHandler (ProductPartHandler) that handles the persistance of the Content Part.
Create a custom Driver that is the entry for preparing the Shapes for rendering of the UI.
Potentially create a Service that interacts with the Drivers?
This is where things start to get jumbled and I'm not sure if this is possible or not. What I would like to do is to create a custom Content Type that is backed by my custom DAL rather than having the data be stored through the ContentPartRecord inside the Product-Orchard DB, but still allow it to be indexed by the Lucene module to allow for searching of the Product catalog.
Is it possible to create a custom ContentType and/or ContentPart that is backed by a different datasource and still leverage the Lucene search capabilities?
In high level terms I'd like a Product ContentType where the ContentItems are actually stored in my secondary database, not the Orchard database (and still want to be able to leverage Lucene search via Projections).
For those searching for a similar answer, the following solution is what I settled on. There is no easy mechanism I could find to interact with a separate DAL and perform the Lucene indexing.
Create the Orchard Module
Create new Content Part/Type via aMigration
Use Orchard Command infrastructure to import data from your secondary database
Use the OnIndexing event in the Content Part handler to allow Lucene to index your datasource.
Create a lookup property (I called mine ConcreateProperty) that is populated through a Service I created in the module to interact with the secondary DAL in the OnLoaded event.
My final Handler looked like this:
public class HomePartHandler : ContentHandler {
public HomePartHandler(IRepository<HomePartRecord> repository, IHomeSearchMLSService homeSearchService) {
Filters.Add(StorageFilter.For(repository));
OnLoaded<HomePart>((ctx, part) =>
{
part.ConcreteProperty = homeSearchService.GetByMlsNumber(part.MlsId) ?? new PropertyDetail();
});
OnIndexing<HomePart>((context, homePart) => context.DocumentIndex
.Add("home_StreetFullName", homePart.Record.StreetFullName).RemoveTags().Analyze().Store()
.Add("home_City", homePart.Record.City).RemoveTags().Analyze().Store()
.Add("home_State", homePart.Record.State).RemoveTags().Analyze().Store()
.Add("home_Zip", homePart.Record.Zip).RemoveTags().Analyze().Store()
.Add("home_Subdivision", homePart.Record.Subdivision).RemoveTags().Analyze().Store()
.Add("home_Beds", homePart.Record.Beds).RemoveTags().Analyze().Store()
.Add("home_Baths", homePart.Record.Baths).RemoveTags().Analyze().Store()
.Add("home_SquareFoot", homePart.Record.SquareFoot).RemoveTags().Analyze().Store()
.Add("home_PropertyType", homePart.Record.PropertyType).RemoveTags().Analyze().Store()
.Add("home_ListPrice", homePart.Record.ListPrice).RemoveTags().Analyze().Store()
.Add("home_MlsId", homePart.Record.MlsId).RemoveTags().Analyze().Store()
.Add("home_Latitude", (double)homePart.Record.Latitude).RemoveTags().Analyze().Store()
.Add("home_Longitude", (double)homePart.Record.Longitude).RemoveTags().Analyze().Store()
);
}
}
This allows me to create a search service for searching through all my data and then hook it up to the model via the Concrete Property, which actually works better from a performance standpoint anyway.
I'm working on an NHibernate project, and where I had trouble loading collections earlier (http://stackoverflow.com/questions/4213506/c-hibernate-criteria-loading-collection), I now have problems using data.
I'm using C# in combination with the NHibernate and Spring.Net framework, and I get an LazyInitializationException after I load for instance an 'ordercredit', and then accessing an object of the ordercredit.
I use this code for getting the OrderCredit:
OrderCredit oc = CreditService.getOrderCredit(ordercredit.Id);
The code I use for loading is done using a DAO implementation:
[Transaction(TransactionPropagation.Required, ReadOnly = true)]
public OrderCredit GetOrderCredit(long ordercreditid)
{
var creditrules = Session.CreateCriteria(typeof(OrderCredit));
creditrules.Add(Restrictions.Eq("Id", ordercreditid));
return creditrules.List<OrderCredit>()[0];
}
When I run this on my local machine, everything works fine, and I actually intended to load a list of those 'ordercredits', but that went wrong as well, so I tried a simpler step first.
The objects within the 'OrderCredit' are defined as [OneToMany].
When I put this on the testserver, and try to access the 'OrderObject' object of the loaded OrderCredit, I get the error:
NHibernate.LazyInitializationException: Initializing[.OrderObject#5496522]-Could not initialize proxy - no Session.
Code that fails:
Log.Debug(oc.OrderObject.Name);
Code that works:
Log.Debug(oc.Id);
This happens for any object that's part of the OrderCredit, but I am able to access the property fields of the OrderCredit (for instance the OrderCredit.Id).
Also, when I access any of the objects BEFORE I return the data to the original function calling the method, then it does cache the information or so, as I can access it then.
I've read a lot about this problem, like turning off Lazy, but that did not work for me either (or I did that on the wrong place).
The thing that frustrates me most, is the fact that it actually does work on my local machine, and not on the testserver. What could I be doing wrong?
Any help is highly appreciated.
1st update:
I am using now a GenericDao, using the default method of loading 1 ordercredit. I use the following code to load 1 ordercredit by Id.
OrderCredit oc = GenericService.Load<OrderCredit>(Id);
The code that's inside the GenericDAO is the following, BUT it does not end or breaks the session, which means I am able to access the objects attached to the ordercredit:
[Transaction(TransactionPropagation.Supports, ReadOnly = true)]
public T Load<T>(long id) where T : ISaveableObject
{
var obj = Session.Load<T>(id);
return obj;
}
This is nearly the same code as I had in the function which I included earlier in this question.
I'm now really confused because I don't know what it could be that ends the session. I will work with it now as it works, but I want to change it later on, so I can use my function to call the entire collection and access them via a for each loop.
Currently, I use my 'getOrderCredits' function to get the list of OrderCredit objects, and in the foreach, I get the Id, and use the GenericDao.Load to get the actual item, and can access the objects and such. Of course this is not the way it should be and needs to be.
I'd be amazed if I get this solved.
This is a common problem people have when using NHibernate. It happens because:
You open a session
You load an entity from the database which references another entity
You close the session
You try to access a property on your referenced entity
NHibernate tries to lazily load the entity from the database using the same session that loaded the parent entity
The session is closed, so NHibernate throws exceptions like woah.
You have a few options here:
Keep your session open longer, preferably using something like the unit of work pattern, which will give you tighter control.
Eagerly load your referenced entities when you query:
In your case as spring is managing your transaction for you the 2nd option is probably the quickest/easiest solution.
var creditrules = Session.CreateCriteria(typeof(OrderCredit));
creditrules.Add(Restrictions.Eq("Id", ordercreditid))
.SetFetchMode("OrderObject", FetchMode.Eager);
This will load the OrderObject when you load the OrderCredit.