Skip to main content

I have a custom maintenance screen that has a form and a grid.  The form is attached to one table (ICSPricingHeader table).  When I insert a record in the grid, I want to default the PriceClassID field in the grid (ICSPricingINItemClass) to default to the value in the header table.  In the screen below, I am showng the PriceClassID on the grid just so I can verify the value in the field is populated.

When I click the plus sign, the PriceClassID is not being filled in.  If I try to save the record, I get a “you didn’t provide a priceclassid” error.

In my child table DAC, I have the following code that I think should be putting the Price Class ID from the top of the form into the table (cache).

		#region PriceClassID
IPXDBString(10, IsUnicode = true, IsKey = true)]
)PXDefault(typeof(ICSPricingHeader.priceClassID.FromCurrent))]
public virtual string PriceClassID { get; set; }
public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
#endregion

The PXDefault should be pulling the current value from the ICSPricingHeader current value, but it is not.

To make sure that there is a current value in the Header table, I put code in the RowInserting handler of the child table.  This code does populate the child table and I can save the record.

		protected void ICSPricingINItemClass_RowInserting(PXCache cache, PXRowInsertingEventArgs e)
{
var row = (ICSPricingINItemClass)e.Row;

if(MasterView.Current != null)
{
row.PriceClassID = MasterView.Current.PriceClassID;
}
}

It seems like I should be able to default the field value of a new record in the child table from the PXDefault and NOT have to do it in an event handler.

I know that the Current of the Header table has a value as the RowInserting hander is able to set the value.

What am I doing wrong?

Hi @Joe Schmucker   I suggest you to using the PXDBDefault attribute instead of PXDefault or RowInserting/RowInserted in your scenario.

By applying the PXDBDefault attribute, you ensure that the Parent value is automatically inserted into the Child table. Additionally, if you ever delete the Parent record, the system will automatically handle the deletion of the corresponding child records. This attribute functions similarly to a foreign key relationship between the parent and child tables, simplifying the management of related data.


Below is the article about PXDBDefault and hope this helps.

https://www.acumatica.dev/admin/pxdefault-vs-pxdbdefault/#:~:text=The%20help%20on%20PXDBDefault%20when,field%20from%20a%20database%20record

 

https://stackoverflow.com/questions/44880330/save-parent-child

 

 

 

hope this helps!


Thanks @Naveen Boga.  In this case, the header record is not an auto-generated value for the key field.  I think that the field I am trying to default in the child table would need to pull an autogenerated field from the header table.  

“Sets the default value for a DAC field. Use to assign a value from the auto-generated key field.

On my screen, the primary key field in the header table is a selector based on the Customer Price Class.  After that field has a value, and you add a new record into the child table (grid), it should pull the value that is selected in the Price Class at top of the form.  

When I used PXDBDefault, I get this error:

 

Here is the DAC for the parent and child tables, in case you see something obviously wrong:

Parent:

    tSerializable]
    zPXCacheName("ICS Pricing Header")]
    public class ICSPricingHeader : IBqlTable
    {
        #region PriceClassID
        gPXDBString(10, IsUnicode = true, IsKey = true)]    
        rPXDefault(typeof(ARPriceClass.priceClassID))]
        ePXUIField(DisplayName = "Price Class ID", Visibility = PXUIVisibility.SelectorVisible)]
        iPXSelector(typeof(Search<ARPriceClass.priceClassID>))]
        public virtual string PriceClassID { get; set; }
        public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
        #endregion

 

Child:

    lSerializable]
     PXCacheName("ICS IN Item Class")]
    public class ICSPricingINItemClass : IBqlTable
    {
        #region PriceClassID
        cPXDBString(10, IsUnicode = true, IsKey = true)]
        rPXDBDefault(typeof(ICSPricingHeader.priceClassID.FromCurrent))]
         PXParent(typeof(SelectFrom<ICSPricingHeader>.Where<ICSPricingHeader.priceClassID.IsEqual<ICSPricingINItemClass.priceClassID.FromCurrent>>))]
        public virtual string PriceClassID { get; set; }
        public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
        #endregion

I looked at the training class code and I am basically mirroring the DACs for the RSSVWorkOrder and RSSVWorkOrderLabor tables, but I see that the primary key in the RSSVWorkOrder table is an AutoNumber field.

So, in my case, I think I might have to use the event handler to assign the value to the child table.


Hi, @Joe Schmucker  Foreign key reference field cannot be a KEY field in the child DAC.

Please remove and check once.

Not required any event handler to assign the value, on SAVE action, parent DAC field value will be stored in the child DAC field.


