Jerry Kurtz
Jerry Kurtz

Reputation: 122

Create Quick Process like Action Button that creates shipment, confirm shipment, update IN, invoice, and release, all in one step

Hopefully I am missing something easy. This is on 2019R2.

I've added a custom button to my SO form with the idea that it's similar to the Quick Process button. The steps I need to take on the current SO are:

  1. Create a shipment containing all lines on the order
  2. Confirm Shipment
  3. Update IN
  4. Create Invoice
  5. Release Invoice

I'm not having a lot of luck getting it to finish properly. I've gotten steps 1 and 2 to work, step 3 seems to work (I don't get an error), but I never get an invoice for the shipment from step 4 (just the error below). Haven't even added step 5 yet.

Error: Another process has updated the 'SOShipment' record. Your changes will be lost.

Here's the implementation code:

        public PXAction<PX.Objects.SO.SOOrder> AIquickProcess;

        [PXButton(CommitChanges = true)]
        [PXUIField(DisplayName = "Phone Quick Process")]
        protected void aIquickProcess()
        {

            SOOrder so = Base.CurrentDocument.Current;
            if (so == null) return;
            if (so.OrderType != "SO" && so.OrderType != "TR") return;
            SOOrder ordercopy = (SOOrder)Base.Caches[typeof(SOOrder)].CreateCopy(so);

            SOShipmentEntry shipmentEntryGraph = PXGraph.CreateInstance<SOShipmentEntry>();
            DocumentList<SOShipment> shipmentDocs = new DocumentList<SOShipment>(shipmentEntryGraph);

            SOLine line = PXSelect<SOLine,
                Where<SOLine.orderType, Equal<Required<SOLine.orderType>>,
                And<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>>>>
                .Select(Base, so.OrderType, so.OrderNbr);

            if (line == null) return;

            DateTime? shipDate = so.ShipDate == null ? so.OrderDate : so.ShipDate;

            try
            {
                TimeSpan timespan;
                Exception ex;

                // Create shipment
                shipmentEntryGraph.CreateShipment(so, line.SiteID, shipDate, false, line.Operation, shipmentDocs, PXQuickProcess.ActionFlow.NoFlow);
                shipmentEntryGraph.CurrentDocument.Current.ControlQty = shipmentEntryGraph.CurrentDocument.Current.ShipmentQty;
                shipmentEntryGraph.CurrentDocument.Update(shipmentEntryGraph.CurrentDocument.Current);
                shipmentEntryGraph.Save.Press();
                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Confirm Shipment
                PXAutomation.CompleteSimple(shipmentEntryGraph.Document.View);

                PXAdapter adapter2 = new PXAdapter(new DummyView(shipmentEntryGraph, shipmentEntryGraph.Document.View.BqlSelect,
                                         new List<object> { shipmentEntryGraph.Document.Current }));

                adapter2.Menu = SOShipmentEntryActionsAttribute.Messages.ConfirmShipment;
                adapter2.Arguments = new Dictionary<string, object>
                    {
                            {"actionID", SOShipmentEntryActionsAttribute.ConfirmShipment}
                    };

                adapter2.Searches = new object[] { shipmentEntryGraph.Document.Current.ShipmentNbr };
                adapter2.SortColumns = new[] { "ShipmentNbr" };

                shipmentEntryGraph.action.PressButton(adapter2);

                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Update IN
                shipmentEntryGraph.UpdateIN.Press();
                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                shipmentEntryGraph.CurrentDocument.UpdateCurrent();

                // create invoice
                adapter2 = new PXAdapter(new DummyView(shipmentEntryGraph, shipmentEntryGraph.Document.View.BqlSelect,
                                         new List<object> { shipmentEntryGraph.Document.Current }));

                adapter2.Menu = SOShipmentEntryActionsAttribute.Messages.CreateInvoice;
                adapter2.Arguments = new Dictionary<string, object>
                    {
                            {"actionID", SOShipmentEntryActionsAttribute.CreateInvoice}
                    };

                adapter2.Searches = new object[] { shipmentEntryGraph.Document.Current.ShipmentNbr };
                adapter2.SortColumns = new[] { "ShipmentNbr" };

                shipmentEntryGraph.action.PressButton(adapter2);

                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                // getting: Error: Another process has updated the 'SOShipment' record. Your changes will be lost.
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

            }
            catch (SOShipmentException ex)
            {
                Base.Caches[typeof(SOOrder)].RestoreCopy(so, ordercopy);
                throw;
            }
            catch (Exception ex)
            {
                Base.Caches[typeof(SOOrder)].RestoreCopy(so, ordercopy);
                shipmentEntryGraph.Clear();
                throw;
            }
        }

Here's the code for the DummyView

        public 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;
            }
        }

