How to display product availability in Opportunity Products Grid footer? - acumatica

In Sales Order documents grid footer. It displays the product's availability.
How to do the same in Opportunity products grid ?
More so, how do you enforce it to be displayed at the footer
instead of a simple grid column ? Is there such attribute ?
Thanks for the replies.

If we compare to sales order, the sales order line gets its value from LSSOLine during Availabilty_FieldSelecting. The wire-up on the page is on the tab via StatusField="Availability". We can do something similar by adding an unbound extension field and then during a field selecting fill in the value. An alternative would be to implement an LSCROpportunityProducts class that is inherits LSSelect similar to LSSoLine (a better preferred solution). To keep this simple and focus on just getting the field to display text, I will use an extension field and a simple field selecting in the extension graph for opportunity.
(1) In a dac extension, create an unbound field (MyAvailability is the example field):
[PXTable(typeof(CROpportunityProducts.cROpportunityID), typeof(CROpportunityProducts.cROpportunityProductID), IsOptional = true)]
[Serializable]
public class CROpportunityProductsMyExtension : PXCacheExtension<CROpportunityProducts>
{
#region MyAvailability
public abstract class myAvailability : PX.Data.IBqlField
{
}
protected string _MyAvailability;
[PXString(IsUnicode = true)]
[PXUIField(DisplayName = "Product Availability", Enabled = false)]
public virtual string MyAvailability
{
get
{
return this._MyAvailability;
}
set
{
this._MyAvailability = value;
}
}
#endregion
}
(2) On the opportunity products tab, wire up the new field as the grid status value by setting property StatusField. The page needs modified to add this value and should look something like this when added (requires screen customization in your project -> actions edit ASPX and locate ProductsGrid to paste in your StatusField and value):
<px:PXGrid ID="ProductsGrid" SkinID="Details" runat="server" Width="100%"
Height="500px" DataSourceID="ds" ActionsPosition="Top" BorderWidth="0px"
SyncPosition="true" StatusField="MyAvailability">
(3) Now in a graph extension populate the field:
Edit: The use of Current<> does not always contain the correct currently highlighted row in the UI. Switched to Required<> based on the PXFieldSelectingEventArgs.Row and the results are correct for multiple rows in the products tab.
public class CROpportunityMaintMyExtension : PXGraphExtension<OpportunityMaint>
{
public virtual void CROpportunityProducts_MyAvailability_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
var row = (CROpportunityProducts) e.Row;
if (row == null)
{
e.ReturnValue = string.Empty;
return;
}
INLocationStatus locationStatus = PXSelectGroupBy<INLocationStatus,
Where<INLocationStatus.inventoryID, Equal<Required<CROpportunityProducts.inventoryID>>,
And2<Where<INLocationStatus.subItemID, Equal<Required<CROpportunityProducts.subItemID>>,
Or<Not<FeatureInstalled<PX.Objects.CS.FeaturesSet.subItem>>>>,
And<Where<INLocationStatus.siteID, Equal<Required<CROpportunityProducts.siteID>>,
Or<Required<CROpportunityProducts.siteID>, IsNull>>>>>,
Aggregate<Sum<INLocationStatus.qtyOnHand, Sum<INLocationStatus.qtyAvail>>>
>.Select(sender.Graph, row.InventoryID, row.SubItemID, row.SiteID, row.SiteID);
// Need to convert to transaction UOM... (this is always base units)
decimal? qtyOnHand = locationStatus?.QtyOnHand;
decimal? qtyAvail = locationStatus?.QtyAvail;
e.ReturnValue = $"Qty On hand = {qtyOnHand.GetValueOrDefault()} ; Qty Avail = {qtyAvail.GetValueOrDefault()}";
}
}
The results:

Related

Adding Custom Entity Type in CRM Activity for selecting in Relative Entity

