X++ passing current selected records in a form for your report - report

I am trying to make this question sound as clear as possible.
Basically, I have created a report, and it now exists as a menuitem button so that the report can run off the form.
What I would like to do, is be able to multi-select records, then when I click on my button to run my report, the current selected records are passed into the dialog form (filter screen) that appears.
I have tried to do this using the same methods as with the SaleLinesEdit form, but had no success.
If anyone could point me in the right direction I would greatly appreciate it.

Take a look at Axaptapedia passing values between forms. This should help you. You will probably have to modify your report to use a form for the dialog rather than using the base dialog methods of the report Here is a good place to start with that!

Just wanted to add this
You can use the MuliSelectionHelper class to do this very simply:
MultiSelectionHelper selection = MultiSelectionHelper::createFromCaller(_args.caller());
MyTable myTable = selection.getFirst();
while (myTable)
{
//do something
myTable = selection.getNext();
}

Here is the resolution I used for this issue;
Two methods on the report so that when fields are multi-selected on forms, the values are passed to the filter dialog;
private void setQueryRange(Common _common)
{
FormDataSource fds;
LogisticsControlTable logisticsTable;
QueryBuildDataSource qbdsLogisticsTable;
QueryBuildRange qbrLogisticsId;
str rangeLogId;
set logIdSet = new Set(Types::String);
str addRange(str _range, str _value, QueryBuildDataSource _qbds, int _fieldNum, Set _set = null)
{
str ret = _range;
QueryBuildRange qbr;
;
if(_set && _set.in(_Value))
{
return ret;
}
if(strLen(ret) + strLen(_value) + 1 > 255)
{
qbr = _qbds.addRange(_fieldNum);
qbr.value(ret);
ret = '';
}
if(ret)
{
ret += ',';
}
if(_set)
{
_set.add(_value);
}
ret += _value;
return ret;
}
;
switch(_common.TableId)
{
case tableNum(LogisticsControlTable):
qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
qbrLogisticsId = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, LogisticsId));
fds = _common.dataSource();
for(logisticsTable = fds.getFirst(true) ? fds.getFirst(true) : _common;
logisticsTable;
logisticsTable = fds.getNext())
{
rangeLogId = addrange(rangeLogId, logisticsTable.LogisticsId, qbdsLogisticsTable, fieldNum(LogisticsControlTable, LogisticsId),logIdSet);
}
qbrLogisticsId.value(rangeLogId);
break;
}
}
// This set the query and gets the values passing them to the range i.e. "SO0001, SO0002, SO000£...
The second methods is as follows;
private void setQueryEnableDS()
{
Query queryLocal = element.query();
;
}
Also on the init method this is required;
public void init()
{
;
super();
if(element.args() && element.args().dataset())
{
this.setQueryRange(element.args().record());
}
}
Hope this helps in the future for anyone else who has the issue I had.

Related

I have a "Upload Record" PXAction to load records to grid and release these records

