Skip to main content
Question

Events for File Attachments

  • October 27, 2026
  • 13 replies
  • 50 views
  • Translate All

Is it possible for a customization project to receive an event when a file is attached to a record (project, invoice, etc.)?

The goal is to force a change to the LastModifiedDateTime of the project (for example) when a file is added so that the REST API will return the project when filtering by timestamp, since it seems that Acumatica doesn’t update the timestamp automatically when a file is added.

I have code that was generated with assistance from AI but I’m not sure if the approach is even valid. Should this RowInserted event for NoteDoc work and is the call to SetStatus() sufficient to update the LastModifiedDateTime value?

Trace information confirms that the extension is being loaded and a different event is received but not the event for NoteDoc.

    public class ProjectEntry_Extension : PXGraphExtension<ProjectEntry>
    {
        public static bool IsActive() => true;

        public ProjectEntry_Extension()
        {
            PXTrace.WriteInformation("ProjectEntry_Extension instance constructor invoked");
        }

        // Intercept when a file link (NoteDoc) is inserted
        protected virtual void _(Events.RowInserted<NoteDoc> e)
        {
            PXTrace.WriteInformation("ProjectEntry_Extension: NoteDoc event received");

            NoteDoc row = e.Row;
            if (row == null) return;

            // Fetch the current Project Header from PM301000's primary view
            PMProject project = Base.Project.Current;

            // Verify a project is loaded and matches the file's attached NoteID
            if (project != null && row.NoteID == project.NoteID)
            {
                // Force an update to the project cache status 
                // This flags the record as "dirty" so the platform increments the tstamp
                Base.Project.Cache.SetStatus(project, PXEntryStatus.Updated);

                // Optional: Force an immediate database save.
                // Remove the line below if you want the user to click 'Save' manually.
                Base.Actions.PressSave();
            }
        }
    }
 

13 replies

darylbowman
Captain II
Forum|alt.badge.img+17

If I remember correctly, we’re not supposed to use graph extension constructors. Since it’s only a useless trace, I would remove that part.

I believe this should work when a file is added via the entry screen. I suspect it won’t if a file is added from a Generic Inquiry with files exposed.

Translate

  • Author
  • Freshman I
  • October 27, 2026

​@darylbowman 

The constructor with the trace is there only temporarily until I can confirm that it works. The testing that I’ve been doing has been from the entry screen. Do you have suggestions for how to update this code or an example that could point me in a right direction?

Translate

darylbowman
Captain II
Forum|alt.badge.img+17

If the Generic Inquiry situation is a concern, I would first test it to verify it doesn’t work. A simple way to avoid that situation would be to turn off the ability to update files on the Projects GI.

 

Translate

  • Author
  • Freshman I
  • October 27, 2026

​@darylbowman I’m adding the file from the entry screen, not the GI.

Translate

darylbowman
Captain II
Forum|alt.badge.img+17

Trace information confirms that the extension is being loaded and a different event is received but not the event for NoteDoc.

Sorry, I missed that you had already tested it and it wasn’t working.

Try this event handler instead:

// Intercept when a file link (Note) is being updated
protected virtual void _(Events.RowUpdating<Note> e)

 

Translate

  • Author
  • Freshman I
  • October 27, 2026

I added an event handler like that and it did get called when updating the note but not when uploading a file, so how would that help with files?

Translate

darylbowman
Captain II
Forum|alt.badge.img+17

so how would that help with files?

Oh boy 😂 It’s been a long week.

Have you tried it with NoteDoc?

Translate

  • Author
  • Freshman I
  • October 27, 2026

Are you suggesting to handle the RowUpdating event instead of RowInserted for NoteDoc?  I added handlers for  RowUpdating and RowUpdated for NoteDoc but neither is being called.

Translate

darylbowman
Captain II
Forum|alt.badge.img+17

That is what I was suggesting, based on a project I did in the past with notes. In the process, I got confused about what you were trying to do 😀

Now I’ve done some research and have a different method to try instead:

