Epicor 10 - Changing the plant for the current session - epicorerp

We are starting to use Epicor 10 and we are wanting to leverage the DLLs/Services to talk to Epicor. That way we can add/update info from a custom app. So far things are going fine but we ran into an issue when we wanted to, lets say, add a job for a specific plant. It seems to always want to save to the plant the user last logged into via the client app.
So for example -- Lets say the user's last plant was plant "A". I want my custom app to log into Epicor (creating a session) and create a job for plant "B". I can add the job fine, but it will put it under plant "A" and not "B", even though I logged into plant "B" when I created the session.
We are calling SetPlant and passing in the right plant we want, but Epicor seems to always override what plant to save it as. Has anyone else run into this case? We are also having this issue with the Company. SetCompany doesnt seem to work at all.
More info: - We are using the net.tcp:///ERP/ICE/Lib/SessionMod.svc service. - We can login fine with Login() and get a SessionId back - Even calling GetValues() on the Session object says we are logged into plant "B" even though Epicor will still use plant "A".
The only work around we can come up with, which we do not want to do, is to have an app user by company and by plant so we can guarantee which company and plant the data gets saved to. This will work but it isnt ideal.
Anyone have any suggestions on how to get the system to take the new Company or Plant?

As user463132 points out, you can wrap your service connection with the temporary session context:
using (CallContext.Current.TemporarySessionCreator.SetCompanyID("YourCompanyHere").SetPlantID("B").Create())
{
}
Haso Keric Article Reference
I'll also add that if you are using the UI adapters, you can simply grab the session from the oTrans object instance and set the properties here which governs how oTrans interacts with your data.
Ice.Core.Session s1 = (Ice.Core.Session)this.oTrans.Session;
s1.CompanyID = "YourCompanyHere";
s1.PlantID = "B";

The plantID can be updated in the session using the SetPlant() method. It must be called after the SetCompany():
sessionModImpl = NetTcp_Helper.ClassAttributHelper.CreateBusObj<SessionModImpl>(Guid.Empty, SessionModImpl.UriPath, Settings);
sessionId = sessionModImpl.Login();
sessionModImpl.SessionID = sessionId;
sessionModImpl.SetCompany(epicorCompanyID, out companyName, out plantID, out plantName, out workstationID, out workstationDescription, out employeeID,
out countryGroupCode, out countryCode, out tenantID);
sessionModImpl.SetPlant(newSiteID, out plantName);

The first thing to understand is that a Server Session is not a single
instance but rather a .NET Stack of Session Instances. The
‘CallContext.Current.Session’ variable is just a pointer to the top of
the Stack. In most cases, there is just a single Session instance in
the CallContext stack. But when you need to iterate over Companies to
process something the Session Stack get pushed and popped around.
That’s where ‘TemporarySessions’ comes in.
Read about it at:
https://www.linkedin.com/pulse/snippet-epicor-change-companyplantuser-bpm-haso-keric/

Related

Best practice for subscribe and publish architecture for chat-like app

