I have one custom screen with some values and a button, based on some logic I want to open Sales Order and set order type. Am able to do that only for SO order type as SO is set as default in the preferences screen. My question is, how I can open Sales Order and set "IN" or "RM" as default.
SOOrderEntry graph = PXGraph.CreateInstance<SOOrderEntry>();
PXRedirectHelper.TryRedirect(graph, PXRedirectHelper.WindowMode.NewWindow);
i also tried with below code and it is also working only for SO type but, it is selecting first order as default.
SOOrderEntry docgraph = PXGraph.CreateInstance<SOOrderEntry>();
docgraph.Document.Current = docgraph.Document.Search<SOOrder.orderType>(SOOrderTypeConstants.SalesOrder);
throw new PXRedirectRequiredException(docgraph, true, "Order") { Mode = PXBaseRedirectException.WindowMode.Same };
docgraph.Document.Search<SOOrder.orderType>(SOOrderTypeConstants.SalesOrder); this code will return you first Order of SalesOrder type. Instead of this you can do the following:
1. Create an instance of SalesOrder.
2. Set the OrderType of the SalesOrder to the one you need.
3. Set docgraph.Document.Current to that SalesOrder.
Your code will be like the following(may some changes will be required):
SOOrderEntry docgraph = PXGraph.CreateInstance<SOOrderEntry>();
SOOrder newOrder = docgraph.Document.Insert();//create new Order
newOrder.OrderType = "IN"; // set the OrderType to the one you need.For example "IN"
newOrder = docgrapg.Document.Update(newOrder);// update the order
docgraph.Document.Current = newOrder;// set your order as the current order of the BLC
throw new PXRedirectRequiredException(docgraph, true, "Order") { Mode = PXBaseRedirectException.WindowMode.Same };
Related
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.
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();
I am trying to set an extended field value for INRegister after I create and insert a new INKitRegister object into the KitAssemblyEntry graph. I understand that the INKitRegister, on save, has a method which creates the INRegister that is stored in the database for the kit. After the save, I have code that I want to execute that would set the extension field I added to the INRegister data table. When this code executes, I get the following error:
Error #78: Another process has updated the 'INRegister' record. Your changes will be lost.
I'm not sure why since I execute this edit after the data table entry is completed.
Here is my code:
...//Code to create component children
INKitRegister kitHeader = new INKitRegister
{
//set header fields
};
//I also have this extended field on the INKitRegister DAC
INKitRegisterExt kitHeaderExt = PXCache<INKitRegister>.GetExtension<INKitRegisterExt>(kitHeader);
kitHeaderExt.UsrWOID = CurrentDocument.Current.Id;
INTranSplit kitParentAssembly = new INTranSplit
{
//Making INTranSplit entry for kit
};
...
//Do I need to get the graph's extension? set register view? do I need to get kitHeader's inserted refNbr and forward that to function to set woid?
KitAssemblyEntry graphKAE = PXGraph.CreateInstance<KitAssemblyEntry>();
graphKAE.Document.Insert(kitHeader);
graphKAE.Document.Current.KitRevisionID = "1";
graphKAE.Actions.PressSave();
foreach (INComponentTran ch in kitChildren)
{
ch.RefNbr = kitHeader.RefNbr;
graphKAE.Components.Insert(ch);
}
graphKAE.Actions.PressSave();
//Code in which I get the newly created INRegister and set the extended field.
string refNbr = graphKAE.Document.Current.RefNbr;
INRegister reg = PXSelect<INRegister, Where<INRegister.refNbr, Equal<Required<INRegister.refNbr>>, And<INRegister.docType, Equal<Required<INRegister.docType>>>>>
.Select(this, refNbr, "P");
INRegisterExt regExt = PXCache<INRegister>.GetExtension<INRegisterExt>(reg);
regExt.UsrWOID = CurrentDocument.Current.Id;
INRegisters.Update(reg);
this.Actions.PressSave();
PXRedirectHelper.TryRedirect(graphKAE, PXRedirectHelper.WindowMode.Popup);
Any suggestions? I tried placing the code in a KitAssemblyEntry_Extension class under INKitRegister_RowPersisting and INKitRegister_RowUpdating. I've also looked into possibly executing the update when the popup window closes, but I do not know how to do that. Any help is welcome to point me in the correct direction.
That indicates the record you have is not the latest as it exists in the database. Try using PXSelectReadonly in place of PXSelect to get your INRegister (reg) object.
I assume it is the line "INRegisters.Update(reg);" then save that is failing?
I would also try to use the kit graph to update the INRegister and select the inregister. Try changing this one section...
//Code in which I get the newly created INRegister and set the extended field.
string refNbr = graphKAE.Document.Current.RefNbr;
INRegister reg = PXSelectReadOnly<INRegister,
Where<INRegister.refNbr, Equal<Required<INRegister.refNbr>>,
And<INRegister.docType, Equal<Required<INRegister.docType>>>>>
.Select(graphKAE, refNbr, "P");
INRegisterExt regExt = PXCache<INRegister>.GetExtension<INRegisterExt>(reg);
regExt.UsrWOID = CurrentDocument.Current.Id;
graphKAE.Caches[typeof(INRegister)].PersistUpdated(graphKAE.Caches[typeof(INRegister)].Update(reg));
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.
My objective is to respond to a picked event in an external system and mark the SalesOrder "Picked" in NetSuite and later respond to a pack event in an external system and mark the SalesOrder "Packed" in NetSuite.
I am using the code from the SuiteTalk sample application. I first get a copy of the existing ItemFulfillment record and then populate a new ItemFulfillment record.
The code works great when I respond to the pick event. Unfortunately, when I respond to the pack event, when I try to get a copy of the existing ItemFulfillment Record for the SalesOrder I get this error.
"You must have at least one valid line item for this transaction."
I assumed that NetSuite is complaining that there are no more line items to fulfill, so I tried not adding any ItemFulfillmentItem(s) when I set the status to picked, but NetSuite didn't like that either.
The only documentation that I could find referenced a task Id, /app/accounting/transactions/itemshipmanager.nl?type=pack. This approach seemed credible because when I brought up Fiddler, this is the call that it made when I click the "Mark Packed" button in the UI. However, I would prefer not to introduce a different paradigm for talking to the NetSuite server.
I have found that NetSuite will let me go straight to the Pack state if I set shipStatus and shipStatusSpecified in the ItemFulfillment.
Can I move a SalesOrder through both the picked and packed states using only NetSuite SuiteTalk?
I was going about the problem incorrectly. Instead of adding items to a new packed item fulfillment, the correct approach is to find the existing ItemFulfillment and change its status to packed.
This is the search that finds an existing ItemFulfillment for a SalesOrder:
TransactionSearch xactionSearch = new TransactionSearch
{
basic = new TransactionSearchBasic
{
type = new SearchEnumMultiSelectField
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new System.String[]
{
"_itemFulfillment"
}
},
createdFrom = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new RecordRef[1]
{
new RecordRef
{
internalId = salesOrderInternalId,
type = RecordType.salesOrder,
typeSpecified = true
}
}
}
}
};
This is the code that updates the ItemFulfillment:
ItemFulfillment update = new ItemFulfillment { internalId = existing.internalId, shipStatus = status, shipStatusSpecified = true };
WriteResponse res = _service.update(update);