Acumatica - get the last displayed record - acumatica

Is there an eloquent way, more or less, to get the last displayed record in a grid in Acumatica? Let's say even if they do all the sorting and rearranging, is there a way for example when pressing a button on a grid to get the last record? Basically, I would like to copy that record as a new one.

Create a PXAction for your button.
Inside the PXAction iterate in your data view until the last record.
For example, if the name of your Data view Bound to your grid is YzLines, and object type in the grid line (DAC) is Yz, then it can be:
Yz lastLine;
foreach (Yz line in YzLines.Select())
lastLine = line;
To get to the last record you can also use .Last() or .LastOrDefault().
If you need the last record according to client sorting, you should implement a data view delegate, it looks like this:
protected virtual IEnumerable yzLines()
{
PXSelectBase<Yz> cmd =
new PXSelectJoinGroupBy<Yz, ...>(this);
int startRow = PXView.StartRow; //Get starting row of the current page
int totalRows = 0;
foreach (PXResult<Yz> res in
cmd.View.Select(null, null,
PXView.Searches,
ARDocumentList.View.GetExternalSorts(),//Get sorting fields
ARDocumentList.View.GetExternalDescendings(),//Get sorting direction
ARDocumentList.View.GetExternalFilters(),//Get filters
ref startRow,
PXView.MaximumRows, //Get count of records in the page
ref totalRows))
{
//processing of records
}
PXView.StartRow = 0;//Reset starting row
}

Related

Grab LineNbr of Newly Inserted Line

I'm trying to get the line number of a newly inserted INTran row.
Here is the code I'm using:
INRegister issue = new INRegister();
//Code to populate INRegister...
INIssueEntry graph = PXGraph.CreateInstance<INIssueEntry>();
graph.issue.Insert(issue);
graph.Actions.PressSave();
//PXSelect to get new RefNbr for INRegister object
issue = PXSelect<INRegister, Where<INRegister.refNbr, Equal<Current<INRegister.refNbr>>,
And<INRegister.docType, Equal<Current<INRegister.docType>>>>>.Select(graph);
graph.issue.Current = issue;
INTran issueRow = new INTran();
//Code to populate issueRow...
graph.transactions.Insert(issueRow);
graph.transactions.Current = issueRow;
graph.Actions.PressSave();
//Trying to get transaction line number
issueRow = PXSelect<INTran, Where<INTran.refNbr, Equal<Current<INTran.refNbr>>,
And<INTran.docType, Equal<Current<INTran.docType>>,
And<INTran.lineNbr, Equal<Current<INTran.lineNbr>>>>>>.Select(graph);
//At this point, issueRow is now null because LineNbr was null above
row.TranRefNbr = issueRow.RefNbr;
row.TranLineNbr = issueRow.LineNbr;
row.Released = true;
ItemReqs.Update(row);
//... ending code...
I've examined trying to save before setting as current (setting as current item for convenience for when I write the PXSelect to get the LineNbr), but I've found that the LineNbr, even on the graph, stays null during the entire excution. I've looked in the database as the graph is saving the new line and it does contain the line number. I'm not sure why my PXSelect isn't grabbing the line number.
When you insert your transaction row the return should have the linenbr
var issueRow = graph.transactions.Insert(issueRow);
I am guessing your issue is that you are setting current after using the issueRow that was NOT returned which would have a null linenbr.
Also you really do not need to PressSave until the end (or ready to generate the batch) as the linenbr will still get set through the LineNbr attribute. Referring to a save after inserting the INRegister record.
I think current will also be set after you insert so I don't see a need to set current which will remove your need for the PXSelects.
Example getting the LineNbr in the simplest of steps:
INIssueEntry graph = PXGraph.CreateInstance<INIssueEntry>();
graph.issue.Insert(new INRegister());
INTran issueRow = graph.transactions.Insert(new INTran());
//issueRow will now have a LineNbr value...
PXTrace.WriteInformation($"My Line Nbr is {issueRow.LineNbr}");
//RefNbr will receive its value when perform the Persist (Actions.PressSave)

Acumatica: Retrieving first record in a view for a display field - extraneous Order Bys

