Screen left in "Save button disabled" state after PXSetPropertyException - acumatica

I have written an extension for the SOShipmentEntry BLC with a FieldVerifying event linked to a udf on the Packages grid. When the user enters a value, it checks to see if this value violates our business rules. If it does, it triggers the following.
throw new PXSetPropertyException(SWKMapadocMessages.Not_Prepped,
PXErrorLevel.RowWarning);
This works fine, however after that error is thrown if the user tries to save any subsequent changes, they get a "Error #299: The Save button is disabled" message and the save is cancelled. The user has to refresh the screen to get back to a usable state. Anyone know why this is?

Upon review of the customization, there seems to be an issue with the primary data view Document used as the DataMember for the custom MAPADOC tab. As explained in section Providing Data for Controls of the T200 developer training material, it is only possible to bind same data view to multiple container controls, if the data view is not specified as the PrimaryView for the datasource control. I believe, the issue with disabled Save button should be completely resolved after CurrentDocument is defined as the DataMember for MAPADOC tab instead of the primary Document data view.
<px:PXTabItem Text="MAPADOC">
<Template>
<px:PXFormView runat="server" ID="CstFormView10" DataMember="CurrentDocument"
SkinID="" Width="100%">
<Template>
...
</Template>
</px:PXFormView>
</Template>
</px:PXTabItem>
Aside from that, I also noticed the Actions.PressSave(); method invoked within the FieldVerifying event handler. To be honest, an event handler is never a good place to save changes to the database. An action delegate is a perfect place for that, but within an event handler, one should realistically never attempt to invoke the Actions.PressSave(); method, since at that moment not all changes in DACs had reached the appropriate PXCache and might get lost because of execution of Actions.PressSave(); being called.

Related

Hiding a tab from the user interface dynamically

I have a page with a PXTab control and want to show or hide individual tabs on the page dynamically. How can I control visibility of tabs at runtime?
You can do it in one of the following two ways:
By setting a VisibleExp property on PXTabItem in ASPX page
By
enabling/disabling AllowSelect property of the view that serves as a
DataMember of the grid that is displayed on that tab
Method 1 – VisibleExp
In this method, you directly write the conditions under which the tab should be visible in the screen's ASPX code.
<px:PXTabItem Text="Tax Agency Settings" BindingContext="tab"
VisibleExp="DataControls["chkTaxAgency"].Value = 1">
Note that the binding context is important as it specifies which element's DataControls you want to access in the VisibleExp.
Also DataControls is collection of values for user interface fields, so you need to specify there IDs of controls (not data access class fields).
However, this method is extremely limited in many ways:
The condition checking is restricted to controls available in the UI,
so it is not possible to condition visibility upon the internal state
of the system.
Sometimes this method will require you to include
"fake" data controls into ASPX that will only be checked in
VisibleExp, but won't actually be ever seen by the user.
There seem to be no support for complex conditions including AND/OR.
Ugly " entities instead of normal quotes in the expression – not
particularly readable.
Most importantly, if you need to disable the tab for a particular document type, there is no way around hard - coding a constant into a VisibleExp. You would be explicitly writing something like: VisibleExp="DataControls["edDocumentType"].Value != CHK"
Hard-coding is generally considered a very poor development practice. It poses a significant threat to code maintainability: probably the above code is going to break something in the future. For example if you decide to rename the document codes form CHK to CHQ.
In addition to that, this solution is not easily generalized to situations when you suddenly discover the need to hide the tab not only for checks, but also for other document types. This is due to lack of complex conditional expressions mentioned above.
Method 2 – AllowSelect
Idea of this method is - if you hide all controls from the tab item, than Acumatica will automatically hide tab with no visible controls.
Lets do an example: assume that you need to hide a tab named Applications depending on the document type selected in SO303000 (Invoices):
The tab that we're interested in has a grid control with a data member set to Adjustments:
<px:PXTabItem Text="Applications" RepaintOnDemand="false">
<Template>
<px:PXGrid ID="detgrid" DataSourceID="ds" SkinID="Details">
<Levels>
<px:PXGridLevel DataMember="Adjustments">
............
</px:PXGridLevel>
</Levels>
</px:PXGrid>
</Template>
</px:PXTabItem>
And not that this tab item has only one control - PXGrid.
Also note required property here - RepaintOnDemand="false". This property indicates whether the control refresh tab items content (and select data) after the item becomes visible. Unfortunately, setting it to false incurs certain performance losses. In particular, the Adjustments view' Select will be called much more frequently.
Currently, the Tab is "smart" in the way that it understands that when its child control (PXGridLevel) cannot perform a select on its data member; in this case, the tab hides itself from the UI. This is why you can control the visibility of the tab by setting the AllowSelect property of the cache that corresponds to the Adjustments:
Adjustments.Cache.AllowSelect =
doc.DocType != ARDocType.CashSale
&& doc.DocType != ARDocType.CashReturn;
The above code is written in the ARInvoice_RowSelected handler of the graph, where ARInvoice is the primary DAC and the type of the master records of the page. So, every time ARInvoice is selected, the tab item will become visible or invisible depending on the document type.
This method has its own limitations too:
You should always remember that it is not enough to disable
AllowSelect, you should also enable it when needed. So you need to
evaluate this property every time when event is called.
This method doesn't seem to work without setting the PXTabItem's RepaintOnDemand
property to false (see above).
Source: http://asiablog.acumatica.com/2016/05/hiding-tab-from-user-interface.html

