Pull Attributes from Stock Item to SO Line custom field - acumatica

I'm relatively new to customization programming in Acumatica and I have a question about the best way to pull in attribute values from the stock item screen to a custom field on the SO Line in the sales order screen.
I have done something similar on another screen using a FieldSelecting event and a PXSelect statement to pull data from another screen and update my custom field.
Needing to pull the values from the attribute value is what's stumping me, should I just join to the CSAnswers table in the pxselect?
I also wanted to ask if there is a better overall approach for pulling data from another screen into a custom field in an Acumatica customization.
*update *
I'm attempting to use the PXDBScalar attribute, but I cannot figure out how to join multiple tables. Here's what I've tried.
[PXDBScalar(typeof(
Search2<CSAnswers.value,
InnerJoin<InventoryItem,
On<SOLine.inventoryID, Equal<InventoryItem.inventoryID>>>,
InnerJoin<CSAnswers,
On<InventoryItem.noteID, Equal<CSAnswers.refNoteID>>>,
Where<CSAnswers.attributeID, Like<QHOLDAttr>>
>))]
Thanks
Scott

Since you asked for "a better overall approach" then I'll share my preferred way of handling this. Not to say it is "better" but perhaps just "different".
I try to keep my data tied to the object where it is related directly. In the case of an attribute of an InventoryItem, I would elevate that attribute value to InventoryItem in a DAC extension so that it can be leveraged anywhere the InventoryItem is used. You seem to want to use it in conjunction with the SOLine record, but since the attribute is not tied to the SOLine, I would not extend SOLine to add it. There is nothing wrong with adding it there if your business requirement mandates it, but it keeps me more sane to know that an InventoryItem's related data comes from InventoryItem rather than trying to remember where I put it and possibly duplicate the effort elsewhere (like on a POLine) later.
Here is an example of how I've done it, pulled straight from my project but changing the Attribute references to be more generic.
public sealed class InventoryItemExt : PXCacheExtension<PX.Objects.IN.InventoryItem>
{
#region MyAttribute
[PXString]
[PXUIField(DisplayName = Messages.MyAttribDisplayName)]
[PXDBScalar(typeof(Search<CSAnswers.value,
Where<CSAnswers.refNoteID, Equal<InventoryItem.noteID>,
And<CSAnswers.attributeID, Equal<MyAttribute>>>>))]
public string MyAttribute { get; set; }
public abstract class myAttribute : PX.Data.BQL.BqlString.Field<myAttribute> { }
#endregion
public class MyAttribute : PX.Data.BQL.BqlString.Constant<MyAttribute>
{
public MyAttribute() : base("MYATTRIB") { }
}
}
Notice the constant defined to access the AttributeID "MYATTRIB" which is attached to the Item Class of the InventoryItem record.
Now that the attribute is pulled into the DAC extension on InventoryItem, I can use it on any screen related to an InventoryItem with ease as long as InventoryItem (and the associated DAC extension) have been made available to that screen.
Without testing, I may be off on this but with regards to your attempt to join 2 tables in the PXDBScalar...
When you say Search2<CSAnswers.value..., you have indicated that you want to search the CSAnswers DAC and retrieve the value field. By subsequently trying to InnerJoin back to CSAnswers, I believe you effectively have said:
Select CSAnswers.Value From CSAnswers
Inner Join InventoryItem On SOLine.InventoryID = InventoryItem.InventoryID
Inner Join CSAnswers...
I'm not sure if the SOLine reference is valid here, but it may be if you are defining this directly in the SOLine DAC extension. However, you have tried to query CSAnswers Inner Join CSAnswers. Not sure if this will fix your PXDBScalar, but if you really want to use your method to attach this to SOLine, try:
Search2<CSAnswers.value,
InnerJoin<InventoryItem,
On<InventoryItem.inventoryID, Equal<SOLine.inventoryID>>>,
Where<InventoryItem.noteID, Equal<CSAnswers.refNoteID>,
And<CSAnswers.attributeID, Equal<QHOLDAttr>>>
>))]
(Notice that I swapped the order of the relations in On clauses.)

Related

SumCalc attribute not working when trying to summarize usr Field from PMTask to PMProject

