As title, i want to auto confirm shipment when I Create shipment from Sales Order screen by automation step.
Thanks all.
SOShipmentEntry docgraph = PXGraph.CreateInstance<SOShipmentEntry>();
docgraph.Document.Current = docgraph.Document.Search<SOShipment.shipmentNbr>(ShipmentNbr);
foreach (var action in (docgraph.action.GetState(null) as PXButtonState).Menus)
{
if (action.Command == "Confirm Shipment")
{
PXAdapter adapter2 = new PXAdapter(new DummyView(docgraph, docgraph.Document.View.BqlSelect, new List<object> { docgraph.Document.Current }));
adapter2.Menu = action.Command;
docgraph.action.PressButton(adapter2);
TimeSpan timespan;
Exception ex;
while (PXLongOperation.GetStatus(docgraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
{ }
break;
}
}
internal class DummyView : PXView
{
List<object> _Records;
internal DummyView(PXGraph graph, BqlCommand command, List<object> records)
: base(graph, true, command)
{
_Records = records;
}
public override List<object> Select(object[] currents, object[] parameters, object[] searches, string[] sortcolumns, bool[] descendings, PXFilterRow[] filters, ref int startRow, int maximumRows, ref int totalRows)
{
return _Records;
}
}
We had issues with 'confirm shipment' , the above code helped to do that. It loads the shipment document from the shipment number and finds the menu of the graph for 'confirm shipment' and clicks it.
Best option to schedule process Confirm Shipment instead of using Automation Steps functionality.
Related
We have requirement to make Ext. Price editable even after the invoice is released, we wrote logic to make field editable but when we update value we are getting "Project cannot be empty error". We are using Acumatica 2020 R2 Build - 20.207.0012 with Sales Demo database and without any customizations.
Here is a code sample:
private bool IsDisabled(ARInvoice doc)
{
return doc.Released == true
|| doc.Voided == true
|| doc.DocType == ARDocType.SmallCreditWO
|| doc.PendingPPD == true
|| doc.DocType == ARDocType.FinCharge
&& !Base.IsProcessingMode
&& Base.Document.Cache.GetStatus(doc) == PXEntryStatus.Inserted;
}
protected void ARInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var rows= (ARInvoice)e.Row;
if (rows == null)
return;
if (IsDisabled(rows))
{
PXUIFieldAttribute.SetEnabled<ARTran.curyExtPrice>(cache, rows, true);
Base.Document.Cache.AllowUpdate = true;
Base.Transactions.Cache.AllowUpdate = true;
}
}
Trace:
3/16/2021 12:30:41 PM Error:
Error: Updating 'SO Invoice' record raised at least one error. Please review the errors.
Error: 'Project' cannot be empty.
at PX.Data.PXUIFieldAttribute.CommandPreparing(PXCache sender, PXCommandPreparingEventArgs e)
at PX.Data.PXCache.OnCommandPreparing(String name, Object row, Object value, PXDBOperation operation, Type table, FieldDescription& description)
at PX.Data.PXProjectionAttribute.PersistUpdated(PXCache sender, Object row)
at PX.Data.PXCache`1.PersistUpdated(Object row, Boolean bypassInterceptor)
at PX.Data.PXCache`1.Persist(PXDBOperation operation)
at PX.Data.PXGraph.Persist(Type cacheType, PXDBOperation operation)
at PX.Data.PXGraph.Persist()
at PX.Objects.AR.ARInvoiceEntry.Persist()
at PX.Objects.AR.ARInvoiceEntryExternalTax.Persist(Action persist)
at PX.Data.PXSave`1.d__2.MoveNext()
at PX.Data.PXAction`1.d__30.MoveNext()
at PX.Data.PXAction`1.d__30.MoveNext()
at PX.Web.UI.PXBaseDataSource.tryExecutePendingCommand(String viewName, String[] sortcolumns, Boolean[] descendings, Object[] searches, Object[] parameters, PXFilterRow[] filters, DataSourceSelectArguments arguments, Boolean& closeWindowRequired, Int32& adapterStartRow, Int32& adapterTotalRows)
at PX.Web.UI.PXBaseDataSource.ExecuteSelect(String viewName, DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments
I think your best bet is to add that field to your screen and check what the value is:
Then can also try to suppress the error like this:
namespace PX.Objects.SO
{
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{
#region Event Handlers
protected void ARTran_ProjectID_FieldVerifying(PXCache cache, PXFieldVerifyingEventArgs e)
{
e.Cancel = true;
}
#endregion
}
}
Hopefully this will be an easy question!
I've created a new report for the Acumatica Opportunity screen, and I'd like to add it to the screen itself. I've previously added a new report to the existing REPORTS dropdown, but I have not played with Automation Steps enough to create the REPORTS dropdown if it does not already exist.
Can someone provide instructions or point me in the direction of the appropriate documentation?
EDIT: So, what I'm running into is that the Opportunity doesn't have a Reports section already there to add to; I have to create it.
What I have so far gets a REPORTS button on the screen but it's not a dropdown and it doesn't appear to do anything.
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
#region Event Handlers
#endregion
#region Actions
public PXAction<CROpportunity> report;
[PXUIField(DisplayName = "Reports", MapEnableRights = PXCacheRights.Select)]
[PXButton(SpecialType = PXSpecialButtonType.Report)]
protected virtual IEnumerable Report(PXAdapter adapter,
[PXString(8, InputMask = "CC.CC.CC.CC")]
[PXStringList(new string[] { "IOCR6410" }, new string[] { "Quote" })]
string reportID)
{
List<CROpportunity> list = adapter.Get<CROpportunity>().ToList();
if (!String.IsNullOrEmpty(reportID))
{
Base.Save.Press();
int i = 0;
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (CROpportunity opp in list)
{
if (reportID == "IOCR6410")
{
parameters["CROpportunity.OpportunityID" + i.ToString()] = opp.OpportunityID;
}
i++;
}
if (i > 0)
{
throw new PXReportRequiredException(parameters, reportID, string.Format("Report {0}", reportID));
}
}
return list;
}
#endregion
}
For this type of changes, it's better to not make changes in automation step(s), but follow the approach suggested in How to add report to Inventory Transfers dropdown menu for reports
Below is a slightly updated version of your code snippet, which should lead to the following result:
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
public override void Initialize()
{
Report.AddMenuAction(QuoteReport);
Report.MenuAutoOpen = true;
}
public PXAction<CROpportunity> Report;
[PXButton]
[PXUIField(DisplayName = "Reports", MapEnableRights = PXCacheRights.Select)]
protected void report()
{ }
public PXAction<CROpportunity> QuoteReport;
[PXButton]
[PXUIField(DisplayName = "Quote", MapEnableRights = PXCacheRights.Select)]
protected IEnumerable quoteReport(PXAdapter adapter)
{
var reportID = "IOCR6410";
List<CROpportunity> list = adapter.Get<CROpportunity>().ToList();
Base.Save.Press();
int i = 0;
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (CROpportunity opp in list)
{
parameters["CROpportunity.OpportunityID" + i.ToString()] = opp.OpportunityID;
i++;
}
if (i > 0)
{
throw new PXReportRequiredException(parameters, reportID, string.Format("Report {0}", reportID));
}
return list;
}
}
I have added a DAC Extension to ARTran with a field to provide the Discount Unit Price (displayed to the user on the Invoices screen)
public class ARTran_Extension : PXCacheExtension<ARTran>
{
[PXDecimal]
[PXUIField(DisplayName = "Disc Unit Price", Enabled = false)]
[PXDefault(TypeCode.Decimal, "0")]
[PXDBCalced(typeof(Div<Mult<IsNull<ARTran.curyUnitPrice, Zero>, Sub<_100Percents, IsNull<ARTran.discPct, Zero>>>, _100Percents>), typeof(decimal))]
public virtual decimal UsrDiscUnitPrice { get; set; }
public abstract class usrDiscUnitPrice : IBqlField { }
}
Now I'm trying to compose a simple Generic Inquiry that has SOOrder Inner Join SOLine and SOLine Left Join ARTran (and a couple parameters to specify a date range of orders). When I view the inquiry and select a date range that returns records it returns an error: "Error #111: An error occurred while processing the field Disc Unit Price : Object reference not set to an instance of an object."
The Results Grid does not currently reference any fields from the ARTran table. This occurs even when Inner joining ARTran (ensuring there are records from the table).
I systematically eliminated elements and identified when the PXDBCalced attribute is removed the Inquiry runs successfully. I then tried changing it to use a Custom attribute instead and found even when a custom attribute is added, even if it has no functional code within it, the inquiry again fails with that error. Even when there is FieldSelecting event code via the custom attribute, setting a breakpoint within it that event is never reached.
public class ARTranDiscUnitPriceAttribute : PXEventSubscriberAttribute, IPXFieldSelectingSubscriber
{
public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
/*
ARTran artran = (ARTran)e.Row;
if (artran == null) return;
e.ReturnValue = (artran.CuryUnitPrice ?? 0) * (100 - (artran.DiscPct ?? 0)) / 100;
*/
}
}
public class ARTran_Extension : PXCacheExtension<ARTran>
{
[PXDecimal]
[PXUIField(DisplayName = "Disc Unit Price")]
[PXDefault(TypeCode.Decimal, "0")]
[ARTranDiscUnitPrice]
public virtual decimal UsrDiscUnitPrice { get; set; }
public abstract class usrDiscUnitPrice : IBqlField { }
}
Any details or suggestions how to resolve this would be greatly appreciated.
The Trace:
Error #111: An error occurred while processing the field Disc Unit Price : Object reference not set to an instance of an object..
System.NullReferenceException: Object reference not set to an instance of an object.
at _SetValueByOrdinal(ARTran , Int32 , Object , PXCacheExtension[] )
at PX.Data.PXCache`1.SetValueByOrdinal(TNode data, Int32 ordinal, Object value, PXCacheExtension[] extensions)
at PX.Data.PXCache`1.SetValue(Object data, Int32 ordinal, Object value)
at PX.Data.PXDBCalcedAttribute.RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
at PX.Data.PXCache.OnRowSelecting(Object item, PXDataRecord record, Int32& position, Boolean isReadOnly)
at PX.Data.PXCache.OnRowSelecting(Object item, PXDataRecord record, Int32& position, Boolean isReadOnly)
at PX.Data.PXGenericInqGrph.d__9.MoveNext()
at _CustomMethod(Object , Object[] )
at PX.Data.PXView.InvokeDelegate(Object[] parameters)
at PX.Data.PXView.Select(Object[] currents, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows)
at PX.Data.PXProcessingBase`1._SelectRecords(Int32 startRow, Int32 maxRows)
at PX.Data.Maintenance.GI.GIFilteredProcessing._List()
at _CustomMethod(Object , Object[] )
at PX.Data.PXView.InvokeDelegate(Object[] parameters)
at PX.Data.PXView.Select(Object[] currents, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows)
at PX.Data.PXGraph.ExecuteSelect(String viewName, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows)
A super easy fix for this issue: in Acumatica all DAC fields must be of a nullable type, so once you declare your UsrDiscUnitPrice field of the Nullable<decimal> type or the decimal? type, you should be good to go:
public class ARTran_Extension : PXCacheExtension<ARTran>
{
public abstract class usrDiscUnitPrice : IBqlField { }
[PXDecimal]
[PXUIField(DisplayName = "Disc Unit Price")]
[PXDefault(TypeCode.Decimal, "0")]
[ARTranDiscUnitPrice]
public virtual decimal? UsrDiscUnitPrice { get; set; }
}
I added a custom field to SOShipment and I want to set its value when the CreateShipment action is invoked on Order Entry or through Process Orders screens. How do I do that ?
Create a graph extension for SOOrderEntry and add an Action method like this :
using PX.Data;
using System.Collections;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> action;
[PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
[PXButton]
protected virtual IEnumerable Action(PXAdapter adapter,
[PXInt]
[PXIntList(new int[] { 1, 2, 3, 4, 5 }, new string[] { "Create Shipment", "Apply Assignment Rules", "Create Invoice", "Post Invoice to IN", "Create Purchase Order" })]
int? actionID,
[PXDate]
DateTime? shipDate,
[PXSelector(typeof(INSite.siteCD))]
string siteCD,
[SOOperation.List]
string operation,
[PXString()]
string ActionName)
{
//actionID = 1 means the CreateShipment action was the one invoked
if (actionID == 1)
{
PXGraph.InstanceCreated.AddHandler<SOShipmentEntry>((graph) =>
{
graph.RowInserting.AddHandler<SOShipment>((sender, e) =>
{
//Custom logic goes here
var shipment = (SOShipment)e.Row;
var shipmentExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(shipment);
if (Base.Document.Current != null && shipmentExt != null)
{
shipmentExt.UsrPriority = Base.Document.Current.Priority;
}
});
});
}
//calls the basic action that was invoked
return Base.action.Press(adapter);
}
}
}
When any of SOOrderEntry's actions is run (even through the Process Orders screen), this method is invoked. We verify that the action really is CreateShipment with the actionID == 1 and add events handler for SOShipmentEntry graph creation and SOShipment RowInserting. Our custom logic is added inside the RowInserting event.
I have to copy the custom field values from opportunity to sales order while converting the opportunity to sales order.
I have come across a sample code to pass custom field from sales order to shipment and I have tried to use the code for overriding the “create sales order” action.
The following code snippet I have used in OpportunityMaint extension class
public PXAction action;
[PXButton]
[PXUIField(DisplayName="Actions",MapEnableRights=PXCacheRights.Select,MapViewRights=PXCacheRights.Select)]
protected IEnumerable Action( PXAdapter adapter,
[PXIntList(new int[] {1,2,3}, new string[] {"Create Account","Create Sales order","Create Invoice"}),PXInt]
int? actionId,
[PXString]
string ActionName)
{
if(actionId == 2)
{
// Implement So Order row insert handler
}
return Base.Action.Press(adapter);
}
The piece of code is not triggering.
Looking forward for better solution to implement this option
Regards,
R.Muralidharan
You will need to override the CreateSalesOrder action. Below is a snippet of code where I had to push the opportunity down to the sales order.
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
public PXAction<CROpportunity> createSalesOrder;
[PXUIField(DisplayName = Messages.CreateSalesOrder, MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntry)]
public virtual IEnumerable CreateSalesOrder(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<SOOrderEntry>((graph) =>
{
graph.RowInserted.AddHandler<SOOrder>((cache, args) =>
{
var soOrder = (SOOrder)args.Row;
var soOrderExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(soOrder);
foreach (CROpportunity opportunity in adapter.Get())
{
soOrderExt.UsrOpportunityID = opportunity.OpportunityID;
}
});
});
return Base.createSalesOrder.Press(adapter);
}
}