I have implemented CRM activity on the Custom page where the Key Field is SOOrder Type, SOOrder Nbr & Job Code which are stored in custom DAC. I have tried to add the Entity Type listed on Related Entity and I am not able to figure out how to do it. Pls let me know where to add or override the method to Implement the functionality
The following cod used to implement the CRM Activity
public sealed class SOOrderJobActivities : CRActivityList<PSSOOrderJob>
{
public SOOrderJobActivities(PXGraph graph)
: base(graph) { }
protected override RecipientList GetRecipientsFromContext(NotificationUtility utility, string type, object row, NotificationSource source)
{
var recipients = new RecipientList();
var order = _Graph.Caches[typeof(PSSOOrderJob)].Current as PSSOOrderJob;
if (order == null || source == null)
return null;
SOOrder ord = SOOrder.PK.Find(_Graph, order.OrderType, order.OrderNbr);
var contact = SOOrder.FK.Contact.FindParent(_Graph, ord);
if (contact == null || contact.EMail == null)
return null;
recipients.Add(new NotificationRecipient()
{
Active = true,
AddTo = RecipientAddToAttribute.To,
Email = contact.EMail
});
source.RecipientsBehavior = RecipientsBehaviorAttribute.Override;
return recipients;
}
}
Update
After going through the Acumatica code, I have done the following changes
#region Noteid
[PXNote(ShowInReferenceSelector =true,Selector =typeof(Search2<PSSOOrderJob.jobCode,
InnerJoin<SOOrder,On<PSSOOrderJob.orderType,Equal<SOOrder.orderType>,And<PSSOOrderJob.orderNbr, Equal<SOOrder.orderNbr>>>>,
Where<SOOrder.orderType,Equal<Current<PSSOOrderJob.orderType>>,And<SOOrder.orderNbr, Equal<Current<PSSOOrderJob.orderNbr>>>>>))]
public virtual Guid? Noteid { get; set; }
public abstract class noteid : PX.Data.BQL.BqlGuid.Field<noteid> { }
#endregion
The entity comes into selection, But I am not able to select the relative entity document and the value is not getting updated in the related entity field.
The above screenshot the select is missing and not able to select the document
The following steps I have done to add relative Entity for any Activity using custom DAC
1. Added ShowInReferenceSelector = true in PXNoteID field.
2. Added Selector in PXNoteID field
3. Decorated [PX.Data.EP.PXFieldDescription] attribute for Key fields
#region NoteID
[PXNote(ShowInReferenceSelector = true, Selector = typeof(Search2<PSSOOrderJob.jobCode,
InnerJoin<SOOrder, On<PSSOOrderJob.orderType, Equal<SOOrder.orderType>, And<PSSOOrderJob.orderNbr, Equal<SOOrder.orderNbr>>>>,
Where<SOOrder.orderType, Equal<Current<PSSOOrderJob.orderType>>, And<SOOrder.orderNbr, Equal<Current<PSSOOrderJob.orderNbr>>,And<PSSOOrderJob.jobType,Equal<Current<PSSOOrderJob.jobType>>>>>>), DescriptionField = typeof(PSSOOrderJob.jobCode))]
//[PXNote(ShowInReferenceSelector = true)]
public virtual Guid? NoteID { get; set; }
public abstract class noteID : PX.Data.BQL.BqlGuid.Field<noteID> { }
#endregion
#region JobCode
[PXDBString(15, IsKey = true, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Job Code")]
[PXDefault()]
[PXSelector(typeof(Search<PSSOOrderJob.jobCode, Where<PSSOOrderJob.orderType, Equal<Current<PSSOOrderJob.orderType>>, And<PSSOOrderJob.orderNbr, Equal<Current<PSSOOrderJob.orderNbr>>,And<PSSOOrderJob.jobType, Equal<Current<PSSOOrderJob.jobType>>>>>>), typeof(PSSOOrderJob.jobCode), ValidateValue = false)]
[PSSOOrderJobNbr.Numbering()]
[PX.Data.EP.PXFieldDescription]
public virtual string JobCode { get; set; }
public abstract class jobCode : PX.Data.BQL.BqlString.Field<jobCode> { }
#endregion
This automatically fills the related entity field with Jobcode.
There still one issue I am facing is not able to access the selector due to Entity field width is more than the popup window and I do not know how to fix it.
This answer is to address the popup size only.
First, give browser zoom a try 'control' + 'minus' key. It might work as a quick workaround.
Otherwise, use the browser debugger feature. Open it with F12 key. Then use the browser debugger inspect element feature (1). Click on the smart panel (2). Go up a bit in html control hierarchy until you reach and select the smart panel root which is a table element (3). Change the width of the smart panel popup using the debugger CSS properties editor (4).
If selector control size increases automatically with window size; change the selector control width instead of popup width using browser debugger CSS property editor.

Acumatica Customize Process Shipments Add branch field from PO Receipts

I'm trying to figure out if it's possible to add fields to the grid on the Process Shipments screen for PO Receipts when the "Prepare Drop-Ship Invoice" action is selected. Specifically, I want to add the POReceipts.BranchID field.
I've studied the source code, and I see where different select commands are run based on the selected action, but I don't understand what drives the grid to show the different set of columns.
I looked in the screen designer in the customization editor, and I don't see a different view or anything, hopefully I'm just missing something simple.
Thanks
Scott
This is how I solved your mystery. First create a user field for SOShipment.
#region UsrReceiptBranchID
[PXInt]
[PXUIField(DisplayName="Receipt Branch ID")]
[GL.Branch()]
public virtual int? UsrReceiptBranchID { get; set; }
public abstract class usrReceiptBranchID : PX.Data.BQL.BqlInt.Field<usrReceiptBranchID> { }
#endregion
Next is the key part. I extended the data view delegate, and added an extra query to pull the POReceipt branch, when the Filter is Drop-Ship.
public virtual IEnumerable orders()
{
List<SOShipment> records = Base.orders().RowCast<SOShipment>().ToList();
if (Base.Filter.Current.Action.Contains("Drop"))
{
foreach (SOShipment sOShipment in records)
{
POReceipt receipt = PXSelect<POReceipt,
Where<POReceipt.receiptNbr,
Equal<Required<POReceipt.receiptNbr>>>>.Select(Base, sOShipment.ShipmentNbr);
if (receipt != null)
{
SOShipmentExt shipExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(sOShipment);
shipExt.UsrReceiptBranchID = receipt.BranchID;
}
}
return records;
}
else
{
return records;
}
}

Global Generic Field Update Event

I have 50+ custom paired fields "Inches" and "Centimeters", each enabled and editable. I need to update "Inches" if the user changed the value of "Centimeters" and visa verse. I was able to do this using SetValuePending on one of the paired fields and SetValueExt on the other during the Field Updated Event. My question, is there a way to do this on a higher level without having to do a Field_Updated event for all the 100+ fields. I know that Formulas would create a circular reference so cannot be used. Thanks
Well, you can use one method to handle FieldUpdated events for all fields you need using graph FieldUpdated.AddHandler method in constructor. To get a field name just extend a standard Acumatica FieldUpdated delegate with one additional parameter (name for example) and put it during the FieldUpdated.AddHandler call.
Here is an example with "Invoices and Memos" screen and ARInvoiceEntry graph.
public ARInvoiceEntry()
{
FieldUpdated.AddHandler(typeof(ARTran), typeof(ARTran.inches).Name, (sender, e) => CommonFieldUpdated(sender, e, typeof(ARTran.inches).Name));
FieldUpdated.AddHandler(typeof(ARTran), typeof(ARTran.centimeters).Name, (sender, e) => CommonFieldUpdated(sender, e, typeof(ARTran.centimeters).Name));
...
}
protected virtual void CommonFieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e, string name)
{
// do something
}
Moreover, you can add handlers dynamically using fields collection for example
foreach(var field in Caches[typeof(ARTran)].Fields)
{
// add handler depends on field name
}
You can do it by PXFormula + ExternalValue BQL expression (the same as PXRowUpdatedEventArgs.ExternalCall for example), which will prevent circular reference between pair fields. The idea is to calculate field only when a related field has been changed by the user from UI (ExternalCall = true) and skip calculation when related field updated by the formula (ExternalCall = false).
public class centimetersInInches : PX.Data.BQL.BqlDecimal.Constant<centimetersInInches>
{
public centimetersInInches() : base(2.54m) { }
}
[PXDecimal]
[PXUIField(DisplayName = "Inches")]
[PXUnboundDefault(TypeCode.Decimal, "0.0")]
[PXFormula(typeof(ExternalValue<Div<centimeters, centimetersInInches>>))]
public virtual decimal? Inches { get; set; }
public abstract class inches : PX.Data.BQL.BqlDecimal.Field<inches> { }
[PXDecimal]
[PXUIField(DisplayName = "Centimeters")]
[PXUnboundDefault(TypeCode.Decimal, "0.0")]
[PXFormula(typeof(ExternalValue<Mult<inches, centimetersInInches>>))]
public virtual decimal? Centimeters { get; set; }
public abstract class centimeters : PX.Data.BQL.BqlDecimal.Field<centimeters> { }
And aspx
<px:PXGridColumn DataField="Inches" CommitChanges="True" />
<px:PXGridColumn DataField="Centimeters" CommitChanges="True" />
You can use the RowUpdated event and compare the old row with the new row to detect which field changed. I agree that keeping all logic in a single method is preferable.
public virtual void DAC_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
DAC row = e.Row as DAC;
DAC oldRow = e.OldRow as DAC;
if (row == null || oldRow == null) return;
// Compare old row with new row to determine which field changed
if (row.Inches != oldRow.Inches)
{
// Inches field changed, update CM value
row.CM = row.Inches * INCHES_TO_CM_CONSTANT;
}
// Add more conditions for the other fields
[..]
}
I know that Formulas would create a circular reference so cannot be
used.
Yes I wouldn't recommend it either, you could use the DAC property Setter though.
public string _Inches
public virtual string Inches
{
get
{
return this._Inches;
}
set
{
this._Inches = value;
this.CM = value * INCHES_TO_CM_CONSTANT;
}
}
For all solution (except Formula/DAC attributes) I think a condition to stop recursion should be possible if it's absolutely necessary:
if (this.CM != value * INCHES_TO_CM_CONSTANT)
this.CM = value * INCHES_TO_CM_CONSTANT;
Ideally proper use/avoidance of SetValueExt to control when events are raised (Ext method raises events) would be enough to stop infinite loops.

