@rbucek what sort of cool things could one do with VSTO plugin? I know the list could be limitless, but what are some useful/practical things we could gain? One thing I can think of is emulating the EPICOR module that allows you to add records to DB through email - which sounds pretty sweet!
I built our own version of crm right into outlook. Works nicely, been done a couple different ways.
Wisconsin Converting Inc.
It’s usually very business specific. Ours was driven by customer requirements. We have to have material traceability back to the steel mills and we get material test reports as pdf’s via email from our suppliers. We need to recall those by job, by assembly when we send out new products to our customers. Tracing these and recalling them from a file cabinet was a nightmare. So I built the pluggin to recognize when it got an email with an attachment of that type. It looked up the supplier by email in the supplier maintenance area of epicor, brought back relevant data, filled in fields on the custom outlook form that was only visible in emails that contained MTR attachments, it gathered other info from the subject line and body, automatically saved the attachment in our file server and created an index record in a UD table in Epicor. It was simple to write a dashboard at that point that prompted a job number and voila, a list of all MTR’S that they could print from within Epicor along with the assembly traceability. We took a task that required almost 20 man hours a week to manage into something that took an hour or two.
Damn Rob, is that ALL it did? Lol, very impressive
Josh’s CRM thing is way cooler.
I could spend some serious time in here
I’ll throw some guidelines I give to internal devs on GetDtlPOLineInfo.Post.EmailExample.cs. Some items are religious style so take those with a grain of salt. Other comments are from scars of picking up others code (I love the old saying - Always write your code like the person taking over is a serial killer with your address)
First reading the code is kind of difficult to pickup on areas due to the verbose nature. Putting in
’foo.X == false’ is the same as !foo.X. Same goes for bar.Y==true. That’s just a bar.Y statement. It’s a true or false value already so no need to make the code longer and harder to read.
Along that same line there are some complex action statements - a couple repeated I think:
r1.SysRowID == r.SysRowID
&& r1.JobReleased != (r.JobReleased)
&& r1.JobReleased == (false)));
what the heck is that comparison you are doing? Will someone know 6 months from now (or yourself next week?)
Refactor that out into its own method and reuse it. Especially when you are doing the same comparison in a couple of areas. If you change the logic at some point you may only fix one, not two. If you pull that out into a common method you prevent breaking things in the future. Furthermore you can name the method something explanatory and avoid the need to put in comments as you name the method what it does - self documenting.
Another positive in pulling out things into a common method is the ability to create Unit Tests to verify functionality. That way when you come back and fix things in 6 months, you run the tests and realize you just broke things - avoiding those long weekends when you mess up a deployment
That is also the reason to remove the goto statements and make those if then queries instead against refactored out methods Then those methods can again be unit tested…
Another item that makes things difficult to follow is the numerous parameters passed around:
Action<string, string, string, string, string, string, string, string, decimal, decimal, decimal, decimal, decimal, DateTime> InsertUDRecord
= (workOrder, action, type, jobNum, partNum, material, rev, opCode, asmbl, qty, rqdQty, oprSeq, mtlSeq, startdate)
Instead of passing these long lists of variables around, create a new class named appropriately to describe this structure and populate an instance, pass that around, etc. If you need to add a field then you change less code as well.
WARNING - Memory ‘Leak’
var UD32svc = Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD32SvcContract>(Db);
This create an instance of the BO you want to call. That BO create a db connection. When you are done with the BO you should dispose it as soon as possible. If you don’t, it sits around until the end of the server call where the framework walks through all the BOs and libs laying around and cleaning them up. In most cases this might not be a bad but in the case of a report or process or long bo method this may suck up all available db connections until the dotNet garbage collector comes along in the background to clean things up. Think MRP or Posting Engine and those calls can take some time starving the app server from accessing the db.
Instead, wrap the stand up of BOs and Libs in using statements:
using (var UD32svc = Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD32SvcContract>(Db))
This gives back the db connection and frees memory asap.
WCF Bindings helper…
Take a look at the ImplFactory in Epicor.ServiceModel.dll. / Epicor.ServiceModel.Channels.ImplFactory.CreateImpl()
This has several overloads to create client proxies easily with most of the differing parameters of interest. It’s used by Task Agent, all Client BOs, Social, Search, …
GetCustomBinding() also will help with setting up all the various binding types ‘in the can’ for E10 -
Epicor.ServiceModel.Bindings.BindingName has constants for all those bindings.
NOTE - These have evolved over the releases so have more overloads from 10.0 to 10.1.400 to 10.1.500, etc.
@Bart_Elia lol that’s code Epicor stubs out from the condition element used in the BPM designer…so I don’t know? Not much i can do there. That being said, thank you so much, your feedback is great! Just what i needed to hear, lots of things to think about… the only code i wrote is in
so if you are working within a BPM and Epicor takes your custom code action, creates a method out of it, how do you refactor anything into it’s own method? I understand i could technically build a library, and make a bunch of classes with methods in it, reference that, but most of this is so specific, I’ll never ever use it anywhere else in the system? Just curious, I’m not a coder by background or trade, but i do enjoy doing it when the task requires it. There are a lot of variables being used in that action and it looks clunky yes, there’s that class question again, how to create that within the scope of the custom action code element, so much to learn lol, what are the limitations there and how to work within that?
Ahhh! Sorry, like I mentioned before if you are doing code in BPM I personally - (Not a company endorsement here but a personal feeling) - I personally think that if you go into writing code it would benefit more from creating an external assembly for BPMs and calling out to it for all the normal reasons - version control, unit testing, stability, etc.
I did not really look at the business process you are working at to see if you ‘need’ code as opposed to the BPM designer widgets. We have been slowly increasing the UI widgets available in BPM to try to make that more of an actionable designer and fit more customer scenarios - less code.
Now I’ll have to go back and load up your sample to look again
What is the reason for doing your own DLL? My concern with going with external DLL’s is the constant re-compiling. If you use any of Epicor’s DLL’s in your custom DLL then that is tied to whatever Epicor version X. When you guys release a patch every 2 weeks… or a service pack I’ll have to constantly re-compile my custom DLL to work with the new version of the Epicor DLL. Am I wrong? do you guys not increment the versions of the DLLs with patches / service packs?
I’ve run into this issue several times, but maybe there is something I am missing.
Just curious if there is a better way to handle that.
But I have that as just another deployment step in the process of pushing out patchs as part of the scripted patching.
It’s something we are working on improving so I understand your concern. It’s all trade offs as everything in architecture. There is something I am in the middle of in this area that hopefully we can talk to at Insights to really deal with this. Stay tuned and thanks for confirming some of what I am taking to the table to argue for internally
Thanks Bart!, and thanks for lurking around here!
Happy to lurk. Gives me some insight to pain points to abuse people with internally and confirms some fixes i have been considering
I second that, glad to have you around. Glad you’re not Nathan Anderson, I’ve done really pissed that guy off
Hey @Chris_Conn Nathan Anderson is awesome! What did you do to piss him off? He is the best support guy that Epicor has!
Yeah…. Nathan is not the bear I would want to poke that’s for sure.
well… I got frustrated. So I had been working for 1.5 weeks in email to try to explain a bug I had found. Note, I didn’t create a ticket, I just mentioned it in a comment email where I praised E10 and offered some wishlists, they created a ticket from it.
The gent (not Nathan) I was working with kept asking me silly questions, some of which I had already answered. After about a week and half into my issue (server printers not listing for a specific condition) he asks:
Do you have a server printer configured?
To which I respond:
Cmon, that question is kind of offensive. Why would I say it wont list my server printers if I didn’t have any configured?
At this point I get this from Nathan:
I am taking this call over from Mitch as I feel your response to his reasonable question was borderline inappropriate and non-productive
Essentially my response started with “Wow” and ended with “if I am too difficult to work with, close the ticket, I’ll code around the problem”
That support ticket escalated in all the wrong ways haha!