Assign value to custom field on dialog panel (Allocation) - acumatica

I had added a custome field "UsrWgtIndex" on Allocation panel in Purchase Receipts. And its value is the sum of the other custom field "UsrWgtPerUnit".
But strange thing happened. The value of UsrWgtIndex keeps the same when I open diffrent allocation panel. It is always the value of first row of transations.
My code is below, and I'm really confused about this. In logic, the code will sum each row of transations, and assign each row of the "UsrWgtIndex". But it's always the value of the first row.
Anyone can help on this? Thanks a lot!
namespace PX.Objects.PO {
public class POReceiptEntry_Extension: PXGraphExtension < POReceiptEntry > {
#region Event Handlers
decimal totalCgt = 0 M,
tempTotal = 0 M;
protected void POReceiptLine_RowSelected(PXCache cache, PXRowSelectedEventArgs e) {
POReceiptLine poRLine = (POReceiptLine) e.Row;
if (poRLine != null) {
totalCgt = 0 M;
foreach(POReceiptLineSplit line in Base.splits.Select()) {
POReceiptLineSplitExt poReceiptLineSplitExt = line.GetExtension < POReceiptLineSplitExt > ();
var recentQty = poReceiptLineSplitExt.UsrWgtPerUnit;
var temp = Convert.ToDecimal(recentQty);
totalCgt = totalCgt + temp;
};
var cgt = Convert.ToDecimal(totalCgt);
if (totalCgt != null) {
cache.SetValue < POReceiptLineExt.usrTotalWgt > (poRLine, cgt);
//This line is setting the value of usrWgtIndex
cache.SetValue < POReceiptLineExt.usrWgtIndex > (poRLine, cgt);
};
}
}
}
}
More detail update:
The customer field “usrWgtIndex” belongs to the data class “POReceiptLine”. But I make its control on the Allocations Panel.
I had made a test: set value to the field “UnassignedQty” on allocations panel with the same value I give to “usrWgtIndex”. It works correctly. Or I changed the other field in POReceiptLine data class with same value in the same time, It works fine again.
3.It seems that if I change a custom field on allocations panel, this strange thing would happen…
More Detail Update2:
I didn't add the "UsrWgtIndex" on the DAC LotSerOptions. I added it on the POReceiptLine. Because when I add the custom field on LotSerOptions, I can't assign its value with setValueEXT methord, it seems there is no DAC named "LotSerOptionsExt".
So I just put "UsrWgtIndex" on DAC POReceiptLine, and assign its value with
cache.SetValue<POReceiptLineExt.usrWgtIndex>(poRLine, cgt);

The 'splits' DataView depends on 'Current' POReceiptLine:
PXSelect<POReceiptLineSplit, Where<POReceiptLineSplit.receiptNbr, Equal<Current<POReceiptLine.receiptNbr>>,
And<POReceiptLineSplit.lineNbr, Equal<Current<POReceiptLine.lineNbr>>,
And<Where<POLineType.goodsForInventory, Equal<Current<POReceiptLine.lineType>>,
Or<POLineType.goodsForSalesOrder, Equal<Current<POReceiptLine.lineType>>,
Or<POLineType.goodsForDropShip, Equal<Current<POReceiptLine.lineType>>>>>>>>> splits;
The issue is that 'Current' POReceiptLine doesn't change when user clicks (select) another POReceiptLine in 'transactions' grid. Setting the grid 'SyncPosition' to true in your customization project should ensure 'Current' value is properly set when users change record selection in the grid:

Related

How to call SetPropertyException from another event handler?