I have a custom PXbutton called UploadRecords, when I click this button I should populate the grid with records and release the records.
Release Action is pressed in the UploadRecords action delegate. The problem I get with this code is, the code here function properly for less records by release action but when passes thousands of records to release, it takes huge time(> 30 min.) and show the error like Execution timeout.
suggest me to avoid more execution time and release the records fastly.
namespace PX.Objects.AR
{
public class ARPriceWorksheetMaint_Extension : PXGraphExtension<ARPriceWorksheetMaint>
{
//public class string_R112 : Constant<string>
//{
// public string_R112()
// : base("4E5CCAFC-0957-4DB3-A4DA-2A24EA700047")
// {
// }
//}
public class string_R112 : Constant<string>
{
public string_R112()
: base("EA")
{
}
}
public PXSelectJoin<InventoryItem, InnerJoin<CSAnswers, On<InventoryItem.noteID, Equal<CSAnswers.refNoteID>>,
LeftJoin<INItemCost, On<InventoryItem.inventoryID, Equal<INItemCost.inventoryID>>>>,
Where<InventoryItem.salesUnit, Equal<string_R112>>> records;
public PXAction<ARPriceWorksheet> uploadRecord;
[PXUIField(DisplayName = "Upload Records", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXButton]
public IEnumerable UploadRecord(PXAdapter adapter)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in records.Select())
{
InventoryItem invItem = (InventoryItem)res;
INItemCost itemCost = (INItemCost)res;
CSAnswers csAnswer = (CSAnswers)res;
ARPriceWorksheetDetail gridDetail = new ARPriceWorksheetDetail();
gridDetail.PriceType = PriceTypeList.CustomerPriceClass;
gridDetail.PriceCode = csAnswer.AttributeID;
gridDetail.AlternateID = "";
gridDetail.InventoryID = invItem.InventoryID;
gridDetail.Description = invItem.Descr;
gridDetail.UOM = "EA";
gridDetail.SiteID = 6;
InventoryItemExt invExt = PXCache<InventoryItem>.GetExtension<InventoryItemExt>(invItem);
decimal y;
if (decimal.TryParse(csAnswer.Value, out y))
{
y = decimal.Parse(csAnswer.Value);
}
else
y = decimal.Parse(csAnswer.Value.Replace(" ", ""));
gridDetail.CurrentPrice = y; //(invExt.UsrMarketCost ?? 0m) * (Math.Round(y / 100, 2));
gridDetail.PendingPrice = y; // (invExt.UsrMarketCost ?? 0m)* (Math.Round( y/ 100, 2));
gridDetail.TaxID = null;
Base.Details.Update(gridDetail);
}
ts.Complete();
}
Base.Document.Current.Hold = false;
using (PXTransactionScope ts = new PXTransactionScope())
{
Base.Release.Press();
ts.Complete();
}
List<ARPriceWorksheet> lst = new List<ARPriceWorksheet>
{
Base.Document.Current
};
return lst;
}
protected void ARPriceWorksheet_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARPriceWorksheet)e.Row;
uploadRecord.SetEnabled(row.Status != SPWorksheetStatus.Released);
}
}
}
First, Do you need them all to be in a single transaction scope? This would revert all changes if there is an exception in any. If you need to have them all committed without any errors rather than each record, you would have to perform the updates this way.
I would suggest though moving your process to a custom processing screen. This way you can load the records, select one or many, and use the processing engine built into Acumatica to handle the process, rather than a single button click action. Here is an example: https://www.acumatica.com/blog/creating-custom-processing-screens-in-acumatica/
Based on the feedback that it must be all in a single transaction scope and thousands of records, I can only see two optimizations that may assist. First is increasing the Timeout as explained in this blog post. https://acumaticaclouderp.blogspot.com/2017/12/acumatica-snapshots-uploading-and.html
Next I would load all records into memory first and then loop through them with a ToList(). That might save you time as it should pull all records at once rather than once for each record.
going from
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in records.Select())
to
var recordList = records.Select().ToList();
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in recordList)

AX2009 update form field value

I want to auto-update the Format field of the NumberSequenceTable form. The purpose is to help the user while manually entering the data.
For example this field should preferably be updated with concatenated values of the fields Number sequence code and Largest.
After thinking for a while I decided to change the underlying table NumberSequenceTable. I changed its "modifiedField" method by adding another case to the already existing switch statement. I think this is the accepted best practice in such cases:
public void modifiedField(fieldId _fieldId)
{
#Define.DefaultCleanInterval(24)
#Define.DefaultCacheSize(10)
str sequenceFormat;
super(_fieldId);
switch (_fieldId)
{
....
//BEGINING
case fieldnum(NumberSequenceTable, NumberSequence):
if (this.NumberSequence && this.Highest && !this.Format)
{
sequenceFormat = System.Text.RegularExpressions.Regex::Replace(int2str(this.Highest), "[0-9]", "#");
this.Format = strFmt("%1_%2",this.NumberSequence, sequenceFormat);
}
break;
//END
default :
break;
}
}
My previous solution involved overriding the "enter" method on the form field. However this would not be in line with best practices. For information purposes I am pasting here the initial solution.
public void enter()
{
str sequenceNode;
str sequenceValue;
str maxValue;
str formatValue;
;
super();
//Get required fields value
sequenceNode = numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,NumberSequence)).getValue();
maxValue = numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,Highest)).getValue();
formatValue = numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,Format)).getValue();
if(sequenceNode && maxValue && !formatValue) {
//Match regex pattern
maxValue = System.Text.RegularExpressions.Regex::Replace(maxValue, "[0-9]", "#");
sequenceValue = strFmt("%1_%2",sequenceNode,maxValue);
//Change field value
numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,Format)).setValue(sequenceValue);
this.update();
}
}

How can i get columns added on column?