First, created a custom field in the PM.PMTask DAC called usrNumberofPanel
Second, created a custom field in the PM.PMProject DAC usrTotalPanels.
Want each of the lines from task to update the total number of panels on the project, so modified the attribute for PM.PMTask.userNumberofPanel and added a PXFormula as shown below to add the SumCalc.
[PXDBDecimal]
[PXUIField(DisplayName="Number of Panels")]
[PXFormula(null, typeof(SumCalc<PX.Objects.CT.ContractExt.usrTotalPanels>))]
Made sure the attributes for the Total Panel and set as follows to make sure no one types into the field.
[PXDBDecimal]
[PXUIField(DisplayName="Total Panels", Enabled = false)]
Any thoughts would be appreciated.
It's a known issue that SumCalc doesn't work properly across DACs that are linked with PXParent relationships.
I can only recommend to use a RowSelected or FieldSelecting graph event handlers to compute the sum instead of a solution involving DAC attributes. You can add a comment explaining the limitation of DAC attributes in the event handler if you are seeking Acumatica ISV Certification for your solution.

Acumatica PXParent behaviour with table with Cascade delete

PXParent Attribute
Creates a reference to the parent record, establishing a parent-child relationship between two tables.
Does anyone know if PXParent is used and the underlying tables have a Cascade delete set if this causes any issues?
I would rather the database has referential integrity than rely on the framework application code to maintain this.
This is one of the goals for using the PXParentAttribute (that it will cascade delete the children and even if the child is a parent to more children it will delete those children too). I would use PXParent vs any configurations in SQL Server or MySql to accomplish the same goal.
If you want to setup a parent child relationship using PXParent and leave the children during a delete you would set the attribute LeaveChildren property to true (it is false by default).
Example:
[PXParent(typeof(Select<Schedule,
Where<Schedule.scheduleID, Equal<Current<WZScenario.scheduleID>>>>),
LeaveChildren = true)]
Adding to referential integrity you can also assign (on the dac fields) foreign references in case such references are attempted to be deleted. Inventory Warehouses for example. If you have a DAC that uses SiteID (warehouse) and want to make sure when someone tries to delete the warehouse (if referenced in a row of your DAC/table) it would prevent the deletion. You can use the PXForeignReferenceAttribute to do this. Here is an example on the IN Transaction dac (INTran):
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
[PXDefault(typeof(INRegister.siteID))]
[PX.Data.ReferentialIntegrity.Attributes.PXForeignReference(typeof(Field<siteID>.IsRelatedTo<INSite.siteID>))]
public virtual Int32? SiteID
{
get
{
return this._SiteID;
}
set
{
this._SiteID = value;
}
}
If you have custom tables, the same logic would apply if you point to your custom DACS/Fields using PXParent and/or PXForeignReference.
Either way the framework should cover you needs and would be my preference.

How to sort the OrderItems in the cart by its category on Broadleaf Commerce?

Broadleaf Commerce site
I need to sort the OrderItems in the cart by its product category. Currently, I am using the comparator to sort the OrderItems.
What is the best or right way to customize the default sort for Cart OrderItems?
I assume that you are currently doing something like this:
List<OrderItem> items = cart.getOrderItems();
Collections.sort(items, comparator);
and I also assume that by "default sort" you mean that you want to change the behavior when you call cart.getOrderItems().
Unfortunately, there is no way to change the behavior of the query that occurs when you call getOrderItems() as this is a JPA-generated query on the OrderItem collection within OrderImpl:
#OneToMany(mappedBy = "order", targetEntity = OrderItemImpl.class, cascade = {CascadeType.ALL})
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region="blOrderElements")
#AdminPresentationCollection(friendlyName="OrderImpl_Order_Items",
tab = TabName.General)
protected List<OrderItem> orderItems = new ArrayList<>();
Because of this you really only have 3 options:
Override the OrderImpl class and change the implementation of the getOrderItems() method. Unless you really needed to extend this (like to add another column to BLC_ORDER) then this isn't a great solution since it will add another table to the database that isn't actually used. See the Broadleaf extending entities tutorial. You can also technically avoid the multi-table inheritance by switching to single-table inheritance which is detailed at the bottom of that doc
Add your own query somewhere in a service method that manually queries for OrderItem for the order with a JPA query
Do what you are currently doing and sort the order items manually after retrieval with your comparator. You might consider adding a Thymeleaf utility method to have it available to you in your templates