public class ProjectEntryExt : PXGraphExtension<ProjectEntry>
{
public override void Initialize()
{
Base.FieldVerifying.AddHandler(typeof(PMProject), "NoteFiles", NoteFilesVerifying);
}

private void NoteFilesVerifying(PXCache cache, PXFieldVerifyingEventArgs e)
{
if (!(e.Row is PMProject project) || !(e.NewValue is Guid[] fileIDs) || fileIDs.Length == 0)
return;

// fileIDs = the file(s) just attached, not the full attachment list
}
}

 

Translate

  • Author
  • Freshman I
  • October 27, 2026

Thanks for that example code. Now we’re making progress; the event is being received. Do you have any documentation that explains this reference to “NoteFiles”?  A search turned up info that NotesFiles is a virtual field provided by PXNoteAttribute but an official source would be helpful for future reference.

For the other aspect of this to update the project timestamp, it seems necessary to set the LastModifiedDateTime property; the call to SetStatus wasn’t sufficient by itself, but that was an easy detail to figure out once an event could be handled.

Translate

darylbowman
Captain II
Forum|alt.badge.img+17

... but an official source would be helpful for future reference.

No official documentation. Just notes from having dug around in the source files.

 

Translate

  • Author
  • Freshman I
  • October 27, 2026

Do you have a suggestion for handling file deletion as well?  That seems like a different approach is needed since files are removed using a different screen.

Translate

Steve Milner
Varsity III
Forum|alt.badge.img+6

@tdkannel Yes, deletes need their own handling, and there are two paths to cover. Daryl's NoteFiles hook still covers attaching.

The common one is Delete on the file's own screen (File Maintenance, SM202510). That screen runs on WikiFileMaintenance, and its delete calls the static UploadFileMaintenance.DeleteFile, which removes the NoteDoc rows with PXDatabase.Delete. No NoteDoc event fires. What you can catch is RowPersisting on UploadFileWithIDSelector. An extension's RowPersisting handler runs before the base one, so the links are still there to read. A file on several records returns several NoteIDs.

The other is Remove Link in a record's Files dialog. Acumatica only allows it when the file is linked to more than one record. Otherwise it tells the user to open the file with Edit and delete it there. Remove Link deletes the NoteDoc through an UploadFileMaintenance graph and saves it, so RowDeleted<NoteDoc> fires.

using PX.Data;
using PX.Data.BQL;
using PX.Data.BQL.Fluent;
using PX.SM;

// Delete on the file's own screen (SM202510)
public class WikiFileMaintenance_Extension : PXGraphExtension<WikiFileMaintenance>
{
    public static bool IsActive() => true;

    protected virtual void _(Events.RowPersisting<UploadFileWithIDSelector> e)
    {
        if (e.Row == null || e.Operation.Command() != PXDBOperation.Delete) return;

        // Runs before the base handler, so the links are still there
        foreach (NoteDoc link in SelectFrom<NoteDoc>
            .Where<NoteDoc.fileID.IsEqual<@P.AsGuid>>
            .View.Select(Base, e.Row.FileID))
        {
            // link.NoteID is the record the file was attached to
        }
    }
}

// Remove Link in a record's Files dialog
public class UploadFileMaintenance_Extension : PXGraphExtension<UploadFileMaintenance>
{
    public static bool IsActive() => true;

    protected virtual void _(Events.RowDeleted<NoteDoc> e)
    {
        if (e.Row == null) return;
        // e.Row.NoteID is the record the link was removed from
    }
}

Each NoteID points to the record that lost the file. Look it up and apply your LastModifiedDateTime update. Note.EntityType tells you which DAC it is, which helps once you go past projects.

One catch on projects. If Document Management is turned on, the project's Files button opens Manage Project Files by Tag (PM509000) instead of the dialog. A delete there calls the static DeleteFile in a long operation, so neither handler sees it. That screen's graph, ProjectFilesEntry, would need its own hook. I ran the SM202510 path on 26.101, and the handler read both project links before they were deleted.

Translate