I'd like to know what best practices exist around subscribing to changes and publishing them to the user. This is a pretty broad and vaguely worded question. Therefore, allow me to elaborate on this using an example.
Imagine the following (simplified) chat-like application:
The user opens the app and sees the home screen.
On this home screen a list of chat-groups is fetched and displayed.
Each chat-group has a list of users (members).
The user can view this list of members.
Each user/member has at least a first name available.
The user can change its name in the settings.
And now the important part: When this name is changed, every user that is viewing the list of members, should see the name change in real-time.
My question concerns the very last point.
Let's create some very naive pseudo-code to simulate such a thing.
The client should at least subscribe to something. So we could write something like this:
subscribeToEvent("userChanged")
The backend should on its part, publish to this event with the right data. So something like this:
publishDataForEvent("userChanged", { userId: "9", name: "newname" } )
Of course there is a problem with this code. The subscribed user now gets all events for every user. Instead it should only receive events for users it is interested in (namely the list of members it is currently viewing).
Now that is the issue I want to know more about. I could think of a few solutions:
Method 1
The client subscribes to the event, and sends with it, the id of the group he is currently viewing. Like so for example:
subscribeToEvent("userChanged", { groupId: "abc" })
Consequently, on the backend, when a user changes its name, the following should happen:
Fetch all group ids of the user
Send out the event using those group ids
Something like this:
publishDataForEvent("userChanged", { userId: "9", name: "newname" }, { groupIds: ["abc", "def" })
Since the user is subscribed to a group with id "abc" and the backend publishes to several groups, including "abc", the user will receive the event.
A drawback of this method is that the backend should always fetch all group ids of the user that is being changed.
Method 2
Same as method 1. But instead of using groupIds, we will use userIds.
subscribeToEvent("userChanged", { myUserId: "1" })
Consequently, on the backend, when a user changes its name, the following should happen:
Fetch all the user ids that relate to the user (so e.g. friendIds based on the users he shares a group with)
Send out the event using those friendIds
Something like this:
publishDataForEvent("userChanged", { userId: "xyz", name: "newname" }, { friendIds: ["1", "2" })
An advantage of this is that the subscription can be somewhat more easily reused. Ergo the user does not need to start a separate subscription for each group he opens, since he is using his own userId instead of the groupId.
Drawback of this method is that it (like with method 1 but probably even worse) potentially requires a lot of ids to publish the event to.
Method 3
This one is just a little different.
In this method the client subscribes on multiple ids.
An example:
On the client side the application gathers all users that are relevant to the current user. For example, that can be done by gathering all the user ids of the currently viewed group.
subscribeToEvent("userChanged", { friendIds: ["9", "10"] })
At the backend the publish method can be fairly simple like so:
publishDataForEvent("userChanged", { userId: "9", name: "newname" }, { userId: "9" } )
Since the client is subscribed to user with userId "9", amongst several users, the client will receive this event.
Advantage of this method is that the backend publish method can be held fairly simple.
Drawback of this is that the client needs quite some logic to subscribe to the right users.
I hope that the examples made the question more clear. I have the feeling I am missing something here. Like, major chat-app companies, can't be doing it one of these ways right? I'd love to hear your opinion about this.
On a side note, I am using graphql as a backend. But I think this question is general enough to not let that play a role.
The user can change its name in the settings.
And now the important part: When this name is changed, every user that is viewing the list of members, should see the name change in real-time.
I assume the user can change his name via a FORM. The contents of that form will be send with a HTTP-Reuqest to a backand script that will do the change in a DB like
update <table> set field=? where userid=?
Preferred
This would be the point where that backend script would connect to your web socket server and send a message like.
{ opcode:'broadcast', task:'namechange', newname='otto' ,userid='47111' }
The server will the broadcast to all connected clients
{task:'namechange', newname='otto' ,userid='4711' }
All clients that have a relationship to userid='4711' can now take action.
Alternative 1
If you cant connect your backend script to the web socket server the client might send { opcode:'broadcast', task:'namechange', newname='otto' ,userid='47111' }
right before the FORM is trasnmitted to the backend script.
This is shaky because if anything goes wrong in the backend, the false message is already delivered, or the client might die before the message goes out, then no one will notice the change.

How to retrieve the process's unique workflow number, while launching it through C# API?

I'm struggling for 4 days now.
There's this C# Process Engine API:
https://www.ibm.com/support/knowledgecenter/en/SSNW2F_5.2.1/com.ibm.p8.pe.dev.doc/web_services/ws_reference.htm
What I need to do is to retrieve the WorkflowNumber when launching the workflow, so later I can find that process in the system.
The issue here is that when you launch it - it returns the LaunchStep (the first step in the workflow) which doesn't have that ID assigned yet - it's null. The only thing available is the LaunchStep's WOBNumber.
In order to assign the Workflow ID to the step, you need to dispatch the step, so I do that:
UpdateStepRequest request = new UpdateStepRequest();
UpdateFlagEnum flag = UpdateFlagEnum.UPDATE_DISPATCH;
request.updateFlag = flag;
request.stepElement = element; // add the launch step
session.Client.updateStep(request);
And here the funny part happens. From this point, there is complately no option to retrieve that, because StepElements are stateless, updateStep() returns nothing and the best part - the LaunchStep is now destroyed in the system, because it's a LaunchStep - it just gets destroyed after the launch.
Any tips would be appreciated!

getting/setting browser_id with Products.BeakerSessionDataManager

I'm having a problem associating a browser_id to a session when using Products.BeakerSessionDataManager. I'm working on Plone 5.
As far as I understand Zope sessions, as soon as .getSessionData() is called on a session data manager, a session data container is created if it did not exist. Furthermore, this data will contain a token, which is the same as the browser_id associated with the browser making the request. And finally, a cookie is set on the response with the name _ZopeId (and the value is the same as the token). Thus, when I use the default session data manager that come with Zope, I get this:
ipdb> context.session_data_manager.getSessionData()
id: 14737473151418102847, token: 38878600A7nh90DE9ao, content keys: []
However, when I use Products.BeakerSessionDataManager, the same call gives me this:
ipdb> context.session_data_manager.getSessionData()
{'_accessed_time': 1473745441.437582, '_creation_time': 1473745441.437582}
Moreover, no cookie is set.
Perusing some old Zope docs, I found a reference to getContainerKey(), so I thought that might get me the browser_id. However, the returned value is different on every request, so that does not work. Also, calling .getBrowserIdManager().getBrowserId() on the session_data_manager throws an error, because Beaker does not support browser id managers.
I want to set a cookie, and I want a token. I'm doing this so that I can identify anonymous clients in a voting application, so that they will not cast multiple votes (at least not in the same session - there are other mechanisms to allow voting only when certain other conditions are met).
Am I misunderstanding the machinery, or am I missing something?

OS X Today Widget - How to detect in NCWidgetProviding.widgetPerformUpdateWithCompletionHandler if content has changed?

widgetPerformUpdateWithCompletionHandler() gives us the possibility to let the Notification Center know if the content of a Today Extension has changed. For example:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
// Refresh the widget's contents in preparation for a snapshot.
// Call the completion handler block after the widget's contents have been
// refreshed. Pass NCUpdateResultNoData to indicate that nothing has changed
// or NCUpdateResultNewData to indicate that there is new data since the
// last invocation of this method.
if(has_new_data()) { // There was an update
completionHandler(.NewData)
}
else { // nothing changed!
completionHandler(.NoData)
}
}
But how would we know if the content has changed? On every snapshot the widget is instantiated from scratch. It is a complete new process with new PID. So you can not store any property in your instance. How would one compare the current widget content with the content of the previous snapshot?
I used Core Data to store the current content for later comparison. This is obvious and works. But then another problem crashes in: What if there is no previous snapshot? Let's say the user removed the widget just to re-add it again. Or the user rebooted. There might be more reasons why there is no previous snapshot that I can not think of now. Either way - there still is content stored in Core Data. If the comparison between this old content and the current content detects there are no changes and I return .NoData the widget would end up empty because the Notification Center would not redraw the content.
You might wonder why It is so important to me to call the completionHandler with a correct state and not simply always return .NewData. That's because I am experiencing a little flicker when there is no change and still return .NewData. I have some images in my widget and when redrawing the widget the whole content gets invisible for a millisecond - long enough to notice.
Is there something I am missing? It seems strange to me that Apple provides a way to give us the option to respond with different states but then makes it impossible to detect which state we should respond.
In theory you would use this to check whether there was any new content since the last call, and pass back the appropriate value. I suppose in theory it might be the same instance of the view controller on more than one call, but that's clearly not how things work right now. Checking whether content has changed depends on the nature of the app and the extension. Since you're using a shared Core Data store to share data, you might do something like:
Any time the app changes data, save the current date in shared user defaults.
Any time the today extension reads data, save the current date in shared user defaults.
When widgetPerformUpdateWithCompletionHandler is called, look up both of those dates. If the data has changed more recently than the last time the extension read that data, return NCUpdateResultNewData. If not, return NCUpdateResultNoData.
You could also save these dates in the metadata on the persistent store instead of in shared user defaults. If it was the same view controller each time, you might keep the value from step 2 in memory instead of saving it to a file. But again that's not how it works now, and it's not clear when or if that will change.
Apps that save data in some other way might need to use different checks, the details of which would depend on how their app and extension worked.
In practice with iOS 8.2 it really doesn't matter because the extension environment doesn't seem to care what value you send back. I tried returning NCUpdateResultNewData and compared it to returning NCUpdateResultNoData every time. There was absolutely no effect on the life cycle of the today extension view controller or its views.
As an aside, it's not always a different process. I tried putting this line in the today extension's viewDidLoad:
NSLog(#"viewDidLoad pid=%d self=%#", getpid(), self);
Then I ran the today extension, scrolled up and down in the notification center a couple of times, closed the notification center, reopened it, and got the following:
2015-03-17 15:19:01.203 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d508cc0>
2015-03-17 15:19:14.441 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d50ade0>
2015-03-17 15:19:23.784 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d619c40>
2015-03-17 15:19:29.015 DemoToday[3484:903442] viewDidLoad pid=3484 self=<TodayViewController: 0x12d50abe0>
Although it's a different instance of the view controller each time, it's the same pid in each of these cases.
You can use any storage API ( Core Data, NSCoding, SQLite ) as long as you enable data sharing with your host app.
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html
Even if the user removes the widget or reboots the device, the data will be stored on your shared container. Whenever iOS launches your widget, you will be able to read and write the same shared container that you have previously used.
Be aware that your host app and your widget may read or write concurrently so you have to coordinate any reading and writing operation.
The simplest way to do this is to cache your current widgets state in user defaults. Then when the widget loads (in viewDidLoad) fetch your cached data from user defaults and set it as a property on your widget view controller.
Then when widgetPerformUpdateWithCompletionHandler is called you have your previous updated and you can decide if you need to do a network request of if your data is fresh enough. If you do a web request you can then compare it to your cache to determine if you have new data or no update.
No need to persist any data in UserDefaults or even Core Storage. Keep it simple: Just declare the variable(s) you use to store any calculated contents or data you use to check whether something changed to be static. Since the widget runs in the same process (as shown here) the static data will be available the next time your widget is activated.
static NSDate *lastDate;
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
NSDate * currentDate = [NSDate date];
if (!lastDate ||
[[NSCalendar currentCalendar]compareDate:lastDate toDate:currentDate toUnitGranularity:NSCalendarUnitDay] != NSOrderedSame) {
// Date changed
lastDate = currentDate;
... do whatever needs to be done ...
completionHandler(NCUpdateResultNewData);
} else {
completionHandler(NCUpdateResultNoData);
}
}

Read a specific ColdFusion session

We are noticing a number of issues relating to our website for shopping cart data.
As the cart data is stored in the user's session, I'm wondering whether it's possible to open a specific session from a script and dump out the output if we have the session ID?
It would be useful to know so we can see what is specifically in a user's session that may be causing the issues to occur.
Yes you can:
<cfscript>
app = 'YOUR_APPLICATION_NAME';
sessiontracker = createObject("java","coldfusion.runtime.SessionTracker");
sessionCollection = sessionTracker.getSessionCollection(app);
structEach(sessionCollection, function(k,v){
writeDump(sessionCollection[k]);
});
</cfscript>
This script basically lists all the session objects for a given application on your server. You can change it to your needs to find out the relevant session based on the keys you put in your session.
thisSession = sessionCollection[k];
//loop through thisSession keys
I do not have a working CF box with me now to test this to perfection; but I believe you will be in the right direction.

Resources