Below is my code to insert whatever value is entered into my UsrWLAmt field into my BudgetGrid representing the history of the fields values.
I want to raise a warning prompting the user to enter a value into the details field in the BudgetGrid History
protected void PMProject_UsrWLAmt_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (PMProject)e.Row;
PMProject con = Base.Project.Current;
PX.Objects.PM.ProjectExt item = con.GetExtension<PX.Objects.PM.ProjectExt>();
if (item.UsrWLAmt > 0)
{
atcBudgetHis bud = new atcBudgetHis();
bud.CreatedDateTime = DateTime.Now;
bud.Value = item.UsrWLAmt;
BudgetGrid.Insert(bud);
// to attach the exception object to the field
BudgetGrid.View.Cache.RaiseExceptionHandling<atcBudgetHis.details>(
bud, " ",
new PXSetPropertyException(
"Please specifiy reason for budget change.",
PXErrorLevel.Warning));
}
}
I've also tried BudgetGrid.Cahce.RaiseExceptionHandling
The code above doesn't raise any trace errors.
EDIT:
PXUIFieldAttribute.SetWarning<atcBudgetHis.details>(BudgetGrid.Cache, null, "Please specifiy reason for budget change.");
Works for all rows but
PXUIFieldAttribute.SetWarning<atcBudgetHis.details>(BudgetGrid.Cache, bud, "Please specifiy reason for budget change.");
Doesn't raise any warnings.
I could create another field above the grid for the notes to be inserted, but is there a way I can set the warning for the last row in the BudgetGird?
First things first, to show a warning in Acumatica one of the following events must be used:
FieldVerifying and throw PXSetPropertyException, when warning should appear only during the time user updates a record
RowUpdating with RaiseExceptionHandling method invoked on PXCache, if warning should appear on multiple fields only during the time user updates a record
RowSelected with RaiseExceptionHandling method invoked on PXCache, if warning should appear on multiple fields all the time until a user addresses the cause of warning
I guess for your particular scenario, RowSelected might work best to constantly show warnings for all empty cells within Notes column:
public void atcBudgetHis_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
atcBudgetHis row = e.Row as atcBudgetHis;
if (row == null) return;
if (string.IsNullOrEmpty(row.Details))
{
sender.RaiseExceptionHandling<atcBudgetHis.details>(row, string.Empty,
new PXSetPropertyException("Please specify reason for budget change.", PXErrorLevel.Warning));
}
else
{
sender.RaiseExceptionHandling<atcBudgetHis.details>(row, row.Details, null);
}
}
It seems like you tried to set a warning on a DAC instance that didn't exists in the grid at the moment the event was called.
Have you tried setting the warning on the existing row returned in the event handler parameter instead?
PXUIFieldAttribute.SetWarning<atcBudgetHis.details>(BudgetGrid.Cache, row, "Please specify reason for budget change.");
The warning applies to all rows that satisfy the condition that executes this line. If you want to display it for only the last row, you would have to manually check if the row received in the parameter is the same as the last row in your data view and only then execute the warning for that row.
The solution was to use the RowInserted event for my grid and pass the row variable into SetWarning
You need to change this code:
BudgetGrid.Insert(bud);
// to attach the exception object to the field
BudgetGrid.View.Cache.RaiseExceptionHandling<atcBudgetHis.details>(bud, " ",new PXSetPropertyException("Please specifiy reason for budget change.",PXErrorLevel.Warning));
To something like this:
bud = BudgetGrid.Insert(bud); //you need to get the "bud" which is in the cache
// to attach the exception object to the field
BudgetGrid.View.Cache.RaiseExceptionHandling<atcBudgetHis.details>(bud, " ",new PXSetPropertyException("Please specifiy reason for budget change.",PXErrorLevel.Warning));

How to override the textbox content in CSWPFAutoCompleteTextBox

I am using the autocomplete textbox from this MSDN link.
Question:
CSWPFAutoCompleteTextBox has property "AutoSuggestionList" which I bound this to observable string collection. Each string is made of id + description. When user selects an item from dropdown, how can I override the texbox content? I want to manipulate the text box content.
This is a textbox that extends a wpf combobox to make it searchable.
When the user types in a string in the textbox, it displays matching strings as a dropdown, user selects an item and the item is displayed in the textbox.
the question is how to override the textbox contents of this control.
Without actual format of your code it is hard to answer precisely, but for example, if your strings are
string[] suggestions = {"0: Yes", "1: No", "666: whatever"}
Then you could get the number by something like
sugestedString.Substring(0, sugestedString.IndexOf(':'));
EDIT: I misunderstood the question. So if I now understands correctly, you might do it with
for(int i = 0; i < suggestions.Length; i++) {
if(suggestions[i] == selectedString) {
return i;
}
}
If you seek only a number within your list of all possible suggestions.
If you seek a number within narrowed-down suggestions, it gets somewhat more difficult.
You firstly need to note down what user has typed so-far (e.g. "Aut"). Then you need what he actually selected (e.g. "Automotive"). With those things, you can then search all your possible suggestions, count how many of them satisfies the user-typed beginning and finaly which of them is the selected one. It might look like this:
int counter=0;
for(int i = 0; i < suggestions.Length; i++) {
if( suggestions[i].StartsWith(typedString)) {
counter++;
if(suggestions[i] == selectedString) {
counter;
}
}
}

Move records up/down in grid with "SortOrder" field and "Up"/"Down" buttons