Acumatica Requisition Total Cost Custom Calculation

I've added three custom decimal? field on Requisition master (RQ302000) and need to prorate the total value of those three fields to the each Requisition line using line base total cost/master total cost ratio and displayed the result as Additional Cost (also custom decimal? field) of each line.
This calculation should be triggered when those three new field are updated.
What I don't understand are:
1. What events should be modify related to this needs
2. If it is event on master field, how to get value from line extension field
3. If it is event on line field, how to get value from master extension field
Requisition Custom Screen
1. "What events should be modify related to this needs?"
In a scenario, like yours, one should use combination of RowInserted, RowUpdated and RowDeleted handlers:
The RowInserted event handler is used to implement the business logic for:
Inserting the detail data records in a one-to-many relationship.
Updating the master data record in a many-to-one relationship.
Inserting or updating the related data record in a one-to-one relationship.
The RowUpdated event handler is used to implement the business logic of:
Updating the master data record in a many-to-one relationship.
Inserting or updating the detail data records in a one-to-many relationship.
Updating the related data record in a one-to-one relationship.
The RowDeleted event handler is used to implement the business logic of:
Deleting the detail data records in a one-to-many relationship.
Updating the master data record in a many-to-one relationship.
Deleting or updating the related data record in a one-to-one relationship.
Also FieldUpdated handlers be considered for your scenario:
The FieldUpdated event handler is used to implement the business logic associated with changes to the value of the DAC field in the following cases:
Assigning the related fields of the data record containing the modified field their default values or updating them
Updating any of the following:
The detail data records in a one-to-many relationship
The related data records in a one-to-one relationship
The master data records in a many-to-one relationship
Refer to API Reference in Help -> Acumatica Framework -> API Reference -> Event Model and T200 developer class for additional information and examples on Acumatica Framework event model.
2. "If it is event on master field, how to get value from line extension field?"
In Acumatica custom fields are declared via DAC extensions. To access the DAC extension object, you can use the following methods:
The GetExtension() generic method available for each DAC instance:
ContactExt contactExt = curLead.GetExtension<ContactExt>();
The GetExtension(object) generic method declared within the non-generic PXCache class
ContactExt contactExt = Base.LeadCurrent.Cache.GetExtension<ContactExt>(curLead);
or
ContactExt contactExt = Base.Caches[typeof(Contact)].GetExtension<ContactExt>(curLead);
The GetExtension(object) static generic method of the PXCache generic class
ContactExt contactExt = PXCache<Contact>.GetExtension<ContactExt>(curLead);
To get value from line extension field, you should first select records from the Lines data view, then use one of the methods described above to access instance of DAC extension class, for instance:
foreach(RQRequisitionLine line in Base.Lines.Select())
{
RQRequisitionLineExt lineExt = line.GetExtension<RQRequisitionLineExt>();
}
3. "If it is event on line field, how to get value from master extension field"
That's an easy one: same 3 approaches described above, this time applied to Current property of the primary Document data view, for instance:
Base.Document.Current.GetExtension<RQRequisitionExt>();

Adding properties to an existing object retrieved using SubSonic

I think this is more of a polymorphism question but it applies to SubSonic table objects...
Here's the thing (and I love this one):
TblUser userObj = new TblUser(1);
Which fills userObj's properties with all of PK=1's goodies.
Now, I'd like to add more properties to the existing user object, for example, an ArrayList property of say, account numbers.
I've seen questions like this around - "add a property to an existing object...", but in this case, would it be most-recommended to create a user wrapper object, then have a TblUser property type, and my own other additional properties in this?
Ok, so it looks like once-again I have come up with a solution to this, but am still curious about the possibility of adding properties to existing objects.
All the generated SubSonic classes are partials so all you need to do to add extra properties/methods to them is to create your own partial class with the same name in the same namespace and the two will be merged at compile time. For example for your TblUser class:
public partial class TblUser
{
public List<AccountNumber> AccountNumbers
{
get
{
// Get and return the AccountNumbers
}
}
}

Resources