CRM 2011 Sub-Grid: Call JavaScript On-Click or On-Focus

How can JavaScript execute when a sub-grid receives focus?
I have a sub-grid that shows all Cases associated with an Account.
I would like to disable some fields on the form when the sub-grid receives focus.
RibbonDiffXML action for a ribbon button:
<Actions>
<JavaScriptFunction Library="$webresource:mda_convertemailtocaselib" FunctionName="ConvertEmailToCaseLib.addToCase">
<CrmParameter Value="SelectedControlSelectedItemIds" />
</JavaScriptFunction>
</Actions>
This passes the sub-grid select items to a ribbon button, but I would like the JavaScript to execute before the ribbon button is pressed.
I haven't tried either of these out, but hopefully one of them should work for you.
Although it is a subgrid, there still is a control on the form. Unfortunately there is no "supported" way to know when a control gets focus since there aren't any OnFocus/OnBlur methods exposed via the CRM JS API. However you should be able to add your own JS event handler for that control and disabled the fields via the API Xrm.Page.getControl("fieldname").setDisabled(true)
I'm not 100% sure if this way would work, but it would be pretty sweet. Create a CustomRule EnableRule. In this CustomRule you call your own JS. In this create a function where you disable the fields and then return true to make the button enabled. It would look something like this:
function disableFieldsEnableRule() {
Xrm.Page.getControl().setDisabled(true);
...
...
return true; // so the button is enabled
}
The only reason I'm not sure if this will work is because I'm not sure if the Enable rules are checked each time you click on the subgrid or just the first time (I think it should be every time). Also I'm not sure if you want those fields re-enabled once you click off. If you do you might have to do something similar to this with a button on the native form, or something else.

XPages sometimes refresh and lose content

