Open GI on button click - acumatica

I have created a button placed on PO line items grid and a new GI. I need to open these GI and automatically pass PO Order number as a parameter to GI.
I have written below code in button event handler. However, it is opening GI inside the inner frame (see screenshot) instead of in the main window.
public PXAction<POOrder> viewFullSODemandGI;
[PXButton()]
[PXUIField(DisplayName = "View Full SO Demand", MapEnableRights = PXCacheRights.Insert, MapViewRights = PXCacheRights.Insert)]
protected virtual IEnumerable ViewFullSODemandGI(PXAdapter adapter)
{
var poOrderNbr = string.Empty;
foreach (POOrder current in adapter.Get<POOrder>())
{
poOrderNbr = current.OrderNbr;
}
var sURL = PXUrl.ToAbsoluteUrl(HttpUtility.UrlPathEncode(string.Format("~/?CompanyID=Company&ScreenId=GI000092&POOrderNumber={0}", poOrderNbr.Trim())));
throw new PXRedirectToUrlException(sURL, PXBaseRedirectException.WindowMode.New, false, null);
}
Please suggest.

I guess the biggest difference between the 2 approaches (one suggested by #Brendan and the other one originally used by #Krunal) is how URL is composed:
#Brendan suggests a relative URL
#Krunal composed an absolute URL
I had exact same result as #Krunal with absolute URLs. However with the relative URL composed by either of code snippets below, the task was successfully achieved:
using GI name (Inquiry Title):
string inqName = "InvoicedItems";
var url = new StringBuilder(PXGenericInqGrph.INQUIRY_URL).Append("?name=").Append(inqName).ToString();
throw new PXRedirectToUrlException(url, PXBaseRedirectException.WindowMode.New, true, null);
using Generic Inquiry ID (Guid of a GI from database):
string inqID = "6b267dbb-0ff2-49b2-b005-355c544daba3";
var url = new StringBuilder(PXGenericInqGrph.INQUIRY_URL).Append("?id=").Append(inqID).ToString();
throw new PXRedirectToUrlException(url, PXBaseRedirectException.WindowMode.New, true, null);
It's also worth checking the PXRedirectToGIRequiredException:
using GI name (Inquiry Title) with a parameter (SalespersonID):
string inqName = "SalespersonSales&SalespersonID=SP0003";
throw new PXRedirectToGIRequiredException(inqName, PXBaseRedirectException.WindowMode.New, true);
using Generic Inquiry ID (Guid of a GI from database):
Guid inqID = Guid.Parse("6b267dbb-0ff2-49b2-b005-355c544daba3");
throw new PXRedirectToGIRequiredException(inqID, PXBaseRedirectException.WindowMode.New, true);
Both samples for the PXRedirectToGIRequiredException can absolutely assign values to GI parameters.

Change your window mode. I think you want to use InlineWindow. Also you can use the url just like this...
throw new PXRedirectToUrlException(
string.Format(#"~/GenericInquiry/GenericInquiry.aspx?ID=47842ccc-aa5d-4840-9d4a-7642cbf34cbe&POOrderNumber={0}", poOrderNbr.Trim()),
BaseRedirectException.WindowMode.InlineWindow,
string.Empty);
Tested and loads in the current window without the menu extras.
I typically call Generic inquiries using the GI's guid "ID" vs screen ID as the screen ID might be different between Acumatica instances. You can get the ID from the export of the GI as the row "DesignID" value in the XML. Replace the ID value i have in the example with your GI DesignID value.

Related

How to set a custom field based on a custom field on a different entity?

I have a checkbox on a DAC extension to the BAccount that I want to use to set a checkbox on Case when a customer is selected. Here is what the checkbox looks like on the Business Account screen.
Here is the field on the Case screen that I want to set when the customer is selected.
I found a stack overflow question where a couple of custom fields on a customer screen were to be copied to custom fields on a sales order. I have tried to substitute my fields into the code but I haven't been able to make it work.
Here is what I have tried. I'm not sure what I am missing.
protected void CRCase_CustomerID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
var crcase = e.Row as CRCase;
if (crcase.CustomerID != null)
{
var customer = PXSelectorAttribute.Select<CRCase.customerID>(sender, crcase) as BAccountR;
if (customer != null)
{
var customerExt = customer.GetExtension<BAccountExt>();
var crcaseExt = crcase.GetExtension<CRCaseExt>();
crcaseExt.UsrContractCustomer = customerExt.UsrSage100;
}
}
}
I do not understand why BAccountR is used instead of just BAccount. Neither works right now.
I solved the issue by combining are response I received on an earlier post. I modified the last two line as follows:
var customerExt = customer.GetExtension<BAccountExt>();
//var crcaseExt = crcase.GetExtension<CRCaseExt>();
//crcaseExt.UsrContractCustomer = customerExt.UsrSage100;
sender.SetValueExt<CRCaseExt.usrContractCustomer>(crcase, customerExt.UsrSage100 != null);
So here is the final issue that I need to resolve. The checkbox I used on the Business Account screen opens a new tab that displays custom fields. These custom fields are an extension of the BAccount DAC called DSDSage100. It is similar to the extension of the BAccount called Customer. In the DSDSage100 extension there is a field called UsrContractCustomer. That is the field that I want to read and set the Case field to the same value. Here is what the Sage 100 Tab looks like. I have a using directive for my project but I can't find the right reference to the DSDSage100 extension.
var customerExt = customer.GetExtension<DSDSage100>();
sender.SetValueExt<CRCaseExt.usrContractCustomer>(crcase, customerExt.UsrContractCustomer != null);
Acumatica provided the answer. While I "extended the BAccount DAC" with my custom table (DSDSage100, similar to the way Customer is an extension of the BAccount DAC), it is it's own DAC. The solution was:
DSDSage100 mydac = PXSelect<DSDSage100, Where<DSDSage100.bAccountID, Equal<Required<DSDSage100.bAccountID>>>>.Select(Base, crcase.CustomerID);
sender.SetValueExt<CRCaseExt.usrContractCustomer>(crcase, mydac.UsrContractCustomer != null);
Thanks for the help and support.

Add Files to Salesorder line item

I want to add files to salesorder line items in Acumatica using web services.
What endpoint should be used?
I want to add an image as shown in the screenshot above, using web service endpoint.
This is an old question, but I just came across this same issue while assisting a customer with a third-party integration. The third-party developers were adamant that they could only use the REST service, as they had already built the rest of their integration around it before realizing they couldn't attach files to sales order lines.
I was able to build a workaround using a customization. The issue at hand is that the way Acumatica's REST API attaches files is only accessible for Top-Level entities - which means there has to be a screen that uses the object as a primary DAC.
The workaround is to do just that, create a new custom screen that uses the SOLine object as it's primary DAC. In order to make the selectors available, I had to remove and replace a couple attributes on the key fields so that they could be visible and enabled. Here is the graph code - it's very simple, as this is basically just the bare minimum needed to be able to create a custom endpoint that uses the SOLine DAC as a top-level entity.
public class SOLineAttachmentEntry : PXGraph<SOLineAttachmentEntry, SOLine>
{
public PXSelect<SOLine> SOLineDetail;
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRemoveBaseAttribute(typeof(PXUIFieldAttribute))]
[PXUIField(DisplayName = "Order Type", Visible=true, Enabled = true)]
protected virtual void SOLine_OrderType_CacheAttached(PXCache sender) { }
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRemoveBaseAttribute(typeof(PXUIFieldAttribute))]
[PXUIField(DisplayName = "Order Nbr", Visible=true, Enabled = true)]
protected virtual void SOLine_OrderNbr_CacheAttached(PXCache sender) { }
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRemoveBaseAttribute(typeof(PXUIFieldAttribute))]
[PXRemoveBaseAttribute(typeof(PXLineNbrAttribute))]
[PXUIField(DisplayName = "Line Nbr", Visible=true, Enabled = true)]
protected virtual void SOLine_LineNbr_CacheAttached(PXCache sender) { }
}
The custom screen layout should be a simple Form with just these three key fields, OrderType, OrderNbr, LineNbr. In the Screen Editor of the customization, you'll want to set CommitChanges=true in the Layout Properties tab for each field.
Once the screen is published, you can use it to create a new custom endpoint and add a single entity by selecting the SOLine view from the custom screen. I named the endpoint "SalesOrderDetailAttach", assigned the endpoint version to be 1.0, and named the new entity "SalesOrderDetail". Using those names, the file attachment request should be a PUT request with the binary file data in the request body, using the url format:
[AcumaticaBaseUrl]/entity/SalesOrderDetailAttach/1.0/SalesOrderDetail/[OrderType]/[OrderNbr]/[LineNbr]/files/[Desired filename in Acumatica]
This worked for this one very specific case, attaching a file to the SOLine object. The screen and the endpoint should really never be used for anything else, and the custom screen should not be accessible to any users other than the administrator and the API user. Ultimately I would recommend using the Screen-Based method from the other answer, but if using the REST API is an absolute must-have, this is a potential workaround.
REST API needs to reference the detail line in the body. Since the body is used to pass the binary data of the attachment REST API can't be used to attach files to detail line.
Below is a screen based API snippet that creates a new master/detail document and attach images to the detail line. If you choose to use the screen based API you will need to adapt the snippet for sales order ASMX screen and fetch sales order with expanded details SOLine. The pattern to attach file will be the same:
string[] detailDescription = "Test";
List<string> filenames = "image.jpg";
List<byte[]> images = new byte[] { put_image_binary_data_here } ;
ServiceReference1.Screen context = new ServiceReference1.Screen();
context.CookieContainer = new System.Net.CookieContainer();
context.Url = "http://localhost/Demo/Soap/XYZ.asmx";
context.Login("admin#CompanyLoginName", "admin");
ServiceReference1.XY999999Content content = PX.Soap.Helper.GetSchema<ServiceReference1.XY999999Content>(context);
List<ServiceReference1.Command> cmds = new List<ServiceReference1.Command>
{
// Insert Master
new ServiceReference1.Value { Value="<NEW>", LinkedCommand = content.Document.KeyField},
new ServiceReference1.Value { Value="Description", LinkedCommand = content.Document.Description},
// Insert Detail
content.DataView.ServiceCommands.NewRow,
new ServiceReference1.Value { Value = noteDetail[0], LinkedCommand = content.DataView.Description },
// Attaching a file to detail
new ServiceReference1.Value
{
Value = Convert.ToBase64String(images[0]),
FieldName = filenames[0],
LinkedCommand = content.DataView.ServiceCommands.Attachment
},
content.Actions.Save,
content.Document.KeyField
};
var returnValue = context.PP301001Submit(cmds.ToArray());
context.Logout();

