Force upper case on input - acumatica

I have an input mask that is supposed to convert to upper case. In fact, the display changes to upper case when focus is moved off the field but the data is still in lower case in the database. How can I force the save in uppercase also.
#region PartnerID
public abstract class partnerID : PX.Data.IBqlField { }
protected string _PartnerID;
[PXDBString(30, IsUnicode = true, IsKey = true, InputMask = ">CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")]
[PXDefault()]
[PXUIField(DisplayName = "Partner")]
public virtual string PartnerID { get; set; }
#endregion

InputMask set for PXDBStringAttribute will not make any effect without the use of PXMaskEdit in Aspx. Try to recreate input control in Layout Editor and test the results with PartnerID defined as an auto-implemented property.
To specify a different prompt character, you can set an HTML code of the new prompt character to the PromptChar property of PXMaskEdit in Aspx:
<px:PXMaskEdit ID="edPartnerID" runat="server" DataField="PartnerID" PromptChar="*" />

There are other (better?) way to achieve this but I believe the C# property setter could work:
#region PartnerID
public abstract class partnerID : PX.Data.IBqlField { }
protected string _PartnerID;
[PXDBString(30, IsUnicode = true, IsKey = true, InputMask = ">CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")]
[PXDefault()]
[PXUIField(DisplayName = "Partner")]
public virtual string PartnerID
{
get
{
return this._PartnerID;
},
set
{
this._PartnerID = value.ToUpper();
}
}
#endregion

Related

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
}
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.)
Delete the screen from your customization project.
Locate the folder CstDesigner and CstPublished under your instance's root folder (i.e. AcumticaDEV, Sandbox, etc. where your site exists)
Go into the Pages_XX folder (where XX is the 2 character code for your 1st 2 letters of the screen ID)
Delete the screen file(s) from there, if you find them.
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.
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.)
Publish your project.
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.

Adding custom fields to Bills and Adjustment screen causes "nullable object must have a value"