Acumatica - Display row numbers

Is there a way to display row numbers in Acumatica grid? You could display LineID; however, when you sort your columns the LineID's will of course become scrambled.
Thanks,
There are two possible cases:
1. you need to add row numbers to existing screen
2. you need to add row numbers to your screen
For case 1 consider that you want to add row numbers to page po301000, tab Document details. The grid in tab Document details is binded to DataMember Transactions as you can see on screenshot fragment:
Second part of puzzle is graph, which you need to extend. As you can see from screenshot you need to extend PX.Objects.PO.POOrderEntry:
Third part that you'll need is extending of POLine. You can achieve it with following sample of code:
public class POLineExt : PXCacheExtension<POLine>
{
public abstract class rowNumber : IBqlField
{
}
[PXInt]
public int RowNumber { get; set; }
}
and extend graph like this:
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
public PXSelect<POLine, Where<POLine.orderType, Equal<Current<POOrder.orderType>>, And<POLine.orderNbr, Equal<Optional<POOrder.orderNbr>>>>, OrderBy<Asc<POLine.orderType, Asc<POLine.orderNbr, Asc<POLine.lineNbr>>>>> Transactions;
protected IEnumerable transactions()
{
var result = Base.Transactions.Select();
int rowNumber = 1;
foreach (PXResult<POLine> lineBoxed in result)
{
var line = lineBoxed.GetItem<POLine>();
var lineExt = line.GetExtension<POLineExt>();
lineExt.RowNumber = rowNumber;
rowNumber++;
}
return result;
}
And the final part, you'll need to add your customized column on your page:
<px:PXGridLevel DataMember="Transactions">
<Columns>
<px:PXGridColumn DataField="RowNumber" Width="80px"></px:PXGridColumn>
After all of this I've seen the following on mine page purchase orders:
Second case is very similar to first described here, just with difference that you don't need to extend anything.

