Skip to main content
Solved

View override not firing


Joe Schmucker
Captain II
Forum|alt.badge.img+2

I am trying to restrict the Attributes that show in the Attributes tab on the Project Entry screen.

The Answers view on this graph is not a standard PXSelect.  The first two lines are from the original graph.

I’ve tried using  public virtual IEnumerable answers() and  public virtual IEnumerable projectAnswers()

but neither of those fire my view override.  I have a breakpoint on PXView currentView = Base.Answers.View; but it is never reached.

 

//[PXViewName(Messages.ProjectAnswers)]
//public TemplateAttributeList<PMProject> Answers;

public virtual IEnumerable answers()
{

    PXView currentView = Base.Answers.View;

    //var view = new PXView(Base, false, myDetails.View.BqlSelect);
    //var startRow = PXView.StartRow;
    var startRow = 0;
    int totalRows = 0;

    foreach (object row in currentView.Select(PXView.Currents, PXView.Parameters, PXView.Searches, PXView.SortColumns, PXView.Descendings, PXView.Filters, ref startRow, PXView.MaximumRows, ref totalRows))
    {
        CSAttributeGroup attributeGroup = PXResult.Unwrap<CSAttributeGroup>(row);

        ICSProjTemplateAttrLinks item = SelectFrom<ICSProjTemplateAttrLinks>
            .Where<ICSProjTemplateAttrLinks.contractID.IsEqual<@P.AsInt>
                .And<ICSProjTemplateAttrLinks.attributeID.IsEqual<@P.AsString>>>
                .View.Select(Base, Base.Project.Current.TemplateID, attributeGroup.AttributeID);
        if (item != null)
        {
            yield return row;
        }
    }
}

 

Best answer by Dmitrii Naumov

The TemplateAttributeList view redefines the view delegate in the constructor. 

So, you’ll need to override it probably.

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

10 replies

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

You have to redefine the Data View in your graph extension before you can define a Data View Delegate:

[PXViewName(Messages.ProjectAnswers)]
public TemplateAttributeList<PMProject> Answers;

public IEnumerable answers()
{

}

 


Dmitrii Naumov
Acumatica Moderator
Forum|alt.badge.img+7
  • Acumatica Moderator
  • 632 replies
  • Answer
  • November 22, 2024

The TemplateAttributeList view redefines the view delegate in the constructor. 

So, you’ll need to override it probably.


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 23, 2024

@Dmitrii Naumov I thought maybe there was something like this going on.  I didn’t know you could override a delegate. 


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 23, 2024

@Dmitrii Naumov what sorcery do I need to learn to do this…

This 

which is located in this file

calls this:

In my code, I tried this, but it didn’t fire the bold coded below.

[PXViewName(Messages.ProjectAnswers)]
public TemplateAttributeList<PMProject> Answers;