I hope someone can help me solve a very serious problem we face at the moment with a business critical application losing data when a user works in it.
This happens randomly - I have never reproduced this but the users are in the system a lot more than me.
A document is created with a load of fields on it, and there are 2 rich text fields. We're using Domino 8.5.3 - there are no extension lib controls in use. The document has workflow built in, and all validation is done by a SSJS function called from the data query save event. There is an insane amount of logging to the sessionscope.log and also this is (now) captured for each user in a notes document so I can review what they are doing.
Sometimes, a user gets to a workflow step where they have to fill in a Rich Text field and make a choice in a dropdown field, then they submit the document with a workflow button. When the workflow button is pressed (does a Full Update) some client side JS runs first
// Process any autogenerated submit listeners
if( XSP._processListeners ){ // Not sure if this is valid in all versions of XPages
XSP._processListeners( XSP.querySubmitListeners, document.forms[0].id );
}
(I added this to try and prevent the RTF fields losing their values after reading a blog but so far it's not working)
then the Server-side event runs and calls view.save() to trigger QS code (for validation) and PS code to run the workflow agent on the server.
95% of the time, this works fine.
5% of the time however, the page refreshes all the changes made, both to the RFT field (CKEditor) and the dropdown field are reloaded as they were previously, with no content. It's like the save hasn't happened, and the Full Update button has decided to work like a page refresh instead of a submit.
Under normal circumstances, the log shows that when a workflow button is pressed, the QuerySave code starts and returns True. Then the ID of the workflow button pressed is logged (so I can see which ones are being used when I am reviewing problems), then the PostSave code starts and finally returns true.
When there is a problem, The QuerySave event runs, returns true if the validation has passed, or false if it's failed, and then it stops. The ID of the workflow button is also logged. But the code should continue by calling the PostSave function if the QuerySave returns true - it doesn't even log that it's starting the PostSave function.
And to make matters worse, after the failure to call the PostSave code, the next thing that is logged is the beforePageLoad event running and this apparently reloads the page, which hasn't got the recent edits on it, and so the users loses all the information they have typed!
This has to be the most annoying problem I've ever encountered with XPages as I can find no reason why a successful QuerySave (or even a failure because mandatory fields weren't filled in) would cause the page to refresh like this and lose the content. Please please can someone help point me in the right direction??
It sounds as if in the 5% use cases, the document open for > 30mins and the XSP session is timing out - the submit causes the component tree to be re-created, and the now empty page returned back to the user. Try increasing the time out for the application to see if the issue goes away.
I would design the flow slightly different. In JSF/XPages validation belongs into validators, not into a QuerySave event. Also I'd rather use a submit for the buttons, so you don't need to trigger a view.save() in code. This does not interfere with JSF's sequence of things - but that's style not necessarily source of your problem.... idea about that:
As Jeremy I would as a first stop suspect a timeout, then the next stop is a fatal issue in your QuerySave event, that derails the runtime (for whatever reason). You can try something like this:
var qsResult = false;
// your code goes here, no return statements
// please and if you are happy
qsResult = true;
return qsResult;
The pessimistic approach would eventually tell you if something is wrong. Also: if there is an abort and your querySave just returns, then you might run in this trap
function noReturn() {return; } //nothing comes back!
noReturn() == true; --> false
noReturn() == false; --> false
noReturn() != false; --> true!!!!
What you need to check: what is your performance setting: serialize to disk, keep in memory or keep latest in memory? It could be you running foul of the way JavaScript libraries work.
A SSJS library is loaded whenever it is needed. Variables inside are initialized. A library is unloaded when memory conditions require it and all related variables are discarded. so if you rely on any variable in a JS Function that sits inside a SSJS library between calls you might or might not get the value back, which could describe your error condition. Stuff you want to keep should go into a scope (viewScope seems right here).
To make it a little more trickier:
When you use closures and first class functions these functions have access to the variables from the parent function, unless the library had been unloaded. Also functions (you could park them in a scope too) don't serialize (open flaw) so you need to be careful when putting them into a scope.
If your stuff is really complex you might be better off with a backing bean.
Did that help?
To create a managed bean (or more) check Per's article. Your validator would sit in a application bean:
<faces-config>
<managed-bean>
<managed-bean-name>workflowvalidator</managed-bean-name>
<managed-bean-class>com.company.WfValidator</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
</faces-config>
Inside you would use a map for the error messages
public Map<String,String> getErrorMessages() {
if (this.errorStrings == null) { // errorStrings implements the MAP interface
this.loadErrorDefinitions(); //Private method, loads from Domino
}
return this.errorStrings;
}
then you can use EL in the Error message string of your validators:
workflowvalidator.errorMessage("some-id");
this allows XPages to pick the right one directly in EL, which is faster than SSJS. You could then go and implement your own custom Java validator that talks to that bean (this would allow you bypass SSJS here). Other than the example I wouldn't put the notes code in it, but talk to your WfValidator class. To do that you need to get a handle to it in Java:
private WfValidator getValidatorBean() {
FacesContext fc = FacesContext.getCurrentInstance();
return (WfValidator) fc.getApplication()
.getVariableResolver()
.resolveVariable(fc, "workflowvalidator");
}
Using the resolver you get access to the loaded bean. Hope that helps!
My experience is that this problem is due to keeping page in memory. Sometimes for some reason the page gets wiped out of memory. I'm seeing this when there is a lot of partial refreshes with rather complex backend Java processing. This processing somehow seems to take the space from memory that is used by the XPage.
The problem might have been fixed in later releases but I'm seeing it at least in 8.5.2.
In your case I would figure out some other workaround for the CKEditor bug and use "Keep pages on disk" option. Or if you can upgrade to 9.0.1 it might fix both problems.

