Skip to main content
Solved

How to Allow Selecting Completed Project Tasks in the Subcontract Screen (POLine.TaskID)?

  • Oct 26, 2026 2:31 PM UTC
  • 3 replies
  • 23 views
  • Translate All

Forum|alt.badge.img

Hi everyone,

I'm customizing the existing TaskID field from the POLine DAC on the Subcontract screen (SC301000) to allow selecting tasks belonging to completed projects.

Currently, when I remove the hold from a subcontract, I receive the following error:

The {0} project is completed.

Customization Code

public class SubcontractEntry_Extension
: PXGraphExtension<SubcontractEntry>
{
[PXDefault(
typeof(Search<PMTask.taskID,
Where<PMTask.projectID,
Equal<Current<POLine.projectID>>,
And<PMTask.isDefault, Equal<True>>>>),
PersistingCheck = PXPersistingCheck.Nothing)]

[PXRemoveBaseAttribute(
typeof(ActiveOrInPlanningProjectTaskAttribute))]

[PXDimensionSelector(
"PROTASK",
typeof(Search<PMTask.taskID,
Where<PMTask.projectID,
Equal<Current<POLine.projectID>>>>),
typeof(PMTask.taskCD),
typeof(PMTask.taskCD),
typeof(PMTask.description),
typeof(PMTask.status),
DescriptionField = typeof(PMTask.description))]

protected virtual void POLine_TaskID_CacheAttached(
PXCache cache)
{
}
}

I tried Merge, Append, and Replace with PXMergeAttributes, but none resolved the issue.

The field now appears as a selector, but the existing value is not displayed, and the selected value disappears after refreshing the screen.

 

Any suggestions or working examples would be greatly appreciated.

Thank you!

Best answer by Steve Milner

@mrthanhkhoi Daryl's attribute swap handles the selector, with two additions. Pass the project field and module when you apply it, and set AllowCompleted: [NotCanceledProjectTask(typeof(POLine.projectID), BatchModule.PO, AllowCompleted = true)]. Without AllowCompleted, BaseProjectTaskAttribute's own FieldVerifying still rejects a completed task unless the user has the Project Accountant role. His attribute also brings back the [PXDBInt]. That isn't on the DAC field itself. It comes in with the task attribute, so your version with a bare PXDimensionSelector had nothing binding the field to the column.

The Remove Hold error is a separate check that no attribute on TaskID can reach. With cost commitment tracking on, Remove Hold builds a PMCommitment record from each project line and copies the line's project and task into it. PMCommitment.ProjectID rejects completed projects and PMCommitment.ProjectTaskID rejects completed tasks. On 26.101, Tools > Trace shows "The HMBAKERY2 project or contract is completed." next to an error on ProjectTaskID, which is the commitment's field name. The line's is TaskID.

This is the version I ran on 26.101. The completed task shows in the lookup and stays on the line after a save, and Remove Hold goes through and creates the commitment on the completed project.

```csharp
using PX.Data;
using PX.Objects.CN.Subcontracts.SC.Graphs;
using PX.Objects.GL;
using PX.Objects.PM;
using PX.Objects.PO;

namespace SubcontractCompletedTasks
{
    public class SubcontractEntry_Extension : PXGraphExtension<SubcontractEntry>
    {
        [PXMergeAttributes(Method = MergeMethod.Merge)]
        [PXRemoveBaseAttribute(typeof(ActiveOrInPlanningProjectTaskAttribute))]
        [BaseProjectTask(typeof(POLine.projectID), BatchModule.PO, AllowCompleted = true, DisplayName = "Project Task")]
        [PXRestrictor(typeof(Where<PMTask.isCancelled, Equal<False>>), PX.Objects.PM.Messages.ProjectTaskIsCanceled, typeof(PMTask.taskCD))]
        protected virtual void _(Events.CacheAttached<POLine.taskID> e) { }

        protected virtual void _(Events.FieldVerifying<PMCommitment, PMCommitment.projectID> e)
        {
            if (PMProject.PK.Find(Base, (int?)e.NewValue)?.IsCompleted == true)
                e.Cancel = true;
        }

        protected virtual void _(Events.FieldVerifying<PMCommitment, PMCommitment.projectTaskID> e)
        {
            if (PMTask.PK.Find(Base, e.Row?.ProjectID, (int?)e.NewValue)?.IsCompleted == true)
                e.Cancel = true;
        }
    }
}
```

The two FieldVerifying handlers only step in when the project or task is completed. They bypass a check Acumatica put there on purpose, so try it in a test tenant first.