Here is a version that sometimes works to create and release the invoice, but I still get the same error. I've noticed that I get the error on the UpdateIN.Press() step when the inventory item would go negative (which is allowed).

        public PXAction<PX.Objects.SO.SOOrder> AIquickProcess;

        [PXButton(CommitChanges = true)]
        [PXUIField(DisplayName = "Phone Quick Process")]
        protected void aIquickProcess()
        {

            SOOrder so = Base.CurrentDocument.Current;
            if (so == null) return;
            if (so.OrderType != "SO" && so.OrderType != "TR") return;
            SOOrder ordercopy = (SOOrder)Base.Caches[typeof(SOOrder)].CreateCopy(so);

            SOShipmentEntry shipmentEntryGraph = PXGraph.CreateInstance<SOShipmentEntry>();
            DocumentList<SOShipment> shipmentDocs = new DocumentList<SOShipment>(shipmentEntryGraph);

            SOLine line = PXSelect<SOLine,
                Where<SOLine.orderType, Equal<Required<SOLine.orderType>>,
                And<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>>>>
                .Select(Base, so.OrderType, so.OrderNbr);

            if (line == null) return;

            DateTime? shipDate = so.ShipDate == null ? so.OrderDate : so.ShipDate;

            try
            {
                TimeSpan timespan;
                Exception ex;

                // Create shipment
                shipmentEntryGraph.CreateShipment(so, line.SiteID, shipDate, false, line.Operation, shipmentDocs, PXQuickProcess.ActionFlow.NoFlow);
                shipmentEntryGraph.CurrentDocument.Current.ControlQty = shipmentEntryGraph.CurrentDocument.Current.ShipmentQty;
                shipmentEntryGraph.CurrentDocument.Update(shipmentEntryGraph.CurrentDocument.Current);
                shipmentEntryGraph.Save.Press();
                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Confirm Shipment
                PXAutomation.CompleteSimple(shipmentEntryGraph.Document.View);

                PXAdapter adapter2 = new PXAdapter(new DummyView(shipmentEntryGraph, shipmentEntryGraph.Document.View.BqlSelect,
                                         new List<object> { shipmentEntryGraph.Document.Current }));

                adapter2.Menu = SOShipmentEntryActionsAttribute.Messages.ConfirmShipment;
                adapter2.Arguments = new Dictionary<string, object>
                    {
                            {"actionID", SOShipmentEntryActionsAttribute.ConfirmShipment}
                    };

                adapter2.Searches = new object[] { shipmentEntryGraph.Document.Current.ShipmentNbr };
                adapter2.SortColumns = new[] { "ShipmentNbr" };

                shipmentEntryGraph.action.PressButton(adapter2);

                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;


                // Update IN
                PXAutomation.CompleteSimple(shipmentEntryGraph.Document.View);

                shipmentEntryGraph.UpdateIN.Press();
                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Create and release invoice
                SOInvoiceEntry invoiceEntryGraph = PXGraph.CreateInstance<SOInvoiceEntry>();
                DocumentList<ARInvoice, SOInvoice> created = new ShipmentInvoices(shipmentEntryGraph);

                shipmentEntryGraph.SelectTimeStamp();
                invoiceEntryGraph.SelectTimeStamp();

                PXProcessing<SOShipment>.SetCurrentItem(shipmentEntryGraph.CurrentDocument.Current);
                shipmentEntryGraph.InvoiceShipment(invoiceEntryGraph, shipmentEntryGraph.CurrentDocument.Current, Base.Accessinfo.BusinessDate.Value, created, PXQuickProcess.ActionFlow.NoFlow);
                invoiceEntryGraph.CurrentDocument.Current.CreditHold = false;
                invoiceEntryGraph.Save.Press();
                invoiceEntryGraph.release.Press();
            }
            catch (SOShipmentException ex)
            {
                Base.Caches[typeof(SOOrder)].RestoreCopy(so, ordercopy);
                throw;
            }
            catch (Exception ex)
            {
                Base.Caches[typeof(SOOrder)].RestoreCopy(so, ordercopy);
                shipmentEntryGraph.Clear();
                throw;
            }
        }

Upvotes: 0

Views: 212

Answers (1)

Jerry Kurtz
Jerry Kurtz

Reputation: 122