public virtual IEnumerable SelectDelegate()
{

 

I’m going down a rabbit hole here.  The actual code that returns the rows is 

   [PXInternalUseOnly]
   public IEnumerable<CSAnswers> SelectInternal(PXGraph graph, object row)
   {

I think maybe I should be creating my own delegate for this, run the basemethod, then somehow reject the records I don’t want? 


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 24, 2024

I followed this blog:

https://www.acumatica.com/blog/displaying-attributes-of-a-related-object/

I am so close!.

I have a red squiggle saying Method must have a return type

This method is a direct copy from this class.

public class CRAttributeList<TEntity> : PXSelectBase<CSAnswers>
I don’t understand why my code is giving me this error.

The only thing I need to change is the bqlCommand to add a join to my table.

Any idea what I need to do to get rid of the error?

        [PXViewName(PX.Objects.CR.Messages.Answers)]
        [PXCopyPasteHiddenView]
        public MyCustomAttributeList<PMProject> Answers;

        public class MyCustomAttributeList<TEntity> : CRAttributeList<TEntity>
        {
            #region SelectDelegate - Redefined
            // This is the actual customization that is enabling the Attributes tab to populate for InventoryItem Attributes instead of MyDAC
            protected new virtual IEnumerable SelectDelegate()
            {
                object current = _Graph.Caches[typeof(TEntity)].Current;
                return SelectInternal(current);
            }
            #endregion

            #region Copied from CRAttributeList to make this work
            private readonly EntityHelper _helper;
            private const string CbApiValueFieldName = "Value$value";
            private const string CbApiAttributeIDFieldName = "AttributeID$value";

            public CRAttributeList(PXGraph graph) : base(graph)
            {
                _Graph = graph;
                _helper = new EntityHelper(graph);
                View = (graph.IsExport ? GetExportOptimizedView() : new PXView(graph, isReadOnly: false, new Select3<CSAnswers, OrderBy<Asc<CSAnswers.order>>>(), new PXSelectDelegate(SelectDelegate)));
                PXDependToCacheAttribute.AddDependencies(View, new Type[1] { typeof(TEntity) });
                _Graph.EnsureCachePersistence(typeof(CSAnswers));
                PXDBAttributeAttribute.Activate(_Graph.Caches[typeof(TEntity)]);
                _Graph.FieldUpdating.AddHandler<CSAnswers.value>(FieldUpdatingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.value>(FieldSelectingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.isRequired>(IsRequiredSelectingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.attributeCategory>(AttributeCategorySelectingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.attributeID>(AttrFieldSelectingHandler);
                _Graph.RowPersisting.AddHandler<CSAnswers>(RowPersistingHandler);
                _Graph.RowPersisting.AddHandler<TEntity>(ReferenceRowPersistingHandler);
                _Graph.RowUpdating.AddHandler<TEntity>(ReferenceRowUpdatingHandler);
                _Graph.RowDeleted.AddHandler<TEntity>(ReferenceRowDeletedHandler);
                _Graph.RowInserted.AddHandler<TEntity>(RowInsertedHandler);
                _Graph.Caches<CSAnswers>().Fields.Add("Value$value");
                _Graph.Caches<CSAnswers>().Fields.Add("AttributeID$value");
                _Graph.FieldSelecting.AddHandler(typeof(CSAnswers), "Value$value", CbApiValueFieldSelectingHandler);
                _Graph.FieldSelecting.AddHandler(typeof(CSAnswers), "AttributeID$value", CbApiAttributeIdFieldSelectingHandler);
            }

            private PXView GetExportOptimizedView()
            {
                object row = _Graph.Caches[typeof(TEntity)].CreateInstance();
                Type classIdField = GetClassIdField(row);
                Type nestedType = typeof(TEntity).GetNestedType("noteID");

                //original
                //BqlCommand bqlCommand = BqlTemplate.OfCommand<Select2<CSAnswers, InnerJoin<CSAttribute, On<CSAnswers.attributeID, Equal<CSAttribute.attributeID>>, InnerJoin<CSAttributeGroup, On<CSAnswers.attributeID, Equal<CSAttributeGroup.attributeID>>>>, Where<CSAttributeGroup.isActive, Equal<True>, And<CSAttributeGroup.entityType, Equal<TypeNameConst>, And<CSAttributeGroup.entityClassID, Equal<Current<BqlPlaceholder.A>>, And<CSAnswers.refNoteID, Equal<Current<BqlPlaceholder.B>>>>>>>>.Replace<BqlPlaceholder.A>(classIdField).Replace<BqlPlaceholder.B>(nestedType).ToCommand();

                //my version
                BqlCommand bqlCommand = BqlTemplate.OfCommand<Select2<CSAnswers, 
                        InnerJoin<CSAttribute, On<CSAnswers.attributeID, Equal<CSAttribute.attributeID>>, 
                        InnerJoin<CSAttributeGroup, On<CSAnswers.attributeID, Equal<CSAttributeGroup.attributeID>>,
                        InnerJoin<ICSProjTemplateAttrLinks, On<ICSProjTemplateAttrLinks.attributeID, Equal<CSAttributeGroup.attributeID>>>>>,
                        Where<CSAttributeGroup.isActive, Equal<True>, And<CSAttributeGroup.entityType, Equal<TypeNameConst>, 
                            And<CSAttributeGroup.entityClassID, Equal<Current<BqlPlaceholder.A>>, 
                            And<CSAnswers.refNoteID, Equal<Current<BqlPlaceholder.B>>>>>>>>.Replace<BqlPlaceholder.A>(classIdField).Replace<BqlPlaceholder.B>(nestedType).ToCommand();

                return new PXView(_Graph, isReadOnly: true, bqlCommand);
            }


            internal virtual void CbApiValueFieldSelectingHandler(PXCache sender, PXFieldSelectingEventArgs e)
            {
                if (e.Row is CSAnswers answer)
                {
                    // set in FieldUpdating
                    if (sender.GetAttributes<CSAnswers.value>(answer).OfType<PXUIFieldAttribute>().FirstOrDefault() is IPXInterfaceField uiField)
                    {
                        if (uiField.ErrorText != null)
                        {
                            e.ReturnState = PXFieldState.CreateInstance(uiField.ErrorValue, typeof(String),
                                errorLevel: PXErrorLevel.Error,
                                error: uiField.ErrorText);
                        }
                    }
                    e.ReturnValue = answer.Value;

                }
            }

            internal virtual void CbApiAttributeIdFieldSelectingHandler(PXCache sender, PXFieldSelectingEventArgs e)
            {
                if (e.Row is CSAnswers answer)
                    e.ReturnValue = answer.AttributeID;
            }
            #endregion
        }

 


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 24, 2024

My constructor had the original name.  I just needed to change it.

Thanks for all your help friends!


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 25, 2024

I did have to make a change to the code to get it to call “MyGetExportOptimized method.  The TemplateAttributeList code fired and hit a breakpoint, but the MyGetExportOptimized method was NOT firing.  I even changed the name from GetExportOptimized to MyGetExportOptimized to see if it was just calling the base method instead of my override.  

To get it to call MyGetExportOptimizedView, I had to change (graph.IsExport to (!graph.IsExport.

The business logic I am changing is in the MyGetExportOptimizedView as that is where I add a join to the query.

 

I don’t know why I had to change to !graph, but it works.  I hope that isn’t a problem.


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

The base code is only running GetExportOptimizedView if graph.IsExport is true. Otherwise, it’s adding a cache attribute that seems to require saving or something.


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 25, 2024

I’m about to throw in the towel on this project and tell the customer I cannot do it.  Working with Attributes grids might just be outside of my abilities.


Joe Schmucker
Captain II
Forum|alt.badge.img+2
  • Author
  • Captain II
  • 455 replies
  • November 25, 2024

I got it to work.  Unbelievable how many lines of code I had to copy just to add this to the bottom:

                    ICSProjTemplateAttrLinks link = SelectFrom<ICSProjTemplateAttrLinks>.Where<ICSProjTemplateAttrLinks.attributeID.IsEqual<@P.AsString>
                        .And<ICSProjTemplateAttrLinks.contractID.IsEqual<@P.AsInt>>>.View.Select(_Graph, answer.AttributeID, templateID);
                    if (link == null)
                    {
                        continue;
                    }
 

Here is the full extension if anyone ever needs to know…

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

        [PXViewName(Messages.ProjectAnswers)]
        public TemplateAttributeList<PMProject> Answers;

        public class TemplateAttributeList<TEntity> : CRAttributeList<TEntity>
        {
            #region SelectDelegate - Redefined
            // This is the actual customization that is enabling the Attributes tab to populate for InventoryItem Attributes instead of MyDAC
            protected new virtual IEnumerable SelectDelegate()
            {
                object current = _Graph.Caches[typeof(TEntity)].Current;
                return MySelectInternal(_Graph, current);
            }
            #endregion

            #region Copied from CRAttributeList to make this work
            private readonly EntityHelper _helper;
            private const string CbApiValueFieldName = "Value$value";
            private const string CbApiAttributeIDFieldName = "AttributeID$value";

            public TemplateAttributeList(PXGraph graph) : base(graph)
            {
                _Graph = graph;
                _helper = new EntityHelper(graph);
                View = (graph.IsExport ? GetExportOptimizedView() : new PXView(graph, isReadOnly: false, new Select3<CSAnswers, OrderBy<Asc<CSAnswers.order>>>(), new PXSelectDelegate(SelectDelegate)));
                PXDependToCacheAttribute.AddDependencies(View, new Type[1] { typeof(TEntity) });
                _Graph.EnsureCachePersistence(typeof(CSAnswers));
                PXDBAttributeAttribute.Activate(_Graph.Caches[typeof(TEntity)]);
                _Graph.FieldUpdating.AddHandler<CSAnswers.value>(FieldUpdatingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.value>(FieldSelectingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.isRequired>(IsRequiredSelectingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.attributeCategory>(AttributeCategorySelectingHandler);
                _Graph.FieldSelecting.AddHandler<CSAnswers.attributeID>(AttrFieldSelectingHandler);
                _Graph.RowPersisting.AddHandler<CSAnswers>(RowPersistingHandler);
                _Graph.RowPersisting.AddHandler<TEntity>(ReferenceRowPersistingHandler);
                _Graph.RowUpdating.AddHandler<TEntity>(ReferenceRowUpdatingHandler);
                _Graph.RowDeleted.AddHandler<TEntity>(ReferenceRowDeletedHandler);
                _Graph.RowInserted.AddHandler<TEntity>(RowInsertedHandler);
                _Graph.Caches<CSAnswers>().Fields.Add("Value$value");
                _Graph.Caches<CSAnswers>().Fields.Add("AttributeID$value");
                _Graph.FieldSelecting.AddHandler(typeof(CSAnswers), "Value$value", CbApiValueFieldSelectingHandler);
                _Graph.FieldSelecting.AddHandler(typeof(CSAnswers), "AttributeID$value", CbApiAttributeIdFieldSelectingHandler);
            }

            private PXView GetExportOptimizedView()
            {
                object row = _Graph.Caches[typeof(TEntity)].CreateInstance();
                Type classIdField = GetClassIdField(row);
                Type nestedType = typeof(TEntity).GetNestedType("noteID");

                BqlCommand bqlCommand = BqlTemplate.OfCommand<Select2<CSAnswers, InnerJoin<CSAttribute, On<CSAnswers.attributeID, Equal<CSAttribute.attributeID>>, InnerJoin<CSAttributeGroup, On<CSAnswers.attributeID, Equal<CSAttributeGroup.attributeID>>>>, Where<CSAttributeGroup.isActive, Equal<True>, And<CSAttributeGroup.entityType, Equal<TypeNameConst>, And<CSAttributeGroup.entityClassID, Equal<Current<BqlPlaceholder.A>>, And<CSAnswers.refNoteID, Equal<Current<BqlPlaceholder.B>>>>>>>>.Replace<BqlPlaceholder.A>(classIdField).Replace<BqlPlaceholder.B>(nestedType).ToCommand();

                return new PXView(_Graph, isReadOnly: true, bqlCommand);
            }

            public IEnumerable<CSAnswers> MySelectInternal(PXGraph graph, object row)
            {
                if (row == null)
                {
                    yield break;
                }

                Guid? noteId = GetNoteId(row);
                if (!noteId.HasValue)
                {
                    yield break;
                }

                PXCache projectCache = graph.Caches[typeof(PMProject)];
                int? templateID = 0;
                foreach (PMProject proj in projectCache.Cached) 
                {
                    templateID = proj.TemplateID;
                }

                PXCache answerCache = graph.Caches[typeof(CSAnswers)];
                PXCache entityCache = graph.Caches[row.GetType()];
                PXEntryStatus status = entityCache.GetStatus(row);
                List<CSAnswers> answerList = ((status != PXEntryStatus.Inserted && status != PXEntryStatus.InsertedDeleted) ? PXSelectBase<CSAnswers, PXSelect<CSAnswers, Where<CSAnswers.refNoteID, Equal<Required<CSAnswers.refNoteID>>, And<CSAnswers.attributeID, NotEqual<PX.Objects.IN.Matrix.Attributes.MatrixAttributeSelectorAttribute.dummyAttributeName>>>>.Config>.Select(graph, noteId).FirstTableItems.ToList() : (from CSAnswers x in answerCache.Inserted
                                                                                                                                                                                                                                                                                                                                                                                                                         where x.RefNoteID == noteId && !"~MX~DUMMY~".Equals(x.AttributeID, StringComparison.OrdinalIgnoreCase)
                                                                                                                                                                                                                                                                                                                                                                                                                                 select x).ToList());

                string classId = GetClassId(row);
                CRAttribute.ClassAttributeList classAttributeList = new CRAttribute.ClassAttributeList();
                if (classId != null)
                {
                    classAttributeList = CRAttribute.EntityAttributes(GetEntityTypeFromAttribute(row), classId);
                    (from _ in classAttributeList
                     where _.NotInClass
                     select _.ID).ToList().ForEach(delegate (string _)
                     {
                         classAttributeList.Remove(_);
                     });
                }

                if (graph.IsImport && PXView.SortColumns.Any() && PXView.Searches.Any())
                {
                    int columnIndex = Array.FindIndex(PXView.SortColumns, (string x) => x.Equals(typeof(CSAnswers.attributeID).Name, StringComparison.OrdinalIgnoreCase));
                    if (columnIndex >= 0 && columnIndex < PXView.Searches.Length)
                    {
                        object searchValue = PXView.Searches[columnIndex];
                        if (searchValue != null)
                        {
                            CRAttribute.Attribute attributeDefinition = CRAttribute.Attributes[searchValue.ToString()] ?? CRAttribute.AttributesByDescr[searchValue.ToString()];
                            if (attributeDefinition == null)
                            {
                                string message = GetSelectInternalExceptionMessage();
                                throw new PXSetPropertyException(message, searchValue.ToString());
                            }

                            if (classAttributeList[attributeDefinition.ID] == null)
                            {
                                classAttributeList.Add(new CRAttribute.AttributeExt(attributeDefinition, null, required: false, isActive: true)
                                {
                                    NotInClass = true
                                });
                            }
                        }
                    }
                }

                if (answerList.Count == 0 && classAttributeList.Count == 0)
                {
                    yield break;
                }

                List<string> attributeIdListClass = classAttributeList.Select((CRAttribute.AttributeExt x) => x.ID).Except(answerList.Select((CSAnswers x) => x.AttributeID)).ToList();
                List<string> attributeIdListIntersection = classAttributeList.Select((CRAttribute.AttributeExt x) => x.ID).Intersect(answerList.Select((CSAnswers x) => x.AttributeID)).Distinct()
                    .ToList();
                bool cacheIsDirty = answerCache.IsDirty;
                List<CSAnswers> output = new List<CSAnswers>();
                foreach (string attributeId in attributeIdListClass)
                {
                    CRAttribute.AttributeExt classAttributeDefinition = classAttributeList[attributeId];
                    if ((PXSiteMap.IsPortal && classAttributeDefinition.IsInternal && string.IsNullOrEmpty(classAttributeDefinition.DefaultValue)) || !classAttributeDefinition.IsActive)
                    {
                        continue;
                    }

                    CSAnswers answer2 = (CSAnswers)answerCache.CreateInstance();
                    answer2.AttributeID = classAttributeDefinition.ID;
                    answer2.RefNoteID = noteId;
                    answer2.Value = GetDefaultAnswerValue(classAttributeDefinition);
                    if (classAttributeDefinition.ControlType == 4)
                    {
                        if (bool.TryParse(answer2.Value, out var value))
                        {
                            answer2.Value = Convert.ToInt32(value).ToString(CultureInfo.InvariantCulture);
                        }
                        else if (answer2.Value == null)
                        {
                            answer2.Value = 0.ToString();
                        }
                    }

                    answer2.IsRequired = classAttributeDefinition.Required;
                    answer2.NotInClass = classAttributeDefinition.NotInClass;
                    Dictionary<string, object> keys = new Dictionary<string, object>();
                    string[] array = answerCache.Keys.ToArray();
                    foreach (string key in array)
                    {
                        keys[key] = answerCache.GetValue(answer2, key);
                    }

                    answerCache.Locate(keys);
                    answer2 = (CSAnswers)(answerCache.Locate(answer2) ?? answerCache.Insert(answer2));
                    if (!PXSiteMap.IsPortal || !classAttributeDefinition.IsInternal)
                    {
                        output.Add(answer2);
                    }
                }

                foreach (CSAnswers answer4 in answerList.Where((CSAnswers x) => attributeIdListIntersection.Contains(x.AttributeID)).ToList())
                {
                    CRAttribute.AttributeExt classAttributeDefinition2 = classAttributeList[answer4.AttributeID];
                    if ((!PXSiteMap.IsPortal || !classAttributeDefinition2.IsInternal) && classAttributeDefinition2.IsActive)
                    {
                        if (answer4.Value == null && classAttributeDefinition2.ControlType == 4)
                        {
                            answer4.Value = bool.FalseString;
                        }

                        if (!answer4.IsRequired.HasValue || classAttributeDefinition2.Required != answer4.IsRequired)
                        {
                            answer4.IsRequired = classAttributeDefinition2.Required;
                            bool fieldValue = View.Cache.GetValueExt<CSAnswers.isRequired>(answer4) is PXFieldState fieldState && ((bool?)fieldState.Value).GetValueOrDefault();
                            answer4.IsRequired = classAttributeDefinition2.Required || fieldValue;
                        }

                        output.Add(answer4);
                    }
                }

                answerCache.IsDirty = cacheIsDirty;
                output = (from x in output
                          orderby classAttributeList.Contains(x.AttributeID) ? classAttributeList.IndexOf(x.AttributeID) : x.Order.GetValueOrDefault(), x.AttributeID
                          select x).ToList();
                short attributeOrder = 0;
                foreach (CSAnswers answer in output)
                {
                    ICSProjTemplateAttrLinks link = SelectFrom<ICSProjTemplateAttrLinks>.Where<ICSProjTemplateAttrLinks.attributeID.IsEqual<@P.AsString>
                        .And<ICSProjTemplateAttrLinks.contractID.IsEqual<@P.AsInt>>>.View.Select(_Graph, answer.AttributeID, templateID);
                    if (link == null)
                    {
                        continue;
                    }

                    answer.Order = attributeOrder++;
                    yield return answer;
                }
            }



            internal virtual void CbApiValueFieldSelectingHandler(PXCache sender, PXFieldSelectingEventArgs e)
            {
                if (e.Row is CSAnswers answer)
                {
                    // set in FieldUpdating
                    if (sender.GetAttributes<CSAnswers.value>(answer).OfType<PXUIFieldAttribute>().FirstOrDefault() is IPXInterfaceField uiField)
                    {
                        if (uiField.ErrorText != null)
                        {
                            e.ReturnState = PXFieldState.CreateInstance(uiField.ErrorValue, typeof(String),
                                errorLevel: PXErrorLevel.Error,
                                error: uiField.ErrorText);
                        }
                    }
                    e.ReturnValue = answer.Value;

                }
            }

            internal virtual void CbApiAttributeIdFieldSelectingHandler(PXCache sender, PXFieldSelectingEventArgs e)
            {
                if (e.Row is CSAnswers answer)
                    e.ReturnValue = answer.AttributeID;
            }
            #endregion
        }

 


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