heres my code below...
TableColumn tc = new TableColumn();
TableColumn[] tc2 = new TableColumn[10];
for(int i=0; i<5, i++){
tc.getColumns().add(tc2[i]);
}
and i try to override commit method for editing cells.
public void commit(Object val) {
// Get the table
TableView<MainTable> t = this.getTableView();
// Get the selected row/column
MainTable selectedRow = t.getItems().get(this.getTableRow().getIndex());
TableColumn<MainTable, ?> selectedColumn = t.getColumns().get(t.getColumns().indexOf(this.getTableColumn()));
// Get current property name
String propertyName = ((PropertyValueFactory) selectedColumn.getCellValueFactory()).getProperty();
// Create a method name conforming to java standards ( setProperty )
propertyName = ("" + propertyName.charAt(0)).toUpperCase() + propertyName.substring(1);
// Try to run the update
try {
// Type specific checks - could be done inside each setProperty() method
if(val instanceof Double) {
Method method = selectedRow.getClass().getMethod("set" + propertyName, double.class);
method.invoke(selectedRow, (double) val);
}
if(val instanceof String) {
Method method = selectedRow.getClass().getMethod("set" + propertyName, String.class);
method.invoke(selectedRow, (String) val);
}
if(val instanceof Integer) {
Method method = selectedRow.getClass().getMethod("set" + propertyName, int.class);
method.invoke(selectedRow, (int) val);
}
} catch (Exception e) {
e.printStackTrace();
}
// CommitEdit for good luck
commitEdit((String) val);
}
and i got ArrayIndexOutofBoundsException on console view.
so my question is
how can i select getcolumns added other column???
TableColumn<MainTable, ?> selectedColumn = t.getColumns().get(t.getColumns().indexOf(this.getTableColumn()));
i think this code has to be changed...
anyone got ideas??
Nested columns are not part of the TableView.columns list.
If you need the corresponding TableView column, just go up through the hierarchy until you reach a column without a parentColumn:
TableColumn<MainTable, ?> selectedColumn = this.getTableColumn();
TableColumn<MainTable, ?> c = selectedColumn;
while ((c = selectedColumn.getParentColumn()) != null) {
selectedColumn = c;
}
If you just need the column itself, simply use this.getTableColumn(), instead of finding the index of the column in the columns list and then accessing that index in the same list. (I guess the latter is what you need.)
Furthermore, if PropertyValueFactory returns properties of the item class, you could use this property to set the value instead of using reflection:
ObservableValue obs = selectedColumn.getCellObservableValue(this.getIndex());
if (obs instanceof WritableValue) {
((WritableValue) obs).setValue(val);
} else {
// reflecitive approach
}
Furthermore you shouldn't add null as a nested column, but you're doing it here:
TableColumn[] tc2 = new TableColumn[10];
for(int i=0; i<5, i++){
tc.getColumns().add(tc2[i]);
}

write condition statements in a sharepoint SPListItemCollection before binding the method to asp.net repeater

My situation is like this:
I have a function that returns a value as SPListItemCollection and I bind this function to a repeater.
My problem is how can I do some conditional formatting before the return value?
SPListItemCollection GetListItems()
{
SPWeb curWeb = SPContext.Current.Site.RootWeb;
string siteUrl = SPContext.Current.Web.Url;
SPListItemCollection curItems = GetDep(ListName, department);
// write condition here so that it checks if the item count is higher or
//lower than a specified number.
return curItems;
}
thank you for your help.
Im not 100% sure what you want to do before the return. If you only want to check if the resultItem.Count is bigger then 100 for example, you can do this:
SPListItemCollection GetListItems()
{
SPWeb curWeb = SPContext.Current.Site.RootWeb;
string siteUrl = SPContext.Current.Web.Url;
SPListItemCollection curItems = GetDep(ListName, department);
if (curItems.Count > 100)
{
// change the items or do whatever you want. after that, return:
foreach(SPListItem item in curItems)
{
//format/change
}
return curItems;
}
// return, without any changes
return curItems;
}
Try smth like
SPListItemCollection GetListItems()
{
SPWeb curWeb = SPContext.Current.Site.RootWeb;
string siteUrl = SPContext.Current.Web.Url;
SPListItemCollection curItems = GetDep(ListName, department);
var itemsForDepartment = curItems.GetDataTable().Rows.Where(r => r["Department"] == department); // you can try to do this is caml too
if(itemsForDepartment.Count > itemCount) {
// insert the "show me more" link
}
var itemsForDepartment = itemsForDepartment.Take(itemCount);
// bind itemsForDepartment to a Repeater
return curItems;
}
I have not compiled this code so you will have to correct some syntax mistakes ;)

flex select value from Combo

My goal is to create a generic function that selects a value in a combobox accoring to a value.
(My comoBox holds arrayCollection as dataProvider.)
The difficulty is infact to get a propertyname in runtime mode
public function selectComboByLabel(combo:ComboBox , propetryName:String, value:String):void {
var dp:ArrayCollection = combo.dataProvider as ArrayCollection;
for (var i:int=0;i<dp.length;i++) {
if (dp.getItemAt(i).propertyName==value) {
combo.selectedIndex = i;
return;
}
}
}
the line if (dp.getItemAt(i).propertyName==value)
is of course incorrect.
It should be arther something like: dp.getItemAt(i).getPropertyByName(propertyName)
Any clue on how to that ?
Don't use Object Property notation. Do this:
dp.getItemAt(i)[propertyName]
In addition to what Flextras said, you could also redo your for loop to make it easier to read:
for each(var item:Object in dp) {
if(item[propertyName] == value) {
combo.selectedItem = item;
return;
}
}

Resources