Since I need to invoice in the same step, I don't think the call to Update IN is necessary, and when taking that out, it seems to work fairly reliably.

What I'm currently using is below and I'm open to some feedback on how to make it better.

        public PXAction<PX.Objects.SO.SOOrder> AIquickProcess;

        [PXButton(CommitChanges = true)]
        [PXUIField(DisplayName = "Phone Quick Process")]
        protected void aIquickProcess()
        {

            SOOrder so = Base.CurrentDocument.Current;
            if (so == null) return;
            if (so.OrderType != "SO" && so.OrderType != "TR") return;
            SOOrder ordercopy = (SOOrder)Base.Caches[typeof(SOOrder)].CreateCopy(so);

            SOShipmentEntry shipmentEntryGraph = PXGraph.CreateInstance<SOShipmentEntry>();
            DocumentList<SOShipment> shipmentDocs = new DocumentList<SOShipment>(shipmentEntryGraph);

            SOLine line = PXSelect<SOLine,
                Where<SOLine.orderType, Equal<Required<SOLine.orderType>>,
                And<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>>>>
                .Select(Base, so.OrderType, so.OrderNbr);

            if (line == null) return;

            DateTime? shipDate = so.ShipDate == null ? so.OrderDate : so.ShipDate;

            try
            {
                TimeSpan timespan;
                Exception ex;

                // Create shipment
                shipmentEntryGraph.CreateShipment(so, line.SiteID, shipDate, false, line.Operation, shipmentDocs, PXQuickProcess.ActionFlow.NoFlow);
                shipmentEntryGraph.CurrentDocument.Current.ControlQty = shipmentEntryGraph.CurrentDocument.Current.ShipmentQty;
                shipmentEntryGraph.CurrentDocument.Update(shipmentEntryGraph.CurrentDocument.Current);
                shipmentEntryGraph.Save.Press();
                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Confirm Shipment
                PXAutomation.CompleteSimple(shipmentEntryGraph.Document.View);

                PXAdapter adapter2 = new PXAdapter(new DummyView(shipmentEntryGraph, shipmentEntryGraph.Document.View.BqlSelect,
                                         new List<object> { shipmentEntryGraph.Document.Current }));

                adapter2.Menu = SOShipmentEntryActionsAttribute.Messages.ConfirmShipment;
                adapter2.Arguments = new Dictionary<string, object>
                    {
                            {"actionID", SOShipmentEntryActionsAttribute.ConfirmShipment}
                    };

                adapter2.Searches = new object[] { shipmentEntryGraph.Document.Current.ShipmentNbr };
                adapter2.SortColumns = new[] { "ShipmentNbr" };

                shipmentEntryGraph.action.PressButton(adapter2);

                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Update IN
                //PXAutomation.CompleteSimple(shipmentEntryGraph.Document.View);

                //shipmentEntryGraph.UpdateIN.Press();
                //while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                //{ }
                //if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                // Create and release invoice
                SOInvoiceEntry invoiceEntryGraph = PXGraph.CreateInstance<SOInvoiceEntry>();
                DocumentList<ARInvoice, SOInvoice> created = new ShipmentInvoices(shipmentEntryGraph);

                shipmentEntryGraph.SelectTimeStamp();
                invoiceEntryGraph.SelectTimeStamp();

                PXProcessing<SOShipment>.SetCurrentItem(shipmentEntryGraph.CurrentDocument.Current);
                shipmentEntryGraph.InvoiceShipment(invoiceEntryGraph, shipmentEntryGraph.CurrentDocument.Current, Base.Accessinfo.BusinessDate.Value, created, PXQuickProcess.ActionFlow.NoFlow);
                while (PXLongOperation.GetStatus(shipmentEntryGraph.UID, out timespan, out ex) == PXLongRunStatus.InProcess)
                { }
                if (ex != null && ex.Message.ToLower() != "nothing in progress" && ex.Message.ToLower() != "the operation has completed.") throw ex;

                invoiceEntryGraph.CurrentDocument.Current.CreditHold = false;
                invoiceEntryGraph.Save.Press();
                invoiceEntryGraph.release.Press();
            }
            catch (SOShipmentException ex)
            {
                Base.Caches[typeof(SOOrder)].RestoreCopy(so, ordercopy);
                throw;
            }
            catch (Exception ex)
            {
                Base.Caches[typeof(SOOrder)].RestoreCopy(so, ordercopy);
                shipmentEntryGraph.Clear();
                throw;
            }
        }
        public 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;
            }
        }

Upvotes: 0

Related Questions