I was procrastinating HARD on documenting this, but your post made me feel bad, so here it is.
Disclaimer
I won't pretend that this is the best way to acheive this, maybe some of you goddam wizard have some better solution. Comments are more than welcome.
Business Requirements
- We are using the vanilla Epicor attachment feature.
- Our #1 requirement was absolutely no client customization ; we wanted 100% server actions so that it can (hopefully) be easily pushed to Kinetic. So this thing works on 100% BPMs and Epicor Functions
- I would highly recommend to assign each BPM to the same group, to help debugging
Prerequisite : Epicor Function to generate the unique filename and assign it to the current user
The process of uploading a file invloves many method directives and data directives BPMs. We ran tests like crazy and realized that it is not possible to pass data between the BPMs with CallContextBPMData. So, if we set a unique partnum on the first BPM, it is not possible to pass that partnum to the next BPMs. And using a time stamp imply the risk that the time stamp on the next BPM might be different, which would result on a mismatch on the unique partnum. Also, what happens if two users tries to upload a file at the same time ? The time stamp would not work in this scenario.
So, our solution was that, at the very moment a user tries to upload an attachment, we would increment an ID (using Ice.Lib.NextValue) and write it to a UD Field on the UserFile table for the current user. Our hypothesis here is that any given user will not upload more than one file within some 2-3 seconds that it takes for all the BPMs to fire. It is reasonnable in our case, but that may not be the same for your business. BEWARE
Anyway, here’s the Epicor Function:
Step 1 : Post-Proc Method directive on Ice.BO.Attachment.FileExists
This is the first method that fires when you add an attachment with the client. This method checks if the file name already exist and triggers the client prompt. Since we will have unique filename, we want to bypass that.
Step 2 : Pre-proc Method Directive on Ice.BO.Attachment.UploadFile
As per my limited understanding, this is the method that actually copy the file on the server. This is the moment to define the unique file name
Here we’re calling the Epicor Function created previously
Here we replace the original filename with the new one
Step 3 : In-Tran Data Directive on XFileRef
After Epicor copied the file to the server with the previous BPM (Ice.BO.Attachment.UploadFile), now the file is officially attached to the record (QuoteHed, OrderHed, ...) via the XFileAttach and XFileRef table. This BPM is necessary to replace the filename by our new one.
Step 4 : Pre-Proc and Post-Proc Method Directive to refresh the client data after the file upload
This step was overlooked, but hopefully we caught it before shit hit the fan. Steps 1-3 is all you need to make this thing work. But, the client is unaware that the filename has changed, so if a user tries to edit the file, corruption happens. So we need to create a BPM on update/Master update for
all the BOs that might receive an attachment. Here is an example for Project :
And this nifty little line of code attach the tableSet that we just got from the GetByID to the transaction and sends it back to the client.
Finally, rinse and repeat for every BOs that might receive an attachment:

Real life example
Here's how it goes for the user:
And you can view, edit and delete the file without problem.