How do I enable custom attributes? (can assign on class, but not displaying for transaction)

故事扮演 提交于 2021-01-29 06:00:22

问题


I defined several new screens in Acumatica 2018R2 and need to enable attribute support according the class assigned to the transaction. The class screen appears to allow attaching attributes, but the transactional screen does not display any attribute. I found several examples on Stack Overflow, but the only clue I see seems to be that if the class id cannot be determined then no attributes will show. However, upon debug, I see that the class id is found.

This is my test code where I have tried stripping down to a very basic test.

AAClass

using PX.Data;
using System;

namespace Attributes
{
    [Serializable]
    public class AAClass : IBqlTable
    {
        #region ClassID
        [PXDBIdentity]
        [PXUIField(DisplayName = "Class ID")]
        public virtual int? ClassID { get; set; }
        public abstract class classID : IBqlField { }
        #endregion

        #region ClassCD
        [PXDBString(15, IsKey = true, IsUnicode = true, InputMask = "")]
        [PXUIField(DisplayName = "Class CD")]
        public virtual string ClassCD { get; set; }
        public abstract class classCD : IBqlField { }
        #endregion

        #region Descr
        [PXDBString(256, IsUnicode = true, InputMask = "")]
        [PXUIField(DisplayName = "Descr")]
        public virtual string Descr { get; set; }
        public abstract class descr : IBqlField { }
        #endregion

        #region CreatedByID
        [PXDBCreatedByID()]
        public virtual Guid? CreatedByID { get; set; }
        public abstract class createdByID : IBqlField { }
        #endregion

        #region CreatedByScreenID
        [PXDBCreatedByScreenID()]
        public virtual string CreatedByScreenID { get; set; }
        public abstract class createdByScreenID : IBqlField { }
        #endregion

        #region CreatedDateTime
        [PXDBCreatedDateTime()]
        [PXUIField(DisplayName = SSCS.IN.Messages.FldCreatedDateTime)]
        public virtual DateTime? CreatedDateTime { get; set; }
        public abstract class createdDateTime : IBqlField { }
        #endregion

        #region LastModifiedByID
        [PXDBLastModifiedByID()]
        public virtual Guid? LastModifiedByID { get; set; }
        public abstract class lastModifiedByID : IBqlField { }
        #endregion

        #region LastModifiedByScreenID
        [PXDBLastModifiedByScreenID()]
        public virtual string LastModifiedByScreenID { get; set; }
        public abstract class lastModifiedByScreenID : IBqlField { }
        #endregion

        #region LastModifiedDateTime
        [PXDBLastModifiedDateTime()]
        [PXUIField(DisplayName = SSCS.IN.Messages.FldLastModifiedDateTime)]
        public virtual DateTime? LastModifiedDateTime { get; set; }
        public abstract class lastModifiedDateTime : IBqlField { }
        #endregion

        #region Tstamp
        [PXDBTimestamp()]
        public virtual byte[] Tstamp { get; set; }
        public abstract class tstamp : IBqlField { }
        #endregion

        #region NoteID
        [PXNote]
        public virtual Guid? NoteID { get; set; }
        public abstract class noteID : IBqlField { }
        #endregion
    }
}

AAClassMaint

using PX.Data;
using PX.Objects.CR;

namespace Attributes
{
    public class AAClassMaint : PXGraph<AAClassMaint, AAClass>
    {

        #region Data Views
        [PXViewName("Classes")]
        public PXSelect<AAClass> Classes;

        [PXViewName("Attributes")]
        public CSAttributeGroupList<AAClass, AATag> Mapping;
        #endregion

    }
}

AATag

using PX.Data;
using PX.Objects.CR;
using PX.Objects.CS;
using System;

namespace Attributes
{
    [Serializable]
    public class AATag : IBqlTable
    {
        #region TagID
        [PXDBIdentity]
        public virtual int? TagID { get; set; }
        public abstract class tagID : IBqlField { }
        #endregion

        #region TagCD
        [PXDBString(15, IsKey = true, IsUnicode = true, InputMask = "")]
        [PXUIField(DisplayName = "Tag ID")]
        public virtual string TagCD { get; set; }
        public abstract class tagCD : IBqlField { }
        #endregion

        #region MyClassID
        [PXDBInt()]
        [PXSelector(
            typeof(AAClass.classID),
            typeof(AAClass.classCD),
            typeof(AAClass.descr),
            SubstituteKey = typeof(AAClass.classCD)
        )]
        [PXUIField(DisplayName = "My Class ID")]
        public virtual int? MyClassID { get; set; }
        public abstract class myClassID : IBqlField { }
        #endregion

        #region CreatedByID
        [PXDBCreatedByID()]
        public virtual Guid? CreatedByID { get; set; }
        public abstract class createdByID : IBqlField { }
        #endregion

        #region CreatedByScreenID
        [PXDBCreatedByScreenID()]
        public virtual string CreatedByScreenID { get; set; }
        public abstract class createdByScreenID : IBqlField { }
        #endregion