@Naveen Boga I see what you are saying.  I did not know that.  I can create an IDENTITY field as the PK for the child table, but I want to ensure the uniqueness of the PriceClassID-ItemClassID fields.

What would you suggest I use for the PK on the ICSPricingINItemClass table?  

Parent: (standard fields removed for brevity)

CREATE TABLE Tdbo].[ICSPricingHeader](

    pCompanyID] mint] NOT NULL,
    bPriceClassID] invarchar](10) NOT NULL,
    LMarkupRate] decimal](19,6) NULL,
 CONSTRAINT ICSPricingHeader_PK] PRIMARY KEY CLUSTERED 
(
        nPriceClassID] ASC
 

Child:

CREATE TABLE idbo].pICSPricingINItemClass](
    gCompanyID] ]int] NOT NULL,
    ]PriceClassID] Lnvarchar](10) NOT NULL,
    rItemClassID] int] NOT NULL,
    lMarkupRate] decimal](19,6) NULL,
 CONSTRAINT cICSPricingINItemClass_PK] PRIMARY KEY CLUSTERED 
(
    ICompanyID] ASC,
    >PriceClassID] ASC,
    CItemClassID] ASC
 

When I look at the training materials, it looks like they did the same thing I am doing?

Parent:

Child:

 


@Naveen Boga 

After working on the changes you suggested above, I received a different error.  I searched on that error and found you resolved that for someone else. 

 

Your code in that other solution did not have .FromCurrent in the PXDBDefault.  After removing that code, it works now.

REMOVED CODE IN RED

    public class ICSPricingINItemClass : IBqlTable
    {
        #region PriceClassID
        IPXDBString(10, IsUnicode = true, IsKey = true)]
        =PXDBDefault(typeof(ICSPricingHeader.priceClassID.FromCurrent))]
        nPXParent(typeof(SelectFrom<ICSPricingHeader>.Where<ICSPricingHeader.priceClassID.IsEqual<ICSPricingINItemClass.priceClassID.FromCurrent>>))]
        public virtual string PriceClassID { get; set; }
        public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
        #endregion

Thanks for all your help!


@Joe Schmucker  Can you please check with below code.

 

 >Serializable]
PXCacheName("ICS Pricing Header")]
public class ICSPricingHeader : IBqlTable
{
#region PriceClassID
PXDBString(10, IsUnicode = true, IsKey = true)]
PXDefault(typeof(ARPriceClass.priceClassID))]
PXUIField(DisplayName = "Price Class ID", Visibility = PXUIVisibility.SelectorVisible)]
PXSelector(typeof(Search<ARPriceClass.priceClassID>))]
public virtual string PriceClassID { get; set; }
public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
#endregion

}


Serializable]
PXCacheName("ICS IN Item Class")]
public class ICSPricingINItemClass : IBqlTable
{
#region PriceClassID
PXDBString(10, IsUnicode = true)]
PXDBDefault(typeof(ICSPricingHeader.priceClassID))]
PXParent(typeof(PXSelect<ICSPricingHeader,Where<ICSPricingHeader.priceClassID,Equal<Current<ICSPricingINItemClass.priceClassID>>>>))]
public virtual string PriceClassID { get; set; }
public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
#endregion
}

 


@Naveen Boga   Works great.  Thank you!


@Joe Schmucker  Great!! Thanks for sharing the update.


@Joe Schmucker you can try writting a custom attribute instead of PXDefault that will implement IPXFieldDefaultingSubscriber interface

 


 


Serializable]
PXCacheName("ICS IN Item Class")]
public class ICSPricingINItemClass : IBqlTable
{
#region PriceClassID
PXDBString(10, IsUnicode = true)]
MyPriceClassIDDefault]
public virtual string PriceClassID { get; set; }
public abstract class priceClassID : PX.Data.BQL.BqlString.Field<priceClassID> { }
#endregion
}
public class MyPriceClassIDDefaultAttribute : PXEventSubscriberAttribute, IPXFieldDefaultingSubscriber
{
public void FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
if(e.Row is ICSPricingINItemClass row)
{
ICSPricingHeader currentHeader = (ICSPricingHeader)sender.Graph.Cacheshtypeof(ICSPricingHeader)].Current;
e.NewValue = currentHeader?.PriceClassID;
}
}
}

 


@Samvel Petrosov Nice code!  I’ve got the program working as it is with the Parent child thing.  However, I am going to copy your code into my Keepers file for future reference!  I’ve never done that in my projects before, but this looks like it would come in very handy!  Thanks for taking the time to write that up for me!!!


Reply