1
votes

I am creating a graph using Acumatica Framework and I am trying to save changes to the database but I am getting an error on Save.

I have a typical parent/child (i.e. master/detail) relationships and everything should be set correctly.

My child DAC has the PXDBDefault and the PXParent entries:

        [PXDBInt()]
        [PXDBDefault(typeof(DCRuleHeader.ruleHeaderID))]
        [PXParent(typeof(Select<DCRuleHeader, Where<DCRuleHeader.ruleHeaderID, Equal<Current<DCRule.ruleHeaderID>>>>))]
        public virtual int? RuleHeaderID
        {
            get
            {
                return this._RuleHeaderID;
            }
            set
            {
                this._RuleHeaderID = value;
            }
        }

In the header, the ID is an identity in the database and defined as a key in the DAC as follows:

[PXDBIdentity(IsKey = true)]
    [PXUIField(Enabled = false)]
    public virtual int? RuleHeaderID
    {
        get
        {
            return this._RuleHeaderID;
        }
        set
        {
            this._RuleHeaderID = value;
        }
    }

The View is also configured accordingly:

 public class RulesMaint : PXGraph<RulesMaint, DCRuleHeader>
    {

        public PXSelect<DCRuleHeader> RuleHeader;
        public PXSelect<DCRule, Where<DCRule.ruleHeaderID, Equal<Current<DCRuleHeader.ruleHeaderID>>>> Rules;
        public PXAction<DCRuleHeader> ViewRule;

However, the below code is not working

RulesMaint rulesGraph = PXGraph.CreateInstance<RulesMaint>(); 
DCRuleHeader newHeader = rulesGraph.RuleHeader.Insert();
...
DCRule rule = rulesGraph.Rules.Insert();
...
rulesGraph.Actions.PressSave();

When I try the above, I get the error 'Error "RuleHeaderID" cannot be empty'

Shouldn't the framework handle everything itself and set the Parent ID of the child object automatically? Isn't that what PXDBDefault is for ?

2
In you insertion code you have not provided the instance to insert, have you forgotten to add it in this question? - Samvel Petrosov
Also try to replace PXDBIdentity by PXDBInt(IsKey = true) in the header and add IsKey = true in the DCRule PXDBInt - Samvel Petrosov
Thanks for your comments. I am not passing the instance, because I just want to insert with the default values. I tried also passing the instance, but same error. Going to try to carry out the Identity replacement and changes as you are suggesting - Joseph Caruana
DBIdentity is required because the ID is assigned by the database. If I don't do it, I get a duplicate key error. Still checking on the your other suggestion - Joseph Caruana
I have tried adding IsKey to the DCRule ... but still no luck, exact same error - Joseph Caruana

2 Answers

4
votes

Here is example how you can get Parent/Child DACs working with Form Detail view in Acumatica ERP. For the begging let's create SQL Tables for Parent and Child in the following way: Parent:

/****** Object:  Table [dbo].[SOCustomParentTable]    Script Date: 07/03/2017 12:55:17 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[SOCustomParentTable](
    [CompanyID] [int] NOT NULL,
    [ParentID] [int] IDENTITY(1,1) NOT NULL,
    [Description] [nvarchar](255) NULL,
    [SomeOtherField] [nvarchar](50) NULL,
    [ParentCD] [nvarchar](15) NOT NULL
) ON [PRIMARY]

GO

And the child

/****** Object:  Table [dbo].[SOCustomChildTable]    Script Date: 07/03/2017 12:54:39 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[SOCustomChildTable](
    [CompanyID] [int] NOT NULL,
    [ChildID] [int] IDENTITY(1,1) NOT NULL,
    [ParentID] [int] NOT NULL,
    [Description] [nvarchar](255) NULL,
    [SomeOtherField] [nvarchar](50) NULL
) ON [PRIMARY]

GO

Now as we have SQL Tables ready let's create DAC's as the following classes:

Parent :

using System;
using PX.Data;

namespace DemoParentChild
{
  [Serializable]
  public class SOCustomParentTable: IBqlTable
  {


    #region ParentID

    [PXDBIdentity()]
    public int? ParentID { get; set; }

    public class parentID : IBqlField{}

    #endregion


    #region Description

    [PXDBString(255, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Description")]
    public string Description { get; set; }

    public class description : IBqlField{}

    #endregion


    #region SomeOtherField

    [PXDBString(50, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Some Other Field")]
    public string SomeOtherField { get; set; }

    public class someOtherField : IBqlField{}

    #endregion


    #region ParentCD

    [PXDBString(15,IsKey = true, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Parent ID")]
    public string ParentCD { get; set; }

    public class parentCD : IBqlField{}

    #endregion

  }
}

And the child:

using System;
using PX.Data;

namespace DemoParentChild
{
  [Serializable]
  public class SOCustomChildTable: IBqlTable
  {


    #region ChildID

    [PXDBIdentity(IsKey=true)]
    public int? ChildID { get; set; }

    public class childID : IBqlField{}

    #endregion


    #region ParentID

    [PXDBInt()]
    [PXDBDefault(typeof(SOCustomParentTable.parentID))]
    [PXParent(typeof(Select<SOCustomParentTable, Where<SOCustomParentTable.parentID, Equal<Current<SOCustomChildTable.parentID>>>>))]
    public int? ParentID { get; set; }

    public class parentID : IBqlField{}

    #endregion


    #region Description

    [PXDBString(255, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Description")]
    public string Description { get; set; }

    public class description : IBqlField{}

    #endregion


    #region SomeOtherField

    [PXDBString(50, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Some Other Field")]
    public string SomeOtherField { get; set; }

    public class someOtherField : IBqlField{}

    #endregion

  }
}

And to finish our work let's create a page of FormDetail type with the following Graph:

using System;
using PX.Data;

namespace DemoParentChild
{
  public class tmp : PXGraph<tmp>
  {

    public PXSave<SOCustomParentTable> Save;
    public PXCancel<SOCustomParentTable> Cancel;
    public PXPrevious<SOCustomParentTable> Prev;
    public PXNext<SOCustomParentTable> Next;


    public PXSelect<SOCustomParentTable> MasterView;
    public PXSelect<SOCustomChildTable,Where<SOCustomChildTable.parentID,Equal<Current<SOCustomParentTable.parentID>>>> DetailsView;

  }
}

Now let's understand how this all is working. As you can see the ParentID is Identity in SQL and is set to PXDBIdentity in DAC, but it's not set to be a Key for our DAC as we will use ParentCD as visible Key. Also in the Child class the ChildID is set to PXDBIdentity, but it is set to be Key also as we don't need the line to have visible key for user. Also in the Child class we have the following for creation of the Parent/Child relation:

[PXDBInt()]
[PXDBDefault(typeof(SOCustomParentTable.parentID))]
[PXParent(typeof(Select<SOCustomParentTable, Where<SOCustomParentTable.parentID, Equal<Current<SOCustomChildTable.parentID>>>>))]
public int? ParentID { get; set; }

Where PXDefault is setting ParendID of the Child to current Parent's ID and PXParent is creating the Relation between current Child and the Parent.

Commonly there are being created Line Number for child and Line Counter on the Parent to know the count of the Childs.

The full customization you can download here

0
votes

I was having trouble with this before, because i have a habit of putting new code at the top of the editor, turns out that the data view selection (child) should always be at the bottom (after pxselect)