View original

3 replies

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

I would try creating your own attribute based on BaseProjectTaskAttribute:

[PXRestrictor(typeof(Where<PMTask.isCancelled, Equal<False>>), "Task is Canceled and cannot be used for data entry.", new Type[] { typeof(PMTask.taskCD) })]
public class NotCanceledProjectTaskAttribute : BaseProjectTaskAttribute
{
public NotCanceledProjectTaskAttribute(Type projectID)
: base(projectID)
{
Filterable = true;
}

public NotCanceledProjectTaskAttribute(Type projectID, string Module)
: base(projectID, Module)
{
Filterable = true;
}
}

Then swap it:

public class SubcontractEntry_Extension
: PXGraphExtension<SubcontractEntry>
{
[PXRemoveBaseAttribute(
typeof(ActiveOrInPlanningProjectTaskAttribute))]
[NotCanceledProjectTask]
[PXDefault(
typeof(Search<PMTask.taskID,
Where<PMTask.projectID,
Equal<Current<POLine.projectID>>,
And<PMTask.isDefault, Equal<True>>>>),
PersistingCheck = PXPersistingCheck.Nothing)]
protected virtual void POLine_TaskID_CacheAttached(
PXCache cache) { }
}

 

Translate

Steve Milner
Varsity III
Forum|alt.badge.img+6
  • Varsity III
  • Answer
  • Oct 26, 2026 5:49 PM UTC

@mrthanhkhoi Daryl's attribute swap handles the selector, with two additions. Pass the project field and module when you apply it, and set AllowCompleted: [NotCanceledProjectTask(typeof(POLine.projectID), BatchModule.PO, AllowCompleted = true)]. Without AllowCompleted, BaseProjectTaskAttribute's own FieldVerifying still rejects a completed task unless the user has the Project Accountant role. His attribute also brings back the [PXDBInt]. That isn't on the DAC field itself. It comes in with the task attribute, so your version with a bare PXDimensionSelector had nothing binding the field to the column.

The Remove Hold error is a separate check that no attribute on TaskID can reach. With cost commitment tracking on, Remove Hold builds a PMCommitment record from each project line and copies the line's project and task into it. PMCommitment.ProjectID rejects completed projects and PMCommitment.ProjectTaskID rejects completed tasks. On 26.101, Tools > Trace shows "The HMBAKERY2 project or contract is completed." next to an error on ProjectTaskID, which is the commitment's field name. The line's is TaskID.

This is the version I ran on 26.101. The completed task shows in the lookup and stays on the line after a save, and Remove Hold goes through and creates the commitment on the completed project.

```csharp
using PX.Data;
using PX.Objects.CN.Subcontracts.SC.Graphs;
using PX.Objects.GL;
using PX.Objects.PM;
using PX.Objects.PO;

namespace SubcontractCompletedTasks
{
    public class SubcontractEntry_Extension : PXGraphExtension<SubcontractEntry>
    {
        [PXMergeAttributes(Method = MergeMethod.Merge)]
        [PXRemoveBaseAttribute(typeof(ActiveOrInPlanningProjectTaskAttribute))]
        [BaseProjectTask(typeof(POLine.projectID), BatchModule.PO, AllowCompleted = true, DisplayName = "Project Task")]
        [PXRestrictor(typeof(Where<PMTask.isCancelled, Equal<False>>), PX.Objects.PM.Messages.ProjectTaskIsCanceled, typeof(PMTask.taskCD))]
        protected virtual void _(Events.CacheAttached<POLine.taskID> e) { }

        protected virtual void _(Events.FieldVerifying<PMCommitment, PMCommitment.projectID> e)
        {
            if (PMProject.PK.Find(Base, (int?)e.NewValue)?.IsCompleted == true)
                e.Cancel = true;
        }

        protected virtual void _(Events.FieldVerifying<PMCommitment, PMCommitment.projectTaskID> e)
        {
            if (PMTask.PK.Find(Base, e.Row?.ProjectID, (int?)e.NewValue)?.IsCompleted == true)
                e.Cancel = true;
        }
    }
}
```

The two FieldVerifying handlers only step in when the project or task is completed. They bypass a check Acumatica put there on purpose, so try it in a test tenant first.

Translate

Forum|alt.badge.img
  • Author
  • Jr Varsity II
  • Oct 28, 2026 1:54 PM UTC

Thank you ​@darylbowman  and ​@Steve Milner . Much appreciate your support.
Steve is correct. The key points here are two FieldVerifying handlers which allowing by pass validation on purpose.

Translate