Search code examples
acumaticaacumatica-kb

Acumatica - An item with the same key has already been added


I have customization project in Acumatica 2021R1 (v21.104.0018). I have a parent DAC that has an ID and multiple child DACs that use this ID with a PXParent attribute. Whenever I select the screen in the customization project editor to design it, I get an error message that says, "An item with the same key has already been added." Then, I'm not able to design the screen in the Screen Editor. The weird thing is that I don't get this message when I open the actual screen.

I'm not sure what this is referring to because there are no records in the database in the tables that I'm using for the views of the graph for the screen.

Not sure if anyone else has had this problem and knows what causes it, but Acumatica could really use some more specific error messages.

Here is my parent DAC (note: I left out the system fields here for purposes of space):

    public class PSImport : IBqlTable
    {
        #region Import ID
        [PXDBString(15, IsKey = true, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
        [PXDefault(PersistingCheck = PXPersistingCheck.NullOrBlank)]
        [PXUIField(DisplayName = "Import No.", Visibility = PXUIVisibility.SelectorVisible, Enabled = true)]
        [AutoNumber(typeof(PSSetup.numberingID), typeof(AccessInfo.businessDate))]
        [PXSelector(
            typeof(PSImport.importID),
            typeof(PSImport.description),
            typeof(PSImport.fileFormat),
            typeof(PSImport.status),
            typeof(PSImport.description)
        )]
        public virtual string ImportID { get; set; }
        public abstract class importID : PX.Data.BQL.BqlString.Field<importID> { }
        #endregion

        #region Description
        [PXDBString(IsUnicode = true, IsFixed = false)]
        [PXUIField(DisplayName = "Description")]
        public virtual string Description { get; set; }
        public abstract class description : PX.Data.BQL.BqlString.Field<description> { }
        #endregion

        #region File Format
        [PXDBString(2, IsFixed = true, IsUnicode = true)]
        [PXUIField(DisplayName = "File Format", Enabled = true)]
        [PXDefault]
        [PXStringList(
            new string[]
            {
                FileFormatTypes.Flats,
                FileFormatTypes.IDOA,
                FileFormatTypes.MMS,
                FileFormatTypes.OCR,
                FileFormatTypes.Pressero
            },
            new string[]
            {
                FileFormatTypeDisplayNames.Flats,
                FileFormatTypeDisplayNames.IDOA,
                FileFormatTypeDisplayNames.MMS,
                FileFormatTypeDisplayNames.OCR,
                FileFormatTypeDisplayNames.Pressero
            }
        )]
        public virtual string FileFormat { get; set; }
        public abstract class fileFormat : PX.Data.BQL.BqlString.Field<fileFormat> { }
        #endregion

        #region Status
        [PXDBString(2, IsFixed = true, IsUnicode = true)]
        [PXUIField(DisplayName = "Status", Enabled = false)]
        [PXDefault(ImportStatusTypes.Unvalidated)]
        [PXStringList(
            new string[]
            {
                ImportStatusTypes.Unvalidated,
                ImportStatusTypes.Invalid,
                ImportStatusTypes.Valid,
                ImportStatusTypes.Released
            },
            new string[]
            {
                ImportStatusTypeDisplayNames.Unvalidated,
                ImportStatusTypeDisplayNames.Invalid,
                ImportStatusTypeDisplayNames.Valid,
                ImportStatusTypeDisplayNames.Released
            }
        )]
        public virtual string Status { get; set; }
        public abstract class status : PX.Data.BQL.BqlString.Field<status> { }
        #endregion

        #region Date Imported
        [PXDBDate]
        [PXUIField(DisplayName = "Date Imported", Enabled = false)]
        public virtual DateTime? DateImported { get; set; }
        public abstract class dateImported : PX.Data.BQL.BqlDateTime.Field<dateImported> { }
        #endregion

        #region Date of Last Validation
        [PXDBDate]
        [PXUIField(DisplayName = "Date of Last Validation", Enabled = false)]
        public virtual DateTime? DateOfLastValidation { get; set; }
        public abstract class dateOfLastValidation : PX.Data.BQL.BqlDateTime.Field<dateOfLastValidation> { }
        #endregion

        #region Date Released
        [PXDBDate]
        [PXUIField(DisplayName = "Date Released", Enabled = false)]
        public virtual DateTime? DateReleased { get; set; }
        public abstract class dateReleased : PX.Data.BQL.BqlDateTime.Field<dateReleased> { }
        #endregion
    }

Here is one of the child DACs (right now I only have the key because I don't know all of the fields that I need yet, and I'm just trying to start designing the screens):

public class PSTranFlats : IBqlTable
{
    #region Import ID
    [PXDBString(15, IsKey = true, IsUnicode = true, InputMask = "")]
    [PXDBDefault(typeof(PSImport.importID))]
    [PXParent(typeof(SelectFrom<PSImport>.
        Where<PSImport.importID.IsEqual<PSTranFlats.importID.FromCurrent>>)
    )]
    public virtual string ImportID { get; set; }
    public abstract class importID : PX.Data.BQL.BqlString.Field<importID> { }
    #endregion
}

Here is my setup DAC:

    [PXPrimaryGraph(typeof(PSSetupMaint))]
    public class PSSetup : IBqlTable
    {
        #region NumberingID
        [PXDBString(10, IsUnicode = true)]
        [PXDefault("PSIMPORT")]
        [PXUIField(DisplayName = "Import Numbering Sequence")]
        [PXSelector(typeof(Numbering.numberingID), DescriptionField = typeof(Numbering.descr))]
        public virtual string NumberingID { get; set; }
        public abstract class numberingID : PX.Data.BQL.BqlString.Field<numberingID> { }
        #endregion
    }

Here is my setup graph:

    public class PSSetupMaint : PXGraph<PSSetupMaint>
    {
        #region Selects / Views

        public PXSave<PSSetup> Save;
        public PXCancel<PSSetup> Cancel;

        public SelectFrom<PSSetup>.View Setup;

        #endregion
    }

And here is my main graph:

    public class PSImportEntry : PXGraph<PSImportEntry>
    {
        #region Selects / Views
        public PXCancelClose<PSImport> CancelClose;
        public PXSaveClose<PSImport> SaveClose;
        public PXSave<PSImport> Save;
        public PXCancel<PSImport> Cancel;
        public PXInsert<PSImport> Insert;
        public PXDelete<PSImport> Delete;
        public PXFirst<PSImport> First;
        public PXPrevious<PSImport> Previous;
        public PXNext<PSImport> Next;
        public PXLast<PSImport> Last;

        public PXSetup<PSSetup> PSSetup;

        public SelectFrom<PSImport>.View Import;

        [PXImport(typeof(PSTranFlats))]
        public SelectFrom<PSTranFlats>.
            Where<PSTranFlats.importID.IsEqual<PSImport.importID.FromCurrent>>.View
            FlatsTransactions;

        [PXImport(typeof(PSTranIDOA))]
        public SelectFrom<PSTranIDOA>.
            Where<PSTranIDOA.importID.IsEqual<PSImport.importID.FromCurrent>>.View
            IDOATransactions;

        [PXImport(typeof(PSTranMMS))]
        public SelectFrom<PSTranMMS>.
            Where<PSTranMMS.importID.IsEqual<PSImport.importID.FromCurrent>>.View
            MMSTransactions;

        [PXImport(typeof(PSTranOCR))]
        public SelectFrom<PSTranOCR>.
            Where<PSTranOCR.importID.IsEqual<PSImport.importID.FromCurrent>>.View
            OCRTransactions;

        [PXImport(typeof(PSTranPressero))]
        public SelectFrom<PSTranPressero>.
            Where<PSTranPressero.importID.IsEqual<PSImport.importID.FromCurrent>>.View
            PresseroTransactions;

        #endregion
    }

Solution

  • I am not 100% certain, but I think this is about the ID's in the ASPX rather than the database data. For example:

    <px:PXLayoutRule LabelsWidth="S" ControlSize="SM" ID="PXLayoutRule1" runat="server" StartRow="True"></px:PXLayoutRule>

    Notice ID="PXLayoutRule1" in that sample. If this "ID" value of any entry in the ASPX is duplicated anywhere in the screen as an ID of another entry, you will get an error.

    I had similar issues in an earlier version, especially when I would try to customize a screen and have the customization screen interface crash on me in the middle of it. I'd try deleting the screen, but I'd get orphaned files in the CstDesigner and CstPublished folder, which would really compound my issues.

    Hopefully, you know how to look through your ASPX file and the Customization Project XML to check all the ID's. If you do know how to walk through all that, just add something to all of your ID values, like an X at the end. (i.e. PXLayoutRule1X) If not, gaining that familiarity is a worthy side-objective as you continue on.

    If your issue is the same as I had in the past and you cannot simply find and edit the duplicate ID in the ASPX file or Project XML, here are some steps to take AFTER you backup your project and the affected folders noted in these steps. And, of course, this also assumes you are working in a development copy of your instance. (I often clone my main DEV instance to my laptop to test things like this if I am unsure so that my DEV instance stays unaffected. It just takes extra time you may or may not have.)

    1. Delete the screen from your customization project.
    2. Locate the folder CstDesigner and CstPublished under your instance's root folder (i.e. AcumticaDEV, Sandbox, etc. where your site exists)
    3. Go into the Pages_XX folder (where XX is the 2 character code for your 1st 2 letters of the screen ID)
    4. Delete the screen file(s) from there, if you find them.
    5. If this is a custom screen, go into the Pages folder which is located in the same root folder of your instance as those folders in step 2 and repeat steps 3 and 4 in that folder.
    6. Check for any reference to your screen in the Project XML from the file menu in the Customization Project screen and remove those sections if they exist. (This tends to relate more for customizing an existing screen.)
    7. Publish your project.
    8. Start over on your page and see if you still get the error.

    Again, be sure to get a backup before taking these steps so that you can easily put it back if that does not resolve your problem.