Acumatica Scan barcode

In Acumatica project , on Receipt screen have a lookup button "Add Item". When Inventory lookup show dialog ( it's FromDetail template ), i will scan barcode from my bill. But, i must scan second times it to working.
How can i to filter grid details and set checked a row filtered according barcode ? And how can to know row count filtered? . Please help me.
The below figure illustrates.
snip code
Screen Receipt
You will be in a much better position with FieldVerifying handler implemented as follows for the INSiteStatusFilter.BarCode field to split scanned barcode and assign the new values both to BarCode field (though the PXFieldVerifyingEventArgs.New property) and custom unbound FilterQtySelected field. Then inside the FieldUpdated handler you will select all records from the detail grid, that satisfy filter condition and set Qty. Selected according to your unbound FilterQtySelected field:
public class INReceiptEntryExt : PXGraphExtension<INReceiptEntry>
{
public class INSiteStatusFilterExt : PXCacheExtension<INSiteStatusFilter>
{
#region QtySelected
public abstract class filterQtySelected : PX.Data.IBqlField
{
}
[PXQuantity]
public virtual decimal? FilterQtySelected { get; set; }
#endregion
}
public void INSiteStatusFilter_BarCode_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
var barCode = (string)e.NewValue;
string[] codes = barCode.Split(';');
decimal qtySelected;
if (codes.Length == 3 && decimal.TryParse(codes[2], out qtySelected))
{
e.NewValue = codes[1];
sender.GetExtension<INSiteStatusFilterExt>(e.Row).FilterQtySelected = qtySelected;
return;
}
sender.GetExtension<INSiteStatusFilterExt>(e.Row).FilterQtySelected = null;
}
public void INSiteStatusFilter_BarCode_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
INSiteStatusFilter filter = e.Row as INSiteStatusFilter;
var filterExt = filter.GetExtension<INSiteStatusFilterExt>();
if (string.IsNullOrEmpty(filter.BarCode) || !filterExt.FilterQtySelected.HasValue) return;
foreach(INSiteStatusSelected record in Base.sitestatus.Select())
{
record.Selected = true;
record.QtySelected = sender.GetExtension<INSiteStatusFilterExt>(e.Row).FilterQtySelected;
Base.sitestatus.Update(record);
}
}
}
You will need to add your custom 2D format support on the existing barcode infrastructure screens via a customization project.

Resources