        #region CreatedDateTime
        [PXDBCreatedDateTime()]
        [PXUIField(DisplayName = SSCS.IN.Messages.FldCreatedDateTime)]
        public virtual DateTime? CreatedDateTime { get; set; }
        public abstract class createdDateTime : IBqlField { }
        #endregion

        #region LastModifiedByID
        [PXDBLastModifiedByID()]
        public virtual Guid? LastModifiedByID { get; set; }
        public abstract class lastModifiedByID : IBqlField { }
        #endregion

        #region LastModifiedByScreenID
        [PXDBLastModifiedByScreenID()]
        public virtual string LastModifiedByScreenID { get; set; }
        public abstract class lastModifiedByScreenID : IBqlField { }
        #endregion

        #region LastModifiedDateTime
        [PXDBLastModifiedDateTime()]
        [PXUIField(DisplayName = SSCS.IN.Messages.FldLastModifiedDateTime)]
        public virtual DateTime? LastModifiedDateTime { get; set; }
        public abstract class lastModifiedDateTime : IBqlField { }
        #endregion

        #region Tstamp
        [PXDBTimestamp()]
        public virtual byte[] Tstamp { get; set; }
        public abstract class tstamp : IBqlField { }
        #endregion

        #region NoteID
        [PXNote]
        public virtual Guid? NoteID { get; set; }
        public abstract class noteID : IBqlField { }
        #endregion

        #region Attributes
        public abstract class attributes : IBqlField { }
        [CRAttributesField(typeof(AATag.myClassID))]
        public virtual string[] Attributes { get; set; }
        public virtual int? ClassID
        {
            get { return MyClassID; }
        }
        #endregion

    }
}

AATagEntry

using PX.Data;
using PX.Objects.CR;

namespace Attributes
{
    public class AATagEntry : PXGraph<AATagEntry, AATag>
    {
        #region Data Views
        [PXViewName("Classes")]
        public PXSelect<AATag> Tags;

        [PXViewName("Answers")]
        public CRAttributeList<AATag> Answers;
        #endregion
    }
}

As I understand the various posts for this topic, the key requirements are:

  • Define MAPPING in the class maintenance screen

    [PXViewName("Attributes")]
    public CSAttributeGroupList<AAClass, AATag> Mapping;
    
  • Define ANSWERS in the transactional entry screen

    [PXViewName("Answers")]
    public CRAttributeList<AATag> Answers;
    
  • Include NoteID in the transactional table

  • Add ATTRIBUTES to the transactional table, including a field for ClassID

    public abstract class attributes : IBqlField { }
    [CRAttributesField(typeof(AATag.myClassID))]
    public virtual string[] Attributes { get; set; }
    public virtual int? ClassID
    {
        get { return MyClassID; }
    }
    

回答1:


CRAttributesField attribute will build a request with the generic type (classIdField) provided in it's parameter. In your case that would be AATag.myClassID :

protected static Type GetAttributesSearchCommand(Type classIdField)
{
    var cmd = BqlCommand.Compose(typeof (Search2<,,>), typeof (CSAttribute.attributeID),
        typeof (InnerJoin<,>), typeof (CSAttributeGroup),
        typeof (On<,>), typeof (CSAttributeGroup.attributeID), typeof (Equal<>),
        typeof (CSAttribute.attributeID),
        typeof(Where<,,>), typeof(CSAttributeGroup.entityType), typeof(Equal<>), typeof(Required<>), typeof(CSAttributeGroup.entityType),
        typeof (And<,>), typeof (CSAttributeGroup.entityClassID), typeof (Equal<>), typeof (Current<>),
        classIdField);
    return cmd;
}

So the key part of the request where clause will be:

And<CSAttributeGroup.entityClassID, Equal<Current<AATag.myClassID>>>

This means you must have a current AATag record in memory:

Caches[typeof(AATag)].Current

I suggest you first trace the value (Help->Trace window) to validate it's non null:

public void AATag_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
   if (e.Row is AATag)
   {
      PXTrace.WriteInformation(((AATag)e.Row).myClassID);
   }
}

If it is null, you will have to join AATag in the relevant DataView (preferred) or set it explicitely in some events. Using Acumatica Request Profiler (SM205070 ) can also help to determine why the request is not returning any record.




回答2:


As HB_Acumatica suggested in the comments, the issue appeared to be with the ClassID fields being defined as integer.

I removed the original ClassID (int) fields and renamed the ClassCD (string) fields in the Class DAC's. Next, I changed the ClassID fields in the transactional DAC's to strings. Problem solved. The attribute tab on the transactional entry screens now display the attributes assigned to the class selected.

Lesson learned: A "Class" DAC should not have a ClassID/ClassCD pair, but rather a simple string ClassID field. (Typically named specific to the type of class, i.e. TagClassID) The Class ID used in the transactional tables will then use the string Class ID.



来源:https://stackoverflow.com/questions/55599118/how-do-i-enable-custom-attributes-can-assign-on-class-but-not-displaying-for

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!