combobox onchange with partial update doesn't fire in new document mode

In my Custom Control I have SSJS for a combobox onchange event to update an element using partial update.
The event is not fired when I'm in new document mode but fires when in edit mode.
I've tried other combinations such as Full Update, other events but it seems like Partial update is not working when in New Document mode.
Am I missing something here?
Using examples from: xpageswiki.com
Please advice
/Mike
I'll need to see the block for your radio button from your page's XML source.
These types of issues are sometimes caused by a bad event handler. In particular, depending on how you use Eclipse, there may be more that one block due to bugs in DDE. If you find more than one block, remove all but one, and try again. When this happens to me, I normally remove all blocks, then use the DDE UI to manually add the code and settings back.
When having required fields on the form and they are not filled in the event will not fire due the JSF phase. Setting the Server option "Process data without validation" to True will prevent the JSF to validate and continue with the event.

Sharepoint item updating event - cancel event back to editform page?

I have an event receiver for a content type to validate some data in the ItemUpdating event. If I cancel the event (some of the data isn't valid for example), I set the properties cancel to true:
properties.Cancel = true;
properties.ErrorMessage = "...";
SharePoint cancels the updating event ok, but shows the standard SharePoint error page (with the specified message). Only problem is, I've got a complaint that this isn't actually very useful - we should return to the EditForm page so the details can be updated.
Has anyone done this, is there an easy way? The only suggestion I've had is that I can implement my own error page, but that's sounding quite a heavy solution to a (theoretically) simple process.
You could try to output HTML code (which includes javascript as well) in the ErrorMessage. BUT even if you do, the problem is that you have no safe way back to the data the user has entered. Either you make a HTTP/301 redirect and then it's a new page load, or you make the client go history.back() with JavaScript and then the browser may reload the page.
The official way of doing this is that you create a list definition and customize the list template. Then you edit the edit form template and include as many ASP.Net validator controls as needed. Then, implement the server side logic as you need. This article explains the technique: http://msdn.microsoft.com/en-us/library/aa543922.aspx
EDIT: To attach a custom control for editing of a specific contenttype, you add an XmlDocuments section to your ContentType definition. For instance, like this
<ContentType
..........
<XmlDocuments>
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<Display>ContentTypeName_DispForm</Display>
<Edit>ContentTypeName_EditForm</Edit>
<New>ContentTypeName_NewForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
.......
Then you create your own yoursolution_controltemplates.ascx file, which contains as well such blocks:"
<SharePoint:RenderingTemplate ID="ContentTypeName_DispForm" runat="server">
<Template>
<!-- put whatever controls you need here, we typically create a
separate custom control which implements everything-->
</Template>
</SharePoint:RenderingTemplate>
You can try to redirect using CopyUtil : http://weblogs.asp.net/jan/archive/2008/02/26/copyutil-aspx-a-little-sharepoint-gem.aspx
link = "http://yoursite/_layouts/CopyUtil.aspx?Use=id&Action=dispform&ItemId=X&ListId=X&WebId=X&SiteId=X";
Page.Response.Redirect(link)
maybe this will work

Resources