Error #153 Another process has added 'Note' record. Your changes will be lost(SalesOrderEntry)

In the Sales Order page, I created a custom button which purpose is to save and refresh the page. Currently it saves fine and processes the new order to an order number but when I try to add an item/edit or perform an action in the drop down menu I receive the error message.
Here's my code:
public PXAction<SOOrder> SRefresh;
[PXUIField(DisplayName = "S RefreshT")]
[PXButton(CommitChanges = true)]
protected virtual IEnumerable sRefresh(PXAdapter adapter)
{
SOOrderEntry graph = PXGraph.CreateInstance<SOOrderEntry>();
Base.Actions.PressSave();
SOLine sLine = PXSelect<SOLine, Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>>>.Select(graph, this.Base.Document.Current.OrderNbr);
if (sLine != null && sLine.InventoryID.HasValue)
{
graph.Document.Current = graph.Document.Search<SOLine.orderNbr>(sLine.OrderNbr);
throw new PXRedirectRequiredException(graph, null);
}
return adapter.Get();
}
I've also tried using graph.Persist() as said in the manual instead of Action.PressSave(); with no success.
I appreciate any input you guys may have, Thank you
Since you're working with the current sales order, you don't need to create a new instance of the sales order entery graph and redirect your user. You can work with the Base object and run all your logic on it.
Base.Document.Current contains a reference to the current SOOrder, and Base.Transactions contains the list of SOLine of this document. Another problem I also found in your code is that you're calling Document.Search<SOline.orderNbr>; it should be SOOrder.orerNbr since you're searching inside the Document view, which contains sales orders, and not lines. In this case, it's not even necessary to search, Base.Document.Current will already be set to the order you're looking at.
I strongly recommend completing the standard Acumatica developer trainings - T100, T200, T300; this stuff is all covered and will get you productive quickly

