Skip to main content

Hello Community,

Does anyone have any experience with mass downloading attachments from Acumatica? I have done a little bit of digging and seems “UploadFileInq” is the graph that manages the files/attachments and the “GetFile” action handles single file downloading. The issue I am facing is I do not see the real implementation code for the graph just declaring the views, filters, actions, and handlers. For example, I see the “GetFile” action but I do not see its method code. Or I see “FilesFilter_RowUpdated” handler but I do not see any code in it but for sure there should be because when we change the filter parameters the result grid changes. Or I can not see any Files select statement tied to the current value of the filters.

I was wondering if this is one of those graphs Acumatica doesn’t like to be accessible for customizations! Any thought or direction to the right path is greatly appreciated.

Ideally, I would like to open “File Download” dialog once to get the download path and download all files that meet the filter criteria.

You may want to introduce a processing screen to select the files of interest and then act on those files.

I once used the following code to extract files from the database and dump to a folder on the server.  You would need to modify for your purposes, but it gives some insight into how to get files.

#region ExportAttachments
public static void ExportAttachments(Guid? fromNoteID, string outFolder)
{
UploadFileMaintenance upload = PXGraph.CreateInstance<UploadFileMaintenance>();

PXResultset<NoteDoc> list = GetFiles(fromNoteID);
foreach (NoteDoc doc in list)
{
PX.SM.FileInfo sourceFile = upload.GetFile((Guid)doc.FileID);

if (!Directory.Exists(outFolder)) Directory.CreateDirectory(outFolder);

string pathOfFile = Path.GetDirectoryName(outFolder) + "\\" + sourceFile.Name;
File.WriteAllBytes(pathOfFile, sourceFile.BinData);
PXTrace.WriteInformation(pathOfFile);
}
}
#endregion

#region GetFiles
public PXResultset<NoteDoc> GetFiles(Guid? noteID)
{
var list = SelectFrom<NoteDoc>
.Where<NoteDoc.noteID.IsEqual<@P.AsGuid>>
.View.Select(this, noteID);
return list;
}
#endregion

Notice how files are linked to objects such as items throuhg NoteDoc.  From NoteDoc, you get the file via GetFile in the UploadFileMaintenance graph by FileID stored in a PX.SM.FileInfo object.  From that object, the actual file data is contained within the BinData field.


@Brian Stevens Thanks for the insight.

It seems the ExportAttachments doesn’t like the GetFile non-static method and if I make the GetFile static am not able to pass the Base graph to select statement. Was there a trick to make this work?

 

Also, don’t you possibly have the processing screen code handy so that I modify it instead of rewriting it?


I extracted the code for GetFiles from the graph I had it in.  It was intended to be code samples, so you’d need to tweak it.

Try this.  I simply added a parameter to pass a graph and adjusted accordingly.

#region ExportAttachments
public static void ExportAttachments(Guid? fromNoteID, string outFolder)
{
UploadFileMaintenance upload = PXGraph.CreateInstance<UploadFileMaintenance>();

PXResultset<NoteDoc> list = GetFiles(upload, fromNoteID);
foreach (NoteDoc doc in list)
{
PX.SM.FileInfo sourceFile = upload.GetFile((Guid)doc.FileID);

if (!Directory.Exists(outFolder)) Directory.CreateDirectory(outFolder);

string pathOfFile = Path.GetDirectoryName(outFolder) + "\\" + sourceFile.Name;
File.WriteAllBytes(pathOfFile, sourceFile.BinData);
PXTrace.WriteInformation(pathOfFile);
}
}
#endregion

#region GetFiles
public static PXResultset<NoteDoc> GetFiles(PXGraph graph, Guid? noteID)
{
var list = SelectFrom<NoteDoc>
.Where<NoteDoc.noteID.IsEqual<@P.AsGuid>>
.View.Select(graph, noteID);
return list;
}
#endregion

This is my processing screen, with some names changed.

using PX.Data;
using System;

namespace MyNamespace
{
public class CUSTMyFileProcess : PXGraph<CUSTMyFileProcess>
{
#region Filters
public PXCancel<CUSTMyItem> Cancel;
public PXProcessing<CUSTMyItem> Items;
#endregion

#region Constructor
public CUSTMyFileProcess()
{
Items.SetProcessCaption("Export");
Items.SetProcessAllCaption("Export All");
Items.SetProcessDelegate<CUSTMyItemEntry>(
delegate (CUSTMyItemEntry graph, CUSTMyItem item)
{
try
{
graph.Clear();
graph.Export(item, true);
}
catch (Exception e)
{
PXProcessing<CUSTMyItem>.SetError(e);
}
});
}
#endregion

#region Overridden Properties
public override bool IsDirty => false;
#endregion

}
}

 


@Brian Stevens really appreciated 


Reply