We have created a screen and graph that stores custom information about a given serial number for an inventory item (INItemLotSerial).
I'm looking to be able to display the current location of the serial number, based on the location of the most recent transaction. (Ideally I'd also like to be able display if the serial number is currently in inventory, but that's probably a different question.)
Here is my view on the graph:
public PXSelect<INTranSplit, Where<INTranSplit.lotSerialNbr, Equal<Optional<INItemLotSerial.lotSerialNbr>>,
And<INTranSplit.inventoryID, Equal<Optional<INItemLotSerial.inventoryID>>>>, OrderBy<Desc<INTranSplit.createdDateTime>>> InventoryLocation;
And my field on the page:
<px:PXSegmentMask Enabled="False" AllowEdit="False" runat="server" ID="CstPXSegmentMask5" DataField="InventoryLocation.LocationID" ></px:PXSegmentMask>
I'm expecting the field to grab the first record and ignore the rest.
However, when I look at the generated SQL in a SQL Trace, Acumatica appears to be adding its own Order By fields:
exec sp_executesql N'SELECT [INTranSplit].[DocType], [INTranSplit].[TranType], [INTranSplit].[RefNbr], [INTranSplit].[LineNbr], [INTranSplit].[POLineType], [INTranSplit].[TransferType], [INTranSplit].[ToSiteID], [INTranSplit].[ToLocationID], [INTranSplit].[SplitLineNbr], [INTranSplit].[TranDate], [INTranSplit].[InvtMult], [INTranSplit].[InventoryID], [INTranSplit].[SubItemID], [INTranSplit].[CostSubItemID], [INTranSplit].[CostSiteID], [INTranSplit].[SiteID], [INTranSplit].[LocationID], [INTranSplit].[LotSerialNbr], [INTranSplit].[ExpireDate], [INTranSplit].[Released], [INTranSplit].[UOM], [INTranSplit].[Qty], [INTranSplit].[BaseQty], [INTranSplit].[MaxTransferBaseQty], [INTranSplit].[OrigPlanType], [INTranSplit].[IsFixedInTransit], [INTranSplit].[PlanID], [INTranSplit].[TotalQty], [INTranSplit].[TotalCost], [INTranSplit].[AdditionalCost], ( CASE WHEN ( [INTranSplit].[TotalQty] = .0) THEN .0 ELSE ( [INTranSplit].[TotalCost] / [INTranSplit].[TotalQty]) END), [INTranSplit].[CreatedByID], [INTranSplit].[CreatedByScreenID], [INTranSplit].[CreatedDateTime], [INTranSplit].[LastModifiedByID], [INTranSplit].[LastModifiedByScreenID], [INTranSplit].[LastModifiedDateTime], [INTranSplit].[tstamp], [INTranSplit].[UsrQtyForQC], [INTranSplit].[UsrQtyCoded], [INTranSplit].[UsrQtyCompleted] FROM INTranSplit INTranSplit WHERE (INTranSplit.CompanyID = 2) AND [INTranSplit].[LotSerialNbr] = #P0 AND [INTranSplit].[InventoryID] = #P1
ORDER BY [INTranSplit].[DocType], [INTranSplit].[RefNbr], [INTranSplit].[LineNbr], [INTranSplit].[SplitLineNbr], [INTranSplit].[CreatedDateTime] DESC OPTION(OPTIMIZE FOR UNKNOWN) /* IN.21.00.00 */',N'#P0 nvarchar(100),#P1 int',#P0=N'EOSC52270005',#P1=16067
which are causing a different record to be returned first, rather than the most recent one.
How do I convince Acumatica to run the BQL I'm asking for and drop the extra order by fields? Or is there an entirely different approach to displaying the most recent transaction location that would be preferable?
What I would recommend doing in this particular situation if you need specialized filtering is using the IEnumerable sort
public IEnumerable inventoryLocation()
{
PXView select = new PXView(this, true, InventoryLocation.View.BqlSelect);
Int32 totalrow = 0;
Int32 startrow = PXView.StartRow;
List<object> result = select.Select(PXView.Currents, PXView.Parameters, PXView.Searches,
PXView.SortColumns, PXView.Descendings, PXView.Filters, ref startrow, PXView.MaximumRows, ref totalrow);
INTranSplit latest = null;
if (result.Count > 0)
{
//We need to perform a custom order in order to get to the latest record.
latest = result.First() as INTranSplit;
foreach (INTranSplit row in result)
{
if (latest.CreatedDateTime.Value < row.CreatedDateTime.Value)
{
latest = row;
}
}
}
return new List<object> { latest };
}
Where you would grab all the records, get the first record, and then look to find the latest record.
Cheers!

How can I get the number of rows of a detail grid?

I'm trying to get the number of rows in documents Details grid. I don't know how to call this inside of another instance.
for example: In this method I receive row by row, I want to know how can I get the total number of rows received.
public virtual void ARTran_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
var row = (ARTran)e.Row;
}
This is for screen Invoices and Meme (AR301000)
Do a .Select() of your data view and use the Count property.
var rowCount = Base.Transactions.Select().Count;
In your case, you want the data view used by the detail grid, which is Transactions. You can find more info on how the screen in builded by using the Inspect Element tool. You can read more about it on http://acumaticaopenuniversity.com/pdf/T300_Acumatica_Cust_Platform.pdf

Table does not refresh