Current customization project I'm working on has the requirement of displaying / editing a grid with a "Sort Order" for records. The "SortOrder" field is read only with up/down buttons to allow the user to re-order the items in the grid.
The "SortOrder" column in the DAC is a simple Int field.
The PXSelect statement for the grid is using a OrderBy>> to display the records.
The Grid in the ASPX is a defined with "SyncPosition= true"
I've added an Up/Down button that increments/decrements the "SortOrder" value for the current selected record.
The issue that I'm running into is that the first time "Up" or "Down" is clicked, the "SortOrder" field is updated however the rows do not move. Once I click Save to persist the update, the grid then refreshes with the right order.
I've looked through the the rest of the code but all other situations where this is used is for treeviews, not grids.
I've tried adding a View.RequestRefresh() at the end of my Action but this doesn't cause the reorder.
What would be the best way without a Persist after each move to get the Grid to update and reflect the current order from the cache values? As usual I'm assuming I'm overlooking something simple.
Any advice would be appreciated.
I had a look at the generic inquiry designer source code - it has an up/down button in the grid to reorder the fields. The views don't have an OrderBy clause:
public PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>>> Parameters;
OrderBy is not necessary because the LineNbr field is a key field - system automatically orders the records by the key fields.
public abstract class lineNbr : IBqlField { }
[PXDBInt(IsKey = true)]
[PXDefault]
[PXLineNbr(typeof(GIDesign))]
[PXParent(typeof(Select<GIDesign,
Where<GIDesign.designID, Equal<Current<GIFilter.designID>>>>))]
public virtual int? LineNbr { get; set; }
The code for the button looks like this:
[PXButton(ImageKey = Sprite.Main.ArrowUp, Tooltip = ActionsMessages.ttipRowUp)]
[PXUIField(DisplayName = ActionsMessages.RowUp, MapEnableRights = PXCacheRights.Update)]
protected void moveUpFilter()
{
if (this.Parameters.Current == null)
return;
GIFilter prev = PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>, And<GIFilter.lineNbr, Less<Current<GIFilter.lineNbr>>>>, OrderBy<Desc<GIFilter.lineNbr>>>.Select(this);
if (prev != null)
this.SwapItems(this.Parameters.Cache, prev, this.Parameters.Current);
}
[PXButton(ImageKey = Sprite.Main.ArrowDown, Tooltip = ActionsMessages.ttipRowDown)]
[PXUIField(DisplayName = ActionsMessages.RowDown, MapEnableRights = PXCacheRights.Update)]
protected void moveDownFilter()
{
if (this.Parameters.Current == null)
return;
GIFilter next = PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>, And<GIFilter.lineNbr, Greater<Current<GIFilter.lineNbr>>>>, OrderBy<Asc<GIFilter.lineNbr>>>.Select(this);
if (next != null)
this.SwapItems(this.Parameters.Cache, next, this.Parameters.Current);
}
The SwapItems function is shared between all the move up / move down actions:
private void SwapItems(PXCache cache, object first, object second)
{
object temp = cache.CreateCopy(first);
foreach (Type field in cache.BqlFields)
if (!cache.BqlKeys.Contains(field))
cache.SetValue(first, field.Name, cache.GetValue(second, field.Name));
foreach (Type field in cache.BqlFields)
if (!cache.BqlKeys.Contains(field))
cache.SetValue(second, field.Name, cache.GetValue(temp, field.Name));
cache.Update(first);
cache.Update(second);
}
Finally, there's a bit of JavaScript code in the ASPX code - it may or may not be what you're missing to get the feature to work correctly; i'm not exactly sure what it's doing but would encourage you to open SM208000.aspx in an editor and look for commandResult. Also check out the CallbackCommands that are defined on the grids which support up/down - it may have something to do with it.

How to find out if an XPages view(Panel) is sorted by a viewColumnHeader sort toggle switch?

I'm displaying a view using a viewPanel. The view contains some columns which are enabled to be sorted in both directions, which is working fine.
Now I'd need to find out if the view has been resorted by the user by being clicked on the "Sort Toggle" in the viewColumnHeader.
Is there a property or a method which I can access programmatically to find this out?
Or is there an event in the viewPanel I could intercept or listen to?
Update:
I found that the class UIViewColumnHeader (which is extended by XspViewColumnHeader) has some static properties describing the icons (see here) - could I check for them? And if yes, how?
The sort column should be defined on the dominoView datasource in the sortColumn property, whose Java class is com.ibm.xsp.model.domino.DominoViewData. That can be accessed by calling getData() on the ViewPanel
So you should be able to use getComponent("myViewPanel").getData().getSortColumn() in SSJS. By default I suspect it will be blank.
Ok, with the help of those both sites, I solved resp. worked around my problem:
First, there was Naveen's solution how to Getting ViewPanel Headers programmatically
Second, Mark Leusink's article about the order of events in XPages.
This led me to following code:
<xp:this.beforeRenderResponse><![CDATA[#{javascript:
var viewPnl:com.ibm.xsp.component.xp.XspViewPanel = getComponent("viewPanel1");
var list:java.util.List = viewPnl.getChildren();
var replaceRespCol = false;
for (var i = 0 ; i < list.size(); i++) {
var viewCol:com.ibm.xsp.component.xp.XspViewColumn = list.get(i);
var viewHdr:com.ibm.xsp.component.xp.XspViewColumnHeader = viewCol.getHeader();
if (#RightBack(viewHdr.getSortIcon(), "/") != "sort_none.gif") {
replaceRespCol = true;
}
}
if (getSearchKey() != "") {
getComponent("viewColumn1").setRendered(false);
getComponent("viewColumn1b").setRendered(true);
} else if (replaceRespCol == true) {
getComponent("viewColumn1").setRendered(false);
getComponent("viewColumn1b").setRendered(true);
} else {
getComponent("viewColumn1").setRendered(true);
getComponent("viewColumn1b").setRendered(false);
}}]]></xp:this.beforeRenderResponse>
which enables me now to dynamically show a categorized column (viewColumn1)
- if no filter/search key is entered and
- no re-sort via the headers is done.
Otherwise, a flat column (viewColumn1b) is shown. HTH

