Skip to main content
Solved

Mass Download Attachments from Acumatica

  • February 3, 2023
  • 4 replies
  • 721 views

aaghaei
Captain II
Forum|alt.badge.img+10

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.

Best answer by Brian Stevens

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

    }
}

 

View original
Did this topic help you find an answer to your question?

4 replies

Forum|alt.badge.img+4

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.


aaghaei
Captain II
Forum|alt.badge.img+10
  • Author
  • Captain II
  • 1199 replies
  • February 3, 2023

@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?


Forum|alt.badge.img+4
  • Pro III
  • 135 replies
  • Answer
  • February 3, 2023

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

    }
}

 


aaghaei
Captain II
Forum|alt.badge.img+10
  • Author
  • Captain II
  • 1199 replies
  • February 3, 2023

@Brian Stevens really appreciated 


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings