Extend Acumatica Projection Based DAC Query - acumatica

Is there any way to extend/modify the projection query of a projected DAC.
For example, if I need to add a join statement to the projection and then use the newly joined table to the available fields.
Adding a custom field to the PXCacheExtension works as expected, but specifying the PXProjection query at the top of the PXCacheExtension DAC don't seems to have any effect.
Original:
[Serializable]
[PXProjection(typeof(Select5<EPApproval, InnerJoin<Note,
On<Note.noteID, Equal<EPApproval.refNoteID>,
And<EPApproval.status, Equal<EPApprovalStatus.pending>>>,>,
Where2<Where<EPApproval.ownerID, IsNotNull, And<EPApproval.ownerID, Equal<CurrentValue<AccessInfo.userID>>>>,
Or2<Where<EPApproval.workgroupID, InMember<CurrentValue<AccessInfo.userID>>,
Or<EPApproval.workgroupID, IsNull>>,
Or<EPApproval.workgroupID, Owned<CurrentValue<AccessInfo.userID>>>>>,
Aggregate<GroupBy<EPApproval.refNoteID,
GroupBy<EPApproval.curyInfoID,
GroupBy<EPApproval.bAccountID,
GroupBy<EPApproval.ownerID,
GroupBy<EPApproval.approvedByID,
GroupBy<EPApproval.curyTotalAmount>>>>>>>>))]
public partial class EPOwned : EPApproval{
Extended:
[Serializable]
[PXProjection(typeof(Select5<EPApproval, InnerJoin<Note,
On<Note.noteID, Equal<EPApproval.refNoteID>,
And<EPApproval.status, Equal<EPApprovalStatus.pending>>>,
LeftJoin<RQRequest, On<RQRequest.noteID, Equal<Note.noteID>>>>,
Where2<Where<EPApproval.ownerID, IsNotNull, And<EPApproval.ownerID, Equal<CurrentValue<AccessInfo.userID>>>>,
Or2<Where<EPApproval.workgroupID, InMember<CurrentValue<AccessInfo.userID>>,
Or<EPApproval.workgroupID, IsNull>>,
Or<EPApproval.workgroupID, Owned<CurrentValue<AccessInfo.userID>>>>>,
Aggregate<GroupBy<EPApproval.refNoteID,
GroupBy<EPApproval.curyInfoID,
GroupBy<EPApproval.bAccountID,
GroupBy<EPApproval.ownerID,
GroupBy<EPApproval.approvedByID,
GroupBy<EPApproval.curyTotalAmount>>>>>>>>))]
public class EPOwnedExt : PXCacheExtension<EPApprovalProcess.EPOwned> {
Thanks

To modify the projection query of a projected DAC, you should create an inherited DAC and decorate it with the PXSubstituteAttribute. Below is the sample for the FAAccrualTran DAC:
[Serializable]
[PXProjection(typeof(Select2<GLTran,
LeftJoin<FAAccrualTran, On<GLTran.tranID, Equal<FAAccrualTran.tranID>>>,
Where<GLTran.module, NotEqual<BatchModule.moduleFA>, And<GLTran.released, Equal<True>>>>), new Type[] { typeof(FAAccrualTran) })]
[PXSubstitute(GraphType = typeof(AssetGLTransactions))]
...
[PXSubstitute(GraphType = typeof(AssetMaint))]
public partial class FAAccrualTranCst : FAAccrualTran
{
...
}
You can decorate a DAC with PXSubstituteAttribute multiple times: 1 PXSubstituteAttribute per 1 BLC, for which custom FAAccrualTranCst will be used instead of the base FAAccrualTran class.
If you specify no value for GraphType property of the PXSubstituteAttribute, your custom DAC will replace its base DAC in all BLCs.

Related

Add Attribute in “Add Item” dialog box in Purchase Order screen of Acumatica

How can I add an Attribute column from "Stock Items" screen to "Add Item" dialog box of Purchase Order screen. I want to add the following attribute from Stock Items screen to the "Add Item" Dialog box of Purchase Order Screen.
Please review the images below for Stock Item and Purchase Order Screens.
I am able to get the field in the inventory lookup of PO, the values did not populated.
here goes my code....
namespace PX.Objects.PO
{
[PXProjection(typeof(Select<CSAnswers, Where<CSAnswers.refNoteID, Equal<POSiteStatusSelected.noteID>,
And<CSAnswers.attributeID, Equal<AttribMyAttribute>>>>), Persistent = false)]
public class POSiteStatusSelectedExt : PXCacheExtension<PX.Objects.PO.POSiteStatusSelected>
{
#region UsrItemType
[PXDBString(10, IsFixed = true, BqlField = typeof(CSAnswers.value))]
[PXUIField(DisplayName = "Item Type")]
//[PXDBScalar(typeof(Search<CSAnswers.value, Where<CSAnswers.refNoteID, Equal<POSiteStatusSelected.noteID>,
// And<CSAnswers.attributeID, Equal<AttribMyAttribute>>>>))]
public virtual string UsrItemType { get; set; }
public abstract class usrItemType : IBqlField { }
#endregion
}
public class AttribMyAttribute : Constant<string>
{
public AttribMyAttribute() : base("ITEMTYPE") { }
}
}
I have created a DAC extension of POSiteStatusSeleted view and added my custom field which is a non-persisted field. There is noteId field in the POSiteStatusSeleted which is of type InventoryItem.noteID, i tried to use the same in the PXDBScalar attribute(the code line is commented), this also didn't work out, it was showing an error for "Unable to convert System.Int32 to System.String".
Updated Answer: Because of the projection, we have to create a new class that inherits the DAC and uses the [PXSubstitute] attribute. The nature of PXSubstitute means that this will not be a DAC extension or even part of a Graph Extension. In my testing, I tried encapsulating this in a graph extension on POOrderEntry, and it did not work. By following the instructions of the stack overflow post below, I was able to create an Attribute called ITEMTYPE, assign it to one of my Item Classes, replace the PXProjection with an enhanced version that includes a left join back to the attribute table (CSAnswers) and then add it to the screen's smart panel grid.
Extend Acumatica Projection Based DAC Query
In the code sample below, the magic is in PXSubstitute, which will take the new class that you create and inherit from the base DAC and replace that base DAC with your new one. If you specify a graph, it will perform the substitution in only that graph (or each graph you specify - 1 per PXSubstitute attribute used). If you do not specify a graph, it will override the base DAC in every case it is used within the xRP Framework.
Code to perform the stated modification:
using PX.Data;
using PX.Objects.AP;
using PX.Objects.Common.Bql;
using PX.Objects.CS;
using PX.Objects.IN;
using System;
namespace PX.Objects.PO
{
[System.SerializableAttribute()]
[PXProjection(typeof(Select2<InventoryItem,
LeftJoin<CSAnswers,
On<CSAnswers.refNoteID, Equal<InventoryItem.noteID>,
And<CSAnswers.attributeID, Equal<AttribItemType>>>,
LeftJoin<INSiteStatus,
On<INSiteStatus.inventoryID, Equal<InventoryItem.inventoryID>, And<INSiteStatus.siteID, NotEqual<SiteAttribute.transitSiteID>>>,
LeftJoin<INSubItem,
On<INSiteStatus.FK.SubItem>,
LeftJoin<INSite,
On<INSiteStatus.FK.Site>,
LeftJoin<INItemXRef,
On<INItemXRef.inventoryID, Equal<InventoryItem.inventoryID>,
And2<Where<INItemXRef.subItemID, Equal<INSiteStatus.subItemID>,
Or<INSiteStatus.subItemID, IsNull>>,
And<Where<CurrentValue<POSiteStatusFilter.barCode>, IsNotNull,
And<INItemXRef.alternateType, Equal<INAlternateType.barcode>>>>>>,
LeftJoin<INItemPartNumber,
On<INItemPartNumber.inventoryID, Equal<InventoryItem.inventoryID>,
And<INItemPartNumber.alternateID, Like<CurrentValue<POSiteStatusFilter.inventory_Wildcard>>,
And2<Where<INItemPartNumber.bAccountID, Equal<Zero>,
Or<INItemPartNumber.bAccountID, Equal<CurrentValue<POOrder.vendorID>>,
Or<INItemPartNumber.alternateType, Equal<INAlternateType.cPN>>>>,
And<Where<INItemPartNumber.subItemID, Equal<INSiteStatus.subItemID>,
Or<INSiteStatus.subItemID, IsNull>>>>>>,
LeftJoin<INItemClass,
On<InventoryItem.FK.ItemClass>,
LeftJoin<INPriceClass,
On<INPriceClass.priceClassID, Equal<InventoryItem.priceClassID>>,
LeftJoin<Vendor,
On<Vendor.bAccountID, Equal<InventoryItem.preferredVendorID>>,
LeftJoin<INUnit,
On<INUnit.inventoryID, Equal<InventoryItem.inventoryID>,
And<INUnit.unitType, Equal<INUnitType.inventoryItem>,
And<INUnit.fromUnit, Equal<InventoryItem.purchaseUnit>,
And<INUnit.toUnit, Equal<InventoryItem.baseUnit>>>>>>>>>>>>>>>,
Where2<CurrentMatch<InventoryItem, AccessInfo.userName>,
And2<Where<INSiteStatus.siteID, IsNull, Or<INSite.branchID, IsNotNull, And2<CurrentMatch<INSite, AccessInfo.userName>,
And<Where2<FeatureInstalled<FeaturesSet.interBranch>,
Or2<SameOrganizationBranch<INSite.branchID, Current<POOrder.branchID>>,
Or<CurrentValue<POOrder.orderType>, Equal<POOrderType.standardBlanket>>>>>>>>,
And2<Where<INSiteStatus.subItemID, IsNull,
Or<CurrentMatch<INSubItem, AccessInfo.userName>>>,
And<InventoryItem.stkItem, Equal<boolTrue>,
And<InventoryItem.itemStatus, NotEqual<InventoryItemStatus.inactive>,
And<InventoryItem.itemStatus, NotEqual<InventoryItemStatus.unknown>,
And<InventoryItem.itemStatus, NotEqual<InventoryItemStatus.markedForDeletion>,
And<InventoryItem.itemStatus, NotEqual<InventoryItemStatus.noPurchases>>>>>>>>>>), Persistent = false)]
//[PXSubstitute(GraphType = typeof(POOrderEntry))]
[PXSubstitute]
public partial class POSiteStatusSelectedCst : POSiteStatusSelected
{
#region UsrItemType
[PXDBString(10, BqlField = typeof(CSAnswers.value))]
[PXUIField(DisplayName = "Item Type")]
public string UsrItemType { get; set; }
public abstract class usrItemType : PX.Data.BQL.BqlString.Field<usrItemType> { }
#endregion
}
public class AttribItemType : PX.Data.BQL.BqlString.Constant<AttribItemType>
{
public AttribItemType() : base("ITEMTYPE") { }
}
}

Can I replace an attribute of a class?

In the Sales Order screen, there is this view :
public PXSelect<SiteStatus> sitestatusview;
And SiteStatus table is declared as this :
[SiteStatusAccumulator]
[PXDisableCloneAttributes()]
[Serializable]
public partial class SiteStatus : INSiteStatus, IQtyAllocated
{...
...}
SiteStatusAccumulator among its usage is to check available quantities. And I want to override this validation. Particularly the SiteStatusAccumulator.PersistInserted method. With fields, I can cache_attached and override the attributes. But with tables, how can you achieve something similar ?

How to generate SQL with specific table columns using BQL

When I join two DAC in BQL syntax its generates SQL which selects all the columns from both tables. What if I want to select one table's columns in order to reach good execution plan?
You could try to look into PXProjection where the Columns are defined as the fields within the projection class. PXProjection is like a SQL view as a DAC in Acumatica. Just search the Acumatica source for PXProjection and you should find many examples. Note that within the class you need to set BqlField for each "column" so the process knows which table.field your projection field maps to.
Quick join PXProjection below. In this example there will be only 1 column in the DAC and it maps to APRegister.docType
[PXProjection(typeof(Select2<APRegister,
InnerJoin<APInvoice, On<APInvoice.docType, Equal<APRegister.docType>,
And<APInvoice.refNbr, Equal<APRegister.refNbr>>>,
InnerJoin<APPayment, On<APPayment.docType, Equal<APRegister.docType>,
And<APPayment.refNbr, Equal<APRegister.refNbr>>>>>,
Where<APRegister.docType, Equal<APDocType.quickCheck>,
Or<APRegister.docType, Equal<APDocType.voidQuickCheck>>>>), Persistent = true)]
[Serializable]
public partial class APQuickCheck : APRegister
{
#region DocType
public new abstract class docType : PX.Data.IBqlField
{
}
[PXDBString(3, IsKey = true, IsFixed = true, BqlField = typeof(APRegister.docType))]
[PXDefault(APDocType.QuickCheck)]
[APQuickCheckType.List()]
[PXUIField(DisplayName = "Type", Visibility = PXUIVisibility.SelectorVisible, Enabled = false)]
[PXFieldDescription]
public override String DocType
{
get
{
return this._DocType;
}
set
{
this._DocType = value;
}
}
#endregion
}

Accessing user-defined fields added via an extension in Acumatica

I have a custom field called 'UsrIsTeacherBook` which was added to the InventoryItem with the following extension:
namespace Lasalle.TeacherBooks
{
public class InventoryItem_TeacherBooks_Extension : PXCacheExtension<InventoryItem>
{
[PXDBBool]
[PXUIField(DisplayName = "Is Teacher Book")]
public virtual bool? UsrIsTeacherBook { get; set; }
public abstract class usrIsTeacherBook : IBqlField { }
}
}
I need to be able to access the value of this IsTeacherBook field from the SOLine grid on the SalesOrder screen. I added a custom field UsrTeacherBook to the SOLine grid on the sales order screen, but I cannot figure out how to populate this field with the value of InventoryItem UsrIsTeacherBook.
I tried customizing the attributes on the SOLine field in the following way:
[PXDBBool]
[PXUIField(DisplayName="Teacher Manual", Visible = true, Enabled = false)]
[PXFormula(typeof(Selector<SOLine.inventoryID, InventoryItemExt.usrIsTeacherBook>))]
But this produced a validation error, "The type name 'usrIsTeacherBook' does not exist in the type 'PX.Objects.IN.InventoryItemExt'."
What is the correct way to access the InventoryItem IsTeacherBook field to populate my field on the SOLine grid?
Your extension class name is InventoryItem_TeacherBooks_Extension, not InventoryItemExt used in PXFormulaAttribute. You should either change your extension name to InventoryItemExt or modify PXFormula declaration with InventoryItem_TeacherBooks_Extension.usrIsTeacherBook

Get combination of CD and description for PXSelector

I'm trying to use a Selector to lookup up customers, and I want it to show the CD along with the description in the field. I've seen this many times in Acumatica - and I thought I knew how to do it, but it's not working. Here is my code:
#region CustomerLookup
public abstract class customerLookup : PX.Data.IBqlField
{
}
protected string _CustomerLookup;
[PXDBString(100, IsUnicode = true)]
[PXUIField(DisplayName = "Customer Lookup")]
[PXSelector(typeof(Customer.acctCD)
,typeof(Customer.acctCD)
,typeof(Customer.acctName)
,DescriptionField=typeof(Customer.acctName))]
public virtual string CustomerLookup
{
get
{
return this._CustomerLookup;
}
set
{
this._CustomerLookup = value;
}
}
#endregion
I would have thought providing the DescriptionField would take care of this, but it does not.
Before answering the question, let me first mention 2 major issues with the code snippet above:
In Acumatica Customers is one the specific system object types, that supports segmented keys. To create a look up for records, that support segmented keys, you should be using the PXDimensionSelectorAttribute instead of the PXSelectorAttribute. For Customer entity there are several attributes provided out of the box, like the CustomerAttribute or the CustomerActiveAttribute, that you can use to create Customer lookups:
CustomerLookup field must be of the Int32? (int?) type: in Acumatica users can change Customer ID with time (via the Change ID button on the Customers screen). To establish a foreign key for the Customer DAC, the better approach is to declare an integer field and associate your custom record with Customers though the Customer.BAccount field
Below is the recommended declaration of a DAC field, that creates Customer lookup in UI:
[Serializable]
public partial class DetailsResult : IBqlTable
{
...
#region CustomerId
public abstract class customerId : PX.Data.IBqlField
{
}
[Customer(DescriptionField = typeof(Customer.acctName))]
public virtual Int32? CustomerID { get; set; }
#endregion
...
}
With the DescriptionField property specified, PXDimensionSelector placed on a form will by default format selected record according to the "{SearchKey/SubstituteKey} - {DescriptionField}" pattern:
To show DescriptionField in a grid cell, you have to define additional column for the CustomerID_description field (where _description part is a special key word for Acumatica used specifically in this type of requests):
I believe the answer is one that's obvious - I don't think the description field works the same way in the grid. In a header field, it works to show the CD - Description when you navigate off of it. Grid fields apparently don't work that way, even when you specify the description field in the selector the way you would in a header selector.

Resources