I have a customization to the Bills and Adjustments screen (AP301000), where I'm simply adding 4 user fields to the Document Details tab's grid. I've done this many, many times in the past and I've never seen this error. I have absolutely NO idea what would cause it.
Here's the DAC extension:
[PXCacheName("AP Tran Extension")]
public class APTranExt : PXCacheExtension<APTran>
{
#region UsrACAllocModule
[PXDBString(30, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Allocation Module")]
public virtual string UsrACAllocModule { get; set; }
public abstract class usrACAllocModule : PX.Data.BQL.BqlString.Field<usrACAllocModule> { }
#endregion
#region UsrACAllocBatch
[PXDBString(30, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Allocation Batch")]
public virtual string UsrACAllocBatch { get; set; }
public abstract class usrACAllocBatch : PX.Data.BQL.BqlString.Field<usrACAllocBatch> { }
#endregion
#region UsrACAllocLineNbr
[PXDBInt()]
[PXUIField(DisplayName = "Allocation LineNbr")]
public virtual int? UsrACAllocLineNbr { get; set; }
public abstract class usrACAllocLineNbr : PX.Data.BQL.BqlInt.Field<usrACAllocLineNbr> { }
#endregion
#region UsrACAllocationID
[PXDBString(30, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Allocation ID")]
public virtual string UsrACAllocationID { get; set; }
public abstract class usrACAllocationID : PX.Data.BQL.BqlString.Field<usrACAllocationID> { }
#endregion
}
The database fields exist as follows:
Here is how the fields are added:
And here is the error:
We ran across the same issue. It is a bug in Acumatica. It is resolved in build 20.104.

Need help in selector control

I created a selector control which displays a list of all serial #s from INItemLotSerial table, it works fine, the problem is the description field is showing InventoryID, how to show InventoryCD. Please have a look at below sample code.
[PXSelector(typeof(Search<INItemLotSerial.lotSerialNbr>),
new Type[] { typeof(INItemLotSerial.lotSerialNbr), typeof(INItemLotSerial.inventoryID) },
SubstituteKey = typeof(INItemLotSerial.lotSerialNbr), DescriptionField = typeof(INItemLotSerial.inventoryID))]
// i also joined with InventoryItem but this doesn't works.
[PXSelector(typeof(Search2<INItemLotSerial.lotSerialNbr,
LeftJoinSingleTable<InventoryItem, On<InventoryItem.inventoryID,Equal<INItemLotSerial.inventoryID>>>>),
new Type[] { typeof(INItemLotSerial.lotSerialNbr), typeof(INItemLotSerial.inventoryID) },
SubstituteKey = typeof(INItemLotSerial.lotSerialNbr), DescriptionField = typeof(InventoryItem.inventoryCD))]
The main problem with DescriptionField property is that it is waiting for getting the field from the same table for which Selector is written. But in the case of ID/CD usually, the CD is not present in the table where ID is present, except the main table.
UPDATED I have removed previous code (implementation using custom attributes and FieldSelecting event handler) because of the performance issues which it is bringing with it. The code below is resulting with the same lookup but getting the data with one inner join instead of all the requests which the previous code was doing.
You can do the following to get this lookup with description:
Create a PXProjection on INItemLotSerial and InventoryItem tables like below:
[PXCacheName("Lot Serials with Inventory CD")]
[PXProjection(typeof(Select2<INItemLotSerial,
InnerJoin<InventoryItem,
On<INItemLotSerial.inventoryID, Equal<InventoryItem.inventoryID>>>>))]
public class INItemLotSerialWithInventoryItem : IBqlTable
{
[PXDBInt(BqlField = typeof(INItemLotSerial.inventoryID))]
[PXUIField(DisplayName = "Inventory ID", Visibility = PXUIVisibility.Visible, Visible = false)]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : IBqlField { }
[PXDBString(InputMask = "", IsUnicode = true, BqlField = typeof(InventoryItem.inventoryCD))]
[PXUIField(DisplayName = "Inventory ID")]
public virtual string InventoryCD { get; set; }
public abstract class inventoryCD : IBqlField { }
[PXDBString(InputMask = "", IsUnicode = true, BqlField = typeof(INItemLotSerial.lotSerialNbr))]
[PXUIField(DisplayName = "Lot/Serial Nbr")]
public virtual string LotSerialNbr { get; set; }
public abstract class lotSerialNbr : IBqlField { }
}
Set your selector to use this PXProjection like below:
[PXSelector(typeof(Search<INItemLotSerialWithInventoryItem.lotSerialNbr>),
new Type[] { typeof(INItemLotSerialWithInventoryItem.lotSerialNbr) },
SubstituteKey = typeof(INItemLotSerialWithInventoryItem.lotSerialNbr),
DescriptionField = typeof(INItemLotSerialWithInventoryItem.inventoryCD))]
As a result, you will get lookup like below:

Acumatica - Possible to create several numberingId with existing numberingId

i have some problems with AutoNumberAttribute in Acumatica. In my Project Issue screen i can create new entity with existing numberingId (see screenshot below)
Field selector with AutoNumberAttribute
But other entities like POOrder, Case haven't this promlems. Code for this shown below:
[Serializable]
[PXEMailSource]
[PXPrimaryGraph(typeof(ProjectIssueMaint))]
[PXCacheName(Messages.ProjectIssue.CacheName)]
public class ProjectIssue : BaseCache, IBqlTable, IAssign, IPXSelectable
{
[PXDBIdentity]
[PXUIField(Visible = false, Visibility = PXUIVisibility.Invisible, DisplayName = Messages.ProjectIssue.NumberId)]
public virtual int? ProjectIssueId
{
get;
set;
}
[PXDefault]
[PXFieldDescription]
[PXDBString(10, IsKey = true, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = Messages.ProjectIssue.NumberId, Required = true)]
[PXSelector(typeof(Search<projectIssueCd>),
typeof(projectIssueCd),
typeof(projectId),
typeof(projectTaskId),
typeof(classId),
typeof(summary),
typeof(status),
typeof(ownerID),
Filterable = true)]
[AutoNumber(typeof(ProjectManagementSetup.projectIssueNumberingId), typeof(createdDateTime))]
public virtual string ProjectIssueCd
{
get;
set;
}
public abstract class projectIssueCd : IBqlField
{
}
public abstract class projectIssueId : IBqlField
{
}
}
[Serializable]
[PXCacheName(Messages.ProjectManagementSetup.CacheName)]
public class ProjectManagementSetup : BaseCache, IBqlTable
{
[PXDBString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
[PXDefault(Constants.ProjectIssue.NumberingId)]
[PXSelector(typeof(Numbering.numberingID), DescriptionField = typeof(Numbering.descr))]
[PXUIField(DisplayName = Messages.ProjectManagementSetup.ProjectIssueNumberingSequence)]
public virtual string ProjectIssueNumberingId
{
get;
set;
}
public abstract class projectIssueNumberingId : IBqlField
{
}
}
public class ProjectIssueMaint : PXGraph<ProjectIssueMaint, ProjectIssue>
{
[PXViewName(Messages.ProjectIssue.CacheName)]
[PXCopyPasteHiddenFields(typeof(ProjectIssue.status))]
public PXSelect<ProjectIssue> ProjectIssue;
[PXHidden]
[PXCheckCurrent]
public PXSetup<ProjectManagementSetup> ProjectManagementSetup;}
public class ProjectManagementSetupMaint : PXGraph<ProjectManagementSetupMaint>
{
public PXSave<ProjectManagementSetup> Save;
public PXCancel<ProjectManagementSetup> Cancel;
public PXSelect<ProjectManagementSetup> ProjectManagementSetup;
}
I reproproduce this issue by change the last number field in the numbering sequence screen (see screenshot https://snag.gy/UpIe8a.jpg).
So somebody know why this maybe happend? Any information will help me)
The AutoNumberAttribute completely relies on the configuration of the associated Numbering Sequence and does not have built-in validation to prevent insertion of the record with the duplicated number, which was generated by the AutoNumberAttribute. Since your table is built with A Pair of Columns with Key Substitution in the UI, you must add a unique index consisting of the CompanyID and ProjectIssueCd columns to prevent the insertion of multiple ProjectIssue records with the same ProjectIssueCd on the database level.
CREATE UNIQUE NONCLUSTERED INDEX [ProjectIssue_ProjectIssueCd_Uindex] ON [dbo].[ProjectIssue]
(
[CompanyID] ASC,
[ProjectIssueCd] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

Customizing Selector when another another Selector value changes

I need to Customize CaseClassID Selector on Cases Screen. It should refresh with different values when Contract field is selected on the CaseScreen under the Additional Info tab. Right now the CaseClass is displaying values from CRCase table.But, now if the contract changes the fieldupdatedEvent should be triggered and CaseClassID selector should have values accordingly. Please suggest me how to customize the CaseClassID selector inside FieldUpdated Event Handler
protected virtual void CRCase_ContractID_FieldUpdated(PXCache sender,
PXFieldUpdatedEventArgs e)
{
CRCase cc = (CRCase)e.Row;
if (cc == null) return;
CRCase_CaseClassID_CacheAttached(sender);
}
[PXMergeAttributes(Method = MergeMethod.Replace)]
[PXDBString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
[PXDefault(typeof(Search<CRSetup.defaultCaseClassID>))]
[PXUIField(DisplayName = "Class ID")]
[PXSelector(typeof(Search2<CRCaseClass.caseClassID,
InnerJoin<CaseContract, On<CaseContract.caseClassID,
Equal<CRCaseClass.caseClassID>>,
InnerJoin<Contract, On<CaseContract.contractID,
Equal<Contract.templateID>>,
InnerJoin<CRCase, On<Contract.contractID,
Equal<Current<CRCase.contractID>>>>>>,
Where<CaseContract.active, Equal<True>>>),
DescriptionField = typeof(CRCaseClass.description),
CacheGlobal = true)]
[PXMassUpdatableField]
public virtual String CaseClassID { get; set; }
public virtual void CRCase_CaseClassID_CacheAttached(PXCache sender){
}
Override CaseClassID selector and add your custom logic in its type parameter.
You can create a custom unbound field to filter the selector according to your business logic. When you want to change selector filter, just change the custom field.
#region CaseClassID
public abstract class caseClassID : IBqlField { }
[PXDBString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
[PXDefault(typeof(Search<CRSetup.defaultCaseClassID>))]
[PXUIField(DisplayName = "Class ID")]
// Use your custom field (filterCaseClassID) in the selector type parameter
[PXSelector(typeof(Search<CRCaseClass.caseClassID,
Where<CRCaseClass.caseClassID, Equal<filterCaseClassID>>>),
DescriptionField = typeof(CRCaseClass.description),
CacheGlobal = true)]
[PXMassUpdatableField]
public virtual String CaseClassID { get; set; }
#endregion
#region FilterCaseClassID
public abstract class filterCaseClassID : IBqlField { }
[PXDBString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
// Change the value of your custom field to set the selector filter
public virtual String FilterCaseClassID { get; set; }
#endregion
EDIT overriding DAC field in graph extension and filtering Selector by using a field from another DAC:
using PX.Data;
using PX.Objects.CR.MassProcess;
using System;
namespace PX.Objects.CR
{
[Serializable]
public class CaseContract : IBqlTable
{
public abstract class caseClassID : IBqlField { }
[PXString(10, IsUnicode = true)]
[PXUIField(DisplayName = "Case Class ID")]
[PXSelector(typeof(CRCaseClass.caseClassID),
DescriptionField = typeof(CRCaseClass.description))]
public virtual String CaseClassID
{
get; set;
}
}
public class CRCaseMaint_Extension : PXGraphExtension<CRCaseMaint>
{
[PXMergeAttributes(Method = MergeMethod.Replace)]
[PXDBString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
[PXDefault(typeof(Search<CRSetup.defaultCaseClassID>))]
[PXUIField(DisplayName = "Class ID")]
[PXSelector(typeof(Search<CRCaseClass.caseClassID,
Where<Current<CaseContract.caseClassID>, IsNull,
Or<CRCaseClass.caseClassID, Equal<Current<CaseContract.caseClassID>>>>>),
DescriptionField = typeof(CRCaseClass.description),
CacheGlobal = true)]
[PXMassUpdatableField]
public virtual void CRCase_CaseClassID_CacheAttached(PXCache sender)
{
}
}
}

Resources