Binding the style property of all cells in a JavaFX TableView

I have a JavaFX TableView where each row should have a conditional style.
The styling is dependent on whether the source item of of the table row is present in a certain list or not.
This is what I have so far:
1) The data class that holds the data of a table row together with two boolean properties (true if the data is contained in list X) and a string property that should bind to the correct style attributes.
private class WebPageData {
private WebPage page;
private BooleanProperty isReferenced = new SimpleBooleanProperty(false);
private BooleanProperty isReferencing = new SimpleBooleanProperty(false);
private StringBinding style = new When(isReferenced).then("...").otherwise(...);
}
2) A change listener on table selection change that updates each boolean property accordingly, when the table selection changes
tblResultData.getSelectionModel().getSelectedIndices().addListener(new ListChangeListener<Integer>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Integer> arg0) {
if (arg0.getList().size() == 0) {
selectedPage.set(null);
} else {
// for coloring only consider the first selected row
// multi select must be doable for certain other features
WebPage selectedWebPage = tblResultData.getItems().get(arg0.getList().get(0)).page;
selectedPage.set(selectedWebPage);
// tableModel.data holds a list of data for every table row
for (WebPageData data : tableModel.data) {
boolean referenced = selectedWebPage.getReferencedWebPagesList().contains(data.page);
boolean referencing = selectedWebPage.getReferencingWebPagesList().contains(data.page);
data.isReferenced.set(referenced);
data.isReferencing.set(referencing);
}
}
}
});
Now what I want to do is to somehow bind the style property of each table cell to the style property of WebPageData - so that the change listener updates the two boolean properties, therefore the style property of WebPageData is updated and in consequence the style of the table cell changes.
I tried to bind the style during creation phase by using a custom TableCellFactory, but of course this approach fails as there is no WebPageData instance present at this time. As the TableColumn classes don't provide an opportunity to iterate over all cells (so I could bind the style after the table actually gets its data), the only option I currently see is to keep a reference to each created table cell. I don't consider this solution is good practice.
So is there any other option to bind the cell styles? If I don't bind them, I have to set the styles manually each time the table selection changes - which puts me to the "I can't iterate over cells" problem again.
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.TreeTableRow;
public class HighlightBoundTreeTableRow extends TreeTableRow<Thing> {
private static final String CHOSEN_STYLE_CLASS = "chosenStyle";
private final ObjectProperty<Boolean> chosen = new SimpleObjectProperty<>();
private final ChangeListener propertyChangeListener = (obs, ov, nv) -> updateHighlight();
#Override
protected void updateItem(Thing item, boolean empty) {
super.updateItem(item, empty);
//cleanup
getStyleClass().remove(CHOSEN_STYLE_CLASS);
chosen.unbind(); // unbinding something that is not bound has no effect
chosen.removeListener(propertyChangeListener); // also ok to remove a listener that was never there
if (empty) {
return;
}
chosen.bind(item.chosenProperty()); //bind will also set the intial value
chosen.addListener(propertyChangeListener);
updateHighlight();
}
private void updateHighlight() {
if (chosen.get()) {
getStyleClass().add(CHOSEN_STYLE_CLASS);
} else {
getStyleClass().remove(CHOSEN_STYLE_CLASS);
}
}
}
I know this was asked forever ago but maybe it'll help someone.
I had a similar issue I wanted to solve. I know you're using a TableCell and this involves a TreeTableRow but I believe the concept is the same: You want to change a field in your data object and have that change update the styling on wherever that object is being displayed in a table.
So I extended TreeTableRow and gave that class its own property field to hold on to. Every time that row is updated I unbind that property and rebind it to the field I want to listen to. (I do the same with the listener.) Since every time updateItem() is called it could be getting a different instance of my data object.
"chosenStyle" is just a class in my style sheet that changes the background color. Using classes instead of calling setStyle() makes it easier to remove the styling.

Resources