Trying to Use LoadMoreElement in Monotouch.Dialog

I am using Monotouch to write an Ipad app. The app uses tables to browse down through a directory tree and then select a file. I have used Monotouch.Dialog to browse the directories and I set up the directory tables as the app starts.However there are too many files to set up in a table as the app starts and so I want to set up the 'file table' as the file is selected from the lowest level directory table. I am trying to use LoadMoreElement to do this but I cannot make it work or find any examples online. I have coded the 'Elements API Walkthrough' in the Xamarin tutorial at:- http://docs.xamarin.com/ios/tutorials/MonoTouch.Dialog
I then add a new section to the code:-
_addButton.Clicked += (sender, e) => {
++n;
var task = new Task{Name = "task " + n, DueDate = DateTime.Now};
var taskElement = new RootElement (task.Name){
new Section () {
new EntryElement (task.Name,
"Enter task description", task.Description)
},
new Section () {
new DateElement ("Due Date", task.DueDate)
},
new Section()
{
new LoadMoreElement("Passive","Active",
delegate {MyAction();})
}
};
_rootElement [0].Add (taskElement);
Where MyAction is:-
public void MyAction()
{
Console.WriteLine ("we have been actioned");
}
The problem is that MyAction is triggered and Console.Writeline writes the message but the table stays in the active state and never returns to passive. the documentation says:-
Once your code in the NSAction is finished, the UIActivity indicator stops animating and the normal caption is displayed again.
What am I missing?
Ian
You need to set the "Animating" property in the element to false.
Like this:
LoadMoreElement loadMore = null;
loadMore = new LoadMoreElement (
"Passive", "Active",
delegate {loadMore.Animating = false;});
Where did you see any documentation that says that the animation stops when the delegate stops running? If that is documented anywhere, that is wrong.

Change UIBarButton Identifier on runtime

i want to change the Identifier of my UIBarButton when the value from my slider has changed. I tried it that way:
var button = new UIBarButtonItem(UIBarButtonSystemItem.Save);
this.Pad_btnClose = null;
this.Pad_btnClose = button;
But it doesnt work. I also tried it that way:
this.Pad_btnClose = new UIBarButtonItem(UIBarButtonSystemItem.Save);
doesn´t work too.
Setting that variable (or outlet, you don't say which) isn't going to remove the UIBarButtonItem from the screen.
In order to do that, you must create an outlet for the UIToolbar, then call:
yourToolbar.Items = new UIBarButtonItem[] { yourNewButtonItem };
Or if you want it to animate:
yourToolbar.SetItems(new UIBarButtonItem[] { yourNewButtonItem }, true);
This will overwrite the list of button items in the toolbar on the screen.

Resources