I have a customer utilizing the User Defined fields. I found that the values are located in KvExt tables in the database, but I have not found a way to access those directly through DACs or DAC extensions. Is there a way I can access that field and add it to a base Acumatica page?
The specific target in my case is the ARTran.RefNbr selector in the ARPayments page.
As you probably know the User Defined Fields are actually using the Attributes defined in the system. The User Defined Field and actual record (for example ARInvoice) are bound by NoteID of the record (ARInvoice) and RecordID of the User Defined Field. And the FieldName of the User Defined Field is storing word 'Attribute' + AttributeID.
Below SQL Queries show that references:
SELECT CompanyID, RefNbr, DocType, DocDesc FROM ARRegister WHERE RefNbr='AR007092';
SELECT CompanyID, RecordID, FieldName, ValueNumeric, ValueDate, ValueString, ValueText FROM ARRegisterKvExt where RecordID='78A9D6DE-52C4-E911-B2FD-FC017C8C8936';
SELECT CompanyID, AttributeID, Description, ControlType, EntryMask, RegExp, List, IsInternal, ContainsPersonalData FROM CSAttribute WHERE'AttributeBURDEN' LIKE '%'+AttributeID;
And the result sets:
After knowing the references shown above, we can create a DAC to the ARRegisterKvExt table like below:
[PXCacheName("AR Register Attributes")]
[Serializable]
public class ARRegisterKvExt : IBqlTable
{
public abstract class recordID : BqlGuid.Field<recordID> { }
[PXDBGuid(IsKey = true)]
public Guid? RecordID { get; set; }
public abstract class fieldName : BqlString.Field<fieldName> { }
[PXDBString(50,IsKey = true)]
[PXUIField(DisplayName ="Name")]
public string FieldName { get; set; }
public abstract class valueNumeric : BqlDecimal.Field<valueNumeric> { }
[PXDBDecimal(8)]
[PXUIField(DisplayName = "Value Numeric")]
public decimal? ValueNumeric { get; set; }
public abstract class valueDate : BqlDateTime.Field<valueDate> { }
[PXDBDate]
[PXUIField(DisplayName = "Value Date")]
public DateTime? ValueDate { get; set; }
public abstract class valueString : BqlString.Field<valueString> { }
[PXDBString(256)]
[PXUIField(DisplayName = "Value String")]
public string ValueString { get; set; }
public abstract class valueText : BqlString.Field<valueText> { }
[PXDBString]
[PXUIField(DisplayName = "Value Text")]
public string ValueText { get; set; }
}
And write a PXSelector with Left Joins to our DAC and CSAttribute like below:
[PXSelector(typeof(Search2<ARInvoice.refNbr, LeftJoin<ARRegisterKvExt, On<ARInvoice.noteID,Equal<ARRegisterKvExt.recordID>>, LeftJoin<CSAttribute, On<ARRegisterKvExt.fieldName,Contains<CSAttribute.attributeID>>>>>), new[] { typeof(ARInvoice.refNbr), typeof(ARInvoice.docType), typeof(CSAttribute.description), typeof(ARRegisterKvExt.valueString)}, DescriptionField = typeof(ARInvoice.docDesc))]
As a result, you will see lookup like below:
As you can see the AR Invoices which don't have value for the User Defined Fields don't have the Name of the field too. This is caused by the way Acumatica is handling the User Defined Fields. The record for the User Defined Fields is being written to the database only when you set a value. And deletes that record if you clear the value.
The bad side of using the User Defined Fields in the lookups is that if you have 2 User Defined Fields for AR Invoice then the same Invoice will be shown twice, but only if the values for both User Defined Fields are set.
Related
when I add a new field, in the payment configuration tab.
It adds the field in location to the database, but at the same time I create DAC addressExt, I don't understand why.
here is the DAC of each one that created me.
First.
the field is created in this table: Location
namespace PX.Objects.CR.Standalone
{
public class LocationExt : PXCacheExtension<PX.Objects.CR.Standalone.Location>
{
#region UsrTest
[PXDBBool]
[PXUIField(DisplayName="test")]
public virtual bool? UsrTest { get; set; }
public abstract class usrTest : PX.Data.BQL.BqlBool.Field<usrTest> { }
#endregion
}
}
second.
namespace PX.Objects.CR
{
public class AddressExt : PXCacheExtension<PX.Objects.CR.Address>
{
#region UsrTest
[PXDBBool]
[PXUIField(DisplayName = "test")]
public virtual bool? UsrTest { get; set; }
public abstract class usrTest : PX.Data.BQL.BqlBool.Field<usrTest> { }
#endregion
}
}
view image:
I hope you understand, I'm not good at English.
This is actually being done by design. The DAC you are editing is a Projection consisting of multiple DACs. Check this link out from Sergey Marenich
PXProjection uses 2 Data Access Classes for quotes:
One class represents the table – PX.Objects.CR.Standalone.CRQuote.
Another represents view as combination of DACs in select – PX.Objects.CR.CRQuote.
And to correctly work, custom field needs to be added in both of the places. And Projection field should be linked to table field using BqlField property of DBFieldAttributes.
I want to add a AutoGenerate number column in PXProjection DAC?
What i want,
In PXProjection DAC, I wan to add a Field, which should generate a unique number for each row, It might be unique with the combinaion of Fields consider in Projection, How may i do it?
Since I'm not sure how well you understand projections, let me cover some basics as I understand them first. A PXProjection is the xRP Framework equivalent of a SQL view. It does not contain any actual database tables or fields but simply creates access to a "view" into the data. As such, it does not have the ability to have a sequence, or identity, field of its own. It does, however, require that you define the key field(s). Once defined, you would retrieve the records of the projection as if it was its own table using reference to all of those key fields.
Here is a sample (not all fields) of one of my projections.
[PXProjection(typeof(
SelectFrom<INTran>
.LeftJoin<INLocation>
.On<INLocation.locationID.IsEqual<INTran.locationID>>
.LeftJoin<SOShipLine>
.On<SOShipLine.shipmentNbr.IsEqual<INTran.sOShipmentNbr>
.And<SOShipLine.lineNbr.IsEqual<INTran.sOShipmentLineNbr>>>
.LeftJoin<SOLine>
.On<SOLine.orderNbr.IsEqual<SOShipLine.origOrderNbr>
.And<SOLine.orderType.IsEqual<SOShipLine.origOrderType>
.And<SOLine.lineNbr.IsEqual<SOShipLine.origLineNbr>>>>
.LeftJoin<SOOrder>
.On<SOOrder.orderNbr.IsEqual<SOShipLine.origOrderNbr>
.And<SOOrder.orderType.IsEqual<SOShipLine.origOrderType>>>
.LeftJoin<ContactBAccount>
.On<ContactBAccount.bAccountID.IsEqual<SOOrder.billContactID>>
.LeftJoin<POReceiptLine>
.On<POReceiptLine.receiptNbr.IsEqual<INTran.pOReceiptNbr>
.And<POReceiptLine.lineNbr.IsEqual<INTran.pOReceiptLineNbr>>>
.LeftJoin<POOrder>
.On<POOrder.orderNbr.IsEqual<POReceiptLine.pONbr>
.And<POOrder.orderType.IsEqual<POReceiptLine.pOType>>>
))]
[Serializable]
[PXCacheName("Transactions Projection")]
public partial class MYINTransactions : IBqlTable
{
#region DocType
public abstract class docType : PX.Data.BQL.BqlString.Field<docType> { }
[PXDBString(1, IsFixed = true, IsKey = true, BqlField = typeof(INTran.docType))]
[PXUIField(DisplayName = INRegister.docType.DisplayName)]
public virtual String DocType { get; set; }
#endregion
#region TranType
public abstract class tranType : PX.Data.BQL.BqlString.Field<tranType> { }
[PXDBString(3, IsFixed = true, BqlField = typeof(INTran.tranType))]
[INTranType.List()]
[PXUIField(DisplayName = "Tran. Type")]
public virtual String TranType { get; set; }
#endregion
#region RefNbr
public abstract class refNbr : PX.Data.BQL.BqlString.Field<refNbr> { }
[PXDBString(15, IsUnicode = true, IsKey = true, BqlField = typeof(INTran.refNbr))]
[PXUIField(DisplayName = INRegister.refNbr.DisplayName)]
public virtual String RefNbr { get; set; }
#endregion
#region LineNbr
public abstract class lineNbr : PX.Data.BQL.BqlInt.Field<lineNbr> { }
[PXDBInt(IsKey = true, BqlField = typeof(INTran.lineNbr))]
[PXUIField(DisplayName = "Line Number")]
public virtual Int32? LineNbr { get; set; }
#endregion
#region TranDate
public abstract class tranDate : PX.Data.BQL.BqlDateTime.Field<tranDate> { }
[PXDBDate(BqlField = typeof(INTran.tranDate))]
public virtual DateTime? TranDate { get; set; }
#endregion
#region InvtMult
public abstract class invtMult : PX.Data.BQL.BqlShort.Field<invtMult> { }
[PXDBShort(BqlField = typeof(INTran.invtMult))]
[PXUIField(DisplayName = "Multiplier")]
public virtual Int16? InvtMult { get; set; }
#endregion
#region BranchID
public abstract class branchID : PX.Data.BQL.BqlInt.Field<branchID> { }
[Branch(typeof(INTran.branchID), BqlField = typeof(INTran.branchID))]
public virtual Int32? BranchID { get; set; }
#endregion
#region InventoryID
public abstract class inventoryID : PX.Data.BQL.BqlInt.Field<inventoryID> { }
[PXDBInt(BqlField = typeof(INTran.inventoryID))]
public virtual Int32? InventoryID { get; set; }
#endregion
#region SubItemID
public abstract class subItemID : PX.Data.BQL.BqlInt.Field<subItemID> { }
[PXDBInt(BqlField = typeof(INTran.subItemID))]
public virtual Int32? SubItemID { get; set; }
#endregion
}
As you can see, it pulls data from many tables and then defines fields to be accessed via MYINTransactions and includes IsKey on the key fields and "BqlField =" to map to the related fields. Even though we use PXDB for the field attributes, these are not actual DB fields themselves. The PXProjectionAttribute overrides the behavior of the fields. I believe it is possible to set properties that will enable the projection to maintain data, at which point, the PXDB type attributes should convey the applicability to the database when the SQL commands are built from BQL. That's more advanced than I have had time to explore myself, so I won't go into any further detail there.
Now, more specifically to your question directly... Depending on how you intend to use the projection, you might define additional fields using PXDBScalar or PXFormula to populate the values, but in my attempts, this had unreliable results. The issue for me was that I needed the values at the time of the actual SQL call, and the fields, being unbound, did not exist at the database level. They would work on my screen, but some of my more complex BQL statements would fail because the values at the time of the SQL call were null due to being unbound.
The best recommendation that I can make is that you use a database field in one of the base DAC's to produce the desired field and map a field in your projection to that field. This will produce the most reliable results, should you try to use the projection in complex ways. Alternatively, if this is for use in a GI, you can use a formula in the GI's field definition to concatenate fields, if that is your goal.
We have a custom graph that was built for us by Acumatica; it uses a PXProjection called by the primary graph's PXSelectJoin to select data and populate the fields.
A couple of fields are inventory ID fields:
#region KitComponentID
public abstract class kitComponentID : IBqlField { }
[StockItem(DisplayName = "Kit Component ID", IsKey = true, BqlField = typeof(INKitSpecStkDet.compInventoryID))]
public virtual int? KitComponentID { get; set; }
#endregion
#region KitInventoryID
public abstract class kitInventoryID : IBqlField { }
[SOLineInventoryItem(DisplayName = "Kit ID", BqlField = typeof(SOLine.inventoryID))]
public virtual int? KitInventoryID { get; set; }
#endregion
These fields are not user editable, only for display. We would like to have them display only the InventoryCD, not the CD and description.
Is there a different way I can define my fields so that they are still automatically populated but display only the InventoryCD?
Under the screen editor, the field should have a drop-down of DisplayMode. By default, I believe it's Hint (which is Key + Description), Value (which is just key), and Text (which is Description.)
If you change the DisplayMode to Value, it should give you what you want.
I use PxProjection attribute in a way that select the specific fields of DACs. How should I pass filter parameter in the select command(the same way as it is in the graph by using Or<CRInsurancePolicy.currentreportsTo, Equal<Current<BAccountParam.bAccountID>>> ) in order to have filtered data in the graph?
I propose to look at Select2 attribute in PXProjection.
Select2 attribute allows to use filter - all possible commands of BQL (Join, Where, GroupBy, OrderBy).
For examle here, the attribute joins data from two table and projects it to the single DAC:
[Serializable]
[PXProjection(typeof(Select2<Supplier,
InnerJoin<SupplierProduct,
On<SupplierProduct.accountID, Equal<Supplier.accountID>>>>))]
public partial class SupplierPrice : IBqlTable
{
public abstract class accountID : PX.Data.IBqlField
{
}
// The field mapped to the Supplier field (through setting of BqlField)
[PXDBInt(IsKey = true, BqlField = typeof(Supplier.accountID))]
public virtual int? AccountID { get; set; }
public abstract class productID : PX.Data.IBqlField
{
}
// The field mapped to the SupplierProduct field
// (through setting of BqlField)
[PXDBInt(IsKey = true, BqlField = typeof(SupplierProduct.productID))]
[PXUIField(DisplayName = "Product ID")]
public virtual int? ProductID { get; set; }
...
}
I have created a Name value pair field for Sales Order table and Search is giving error while using the field from Description field in selector
#region UsrLoader
public abstract class usrLoader : IBqlField { }
[PXDBString(128, IsUnicode = true)]
[PXUIField(DisplayName = "Operator 1")]
[PXSelector(typeof(EPEmployee.acctCD),
new Type[]
{
typeof(EPEmployee.acctCD),
typeof(EPEmployee.acctName)
},
DescriptionField = typeof(EPEmployee.acctName))]
public virtual string UsrLoader { get; set; }
#endregion
#region UsrUnLoader
[PXDBString(128)]
[PXUIField(DisplayName = "Operator 2")]
[PXSelector(typeof(EPEmployee.acctCD),
new Type[]
{
typeof(EPEmployee.acctCD),
typeof(EPEmployee.acctName)
},
DescriptionField = typeof(EPEmployee.acctName))]
public virtual string UsrUnLoader { get; set; }
public abstract class usrUnLoader : IBqlField { }
#endregion
I am getting the following error
Typically I see the Invalid column error when the customization hasn't been published to insert the field in the database table. Did you publish after adding the fields?
Also, the warnings might tell you to join in the parent table. Did you try doing a join to the EPEmployee table to use the description/name field in your GI?