I have a table that consists of rows of reports. A back bean method that is tied to a check box value change event is getting the row IDs and populating an array (rowsToBeRemoved) with this IDs. Another method gets this array and removes the object from reportlist class:
ObjectListDataProvider reportList = new ObjectListDataProvider();
List<RowKey> rowsToBeRemoved=new ArrayList();
Integer rowsToBeRemovedIndex = 0;
for(RowKey rowToBeRemoved:rowsToBeRemoved){
try {
System.out.println("rowToBeRemoved.toString()" + rowToBeRemoved.toString()); // outputs: rowToBeRemoved.toString()RowKey[0]
Report report = (Report) reportList.getObject(rowToBeRemoved);
System.out.println("report.getId()" + report.getId()); //outputs: report.getId()199|
Query resultQuery = queryGeneration(report.getId());
List<String> dropTableQueries = resultQuery.getResultList(); // generated the queries to drop r tables
for(int i=0; i<dropTableQueries.size(); i++){
String aDropTableQuery;
aDropTableQuery = dropTableQueries.get(i);
System.out.println("adroptableuery" + aDropTableQuery);// get single drop table query. outputs adroptableueryDROP TABLE r_199_0
entityManager.createNativeQuery(aDropTableQuery);
System.out.println("entitymanager dropTableQueries is invoked");//OK
reportList.removeObject(rowToBeRemoved);
System.out.println("removeObject");//OK
if (reportList.isRemoved(rowToBeRemoved)){
System.out.println("object removed");//OK
}
reportList.commitChanges();
System.out.println("commitchanges");//OK
}
reportJpaController.delete(report);
reportList.removeRow(rowToBeRemoved);
reportList.commitChanges();
analyzerResultService.drop(report.getId().longValue());
//rowsToBeRemoved.remove(rowsToBeRemovedIndex);
} catch (Exception e) {
error("Cannot delete report with row key " + rowToBeRemoved + e);
}
The code executes fine, removes the object from the reportlist but after that my table brings up a mixed reportlist. If I refresh the page it brings the reportlist in correct order, otherwise it doesn't. And if I try to delete a row in the mixed state it deletes the report as if it is in correct order, thus ending with deleting the wrong report. I hope I could explained.. What am I doing wrong?
I think this is similar to what you're looking for: How to refresh entire JSF page from the backing bean
This doesnt just refresh one part of the page though, but the full page.

How can I customize SharePoint list column aggregation (total) calculations?

I have a SharePoint list column of type 'Single line of text'. Out of the box SharePoint only provides the ability to display a 'Count' total for this column type. I would like to be able to perform a custom aggregation on the data (specifically to sum numeric data held as text to overcome this deficiency).
I have found examples for doing something similar for calculated columns using XSLT and Javascript but I believe that both of these approaches fail where the data is paginated (only aggregating the subset of the list content displayed on screen).
I want to retain the functionality of the ListViewWebPart (rendering, sorting, filtering, view definition, action menus etc.) but add this functionality. How can I do this?
The only things you can do with totals are:
Average; Count; Max; Min; Sum; Standard Deviation; Variance
Not sure how to calculate anything else.
I've not had a chance to fully test it but this is the best I could come up with:
Create a WebPart which contains two controls:
A ViewToolBar with the context of the list/view to be displayed
A Literal containing the rendered HTML of the view to be displayed
This will then render as the original list/view.
On rendering the WebPart, get the items from the view, specifying the RowLimit as the maximum value so that all items in are retrieved (not just the first page).
Iterate over the items, calculating the total in a suitable data type to retain precision.
Render the total as a hidden value in the HTML and overwrite the rendered Count total with Javascript such as by the method described here.
A rough sketch of the code:
public sealed class TextAggregatingWebPart : WebPart {
protected override void CreateChildControls() {
base.CreateChildControls();
var web = SPContext.Current.Web;
var list = web.Lists[SourceList];
var view = list.Views[ViewOfSourceList];
var toolbar = new ViewToolBar();
var context = SPContext.GetContext(
Context, view.ID, list.ID, SPContext.Current.Web);
toolbar.RenderContext = context;
Controls.Add(toolbar);
var viewHtml = new Literal {Text = view.RenderAsHtml()};
Controls.Add(viewHtml);
}
protected override void Render(HtmlTextWriter writer) {
EnsureChildControls();
base.Render(writer);
var web = SPContext.Current.Web;
var list = web.Lists[SourceList];
var view = list.Views[ViewOfSourceList];
var items = list.GetItems(new SPQuery(view) {RowLimit = uint.MaxValue});
foreach (SPItem item in items) {
// Calculate total
}
// Render total and Javascript to replace Count
}
}
Note that this doesn't solve the problem with the Modify View screen only showing Count as a total for text columns. Also there is a possibility that changes to the list between the initial rendering by the view and the retrieval of the items for aggregation could produce discrepancies between the total and the displayed items.

Resources