Skip to main content
Question

As part of a new processing screen how can I print 2 copies of the shipment confirmation report to the default printer.


Forum|alt.badge.img+1

I am developing a new processing to process web orders that can be pushed straight through to creating a shipment.  I have the process set up that assigns the owner and creates the shipment but I’m not sure how to automatically send the shipment confirmation report to a specific printer on devicehub.

The code I have which works to the point of creating the shipment and storing the shipment number is below.

using System;
using PX.Data;
using PX.Objects.SO;
using PX.Data.BQL.Fluent;
using PX.TM;
using PX.Common;
using System.Collections.Generic;
using PX.Objects.CR;
using PX.SM;
using PX.Reports;

namespace PPCustomOrderProcess
{

  public class AIProcessOrdersCust : PXGraph<AIProcessOrdersCust>
  {

    public class wOOrderType: PX.Data.BQL.BqlString.Constant<wOOrderType> {
      public wOOrderType() : base("WO") { }
    }

    public class paymentMethodAFFIRM: PX.Data.BQL.BqlString.Constant<paymentMethodAFFIRM> {
      public paymentMethodAFFIRM() : base("AFFIRM") { }
    }

    public class paymentMethodAFTERPAY: PX.Data.BQL.BqlString.Constant<paymentMethodAFTERPAY> {
      public paymentMethodAFTERPAY() : base("AFTERPAY") { }
    }

    public class paymentMethodPAYPAL: PX.Data.BQL.BqlString.Constant<paymentMethodPAYPAL> {
      public paymentMethodPAYPAL() : base("PAYPAL") { }
    }

    public class paymentMethodCCARD: PX.Data.BQL.BqlString.Constant<paymentMethodCCARD> {
      public paymentMethodCCARD() : base("CCARD") { }
    }

    public class dropShipStart: PX.Data.BQL.BqlString.Constant<dropShipStart> {
      public dropShipStart() : base("DS%") { }
    }

    public class signatureWaived: PX.Data.BQL.BqlString.Constant<signatureWaived> {
      public signatureWaived() : base("%SIGNATURE WAIVED%") { }
    }

    public class orderTotalMaxSpend: PX.Data.BQL.BqlDecimal.Constant<orderTotalMaxSpend> {
      public orderTotalMaxSpend() : base(500m) { }
    }

    public PXCancel<SOOrder> Cancel;

    // Create an alias for clarity
    public class OuterSOOrder : SOOrder { }

    public SelectFrom<SOOrder>
      .LeftJoin<Note>.On<SOOrder.noteID.IsEqual<Note.noteID>>
      .Where<
          SOOrder.orderType.IsEqual<wOOrderType>
          .And<
            SOOrder.shipVia.IsNotLike<dropShipStart>
          >
          .And<
            SOOrder.status.IsEqual<SOOrderStatus.open>
            .Or<
              SOOrder.status.IsEqual<SOOrderStatus.awaitingPayment>
            >
          >
          .And<
            Note.noteID.IsNull
            .Or<Note.noteText.IsNull>
            .Or<Note.noteText.IsEqual<Empty>>
            .Or<Note.noteText.IsLike<signatureWaived>>
          >
          .And<
            SOOrder.curyOrderTotal.IsLessEqual<orderTotalMaxSpend>
          >
          .And<
              SOOrder.paymentMethodID.IsEqual<paymentMethodAFFIRM>
              .Or<SOOrder.paymentMethodID.IsEqual<paymentMethodPAYPAL>>
              .Or<SOOrder.paymentMethodID.IsEqual<paymentMethodAFTERPAY>>
              .Or<
                  SOOrder.paymentMethodID.IsEqual<paymentMethodCCARD>
                  .And<
                      Exists<SelectFrom<SOOrder>
                          .Where<
                              SOOrder.paymentMethodID.IsEqual<paymentMethodCCARD>
                              .And<
                                  SOOrder.customerID.IsEqual<OuterSOOrder.customerID>
                              >
                              .And<
                                  SOOrder.status.IsEqual<SOOrderStatus.shipping> // Change shipping to completed before final publish
                              >
                          >
                      >
                  >
              >
          >
      >
      .ProcessingView AISOOrder;

    public AIProcessOrdersCust()
    {
      AISOOrder.SetProcessCaption("Assign");
      AISOOrder.SetProcessAllCaption("Assign All");

      AISOOrder.SetProcessDelegate(ProcessOrder);
    }

    public PXFilter<DetailsTable> DetailsView;

    [Serializable]
    public class DetailsTable : IBqlTable
    {

    }

    /// <summary>
    /// Processing Method to Assign Owner to Logged-in User
    /// </summary>
    private static void ProcessOrder(SOOrder order)
    {
        var graph = PXGraph.CreateInstance<SOOrderEntry>();

        // Select the order
        graph.Document.Current = graph.Document.Search<SOOrder.orderNbr>(order.OrderNbr, order.OrderType);

        if (graph.Document.Current != null)
        {
            int? currentUserContactID = PXAccess.GetContactID();
            if (currentUserContactID != null)
            {
                graph.Document.Cache.SetValueExt<SOOrder.ownerID>(graph.Document.Current, currentUserContactID);
                graph.Document.Cache.MarkUpdated(graph.Document.Current);

                // Log the new OwnerID for verification
                PXTrace.WriteInformation($"Setting OwnerID to {currentUserContactID} for Order {order.OrderNbr}");

                // Save the changes
                graph.Actions.PressSave();

                // Create a shipment for the order

                bool shipmentCreated = CreateShipmentFromButton(graph);

                // Confirm the shipmnent is created before pressing the new button

                if (shipmentCreated)
                {
                    // Retrieve the Shipment Number
                    var shipmentNbr = GetShipmentNumber(order);

                    if (!string.IsNullOrEmpty(shipmentNbr))
                    {
                      // Load shipment graph and print the shipment confirmation
                      var shipmentGraph = PXGraph.CreateInstance<SOShipmentEntry>();
                      var shipment = shipmentGraph.Document.Search<SOShipment.shipmentNbr>(shipmentNbr);

                      if (shipment != null)
                      {
                        bool printedShipmentConfirmation = PrintShipmentConfirmation(shipmentGraph, shipmentNbr);

                        if (printedShipmentConfirmation)
                        {
                          PXProcessing<SOOrder>.SetInfo($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID} and shipment {shipmentNbr} printed.");
                        }
                        else
                        {
                          PXProcessing<SOOrder>.SetInfo($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID} and shipment {shipmentNbr} created but not printed.");
                        }
                      }
                      else
                      {
                        PXProcessing<SOOrder>.SetError($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID}, but shipment could not be retrieved to print.");
                      }
                    }
                    else
                    {
                        PXProcessing<SOOrder>.SetWarning($"Shipment created, but no shipment number found for Order {order.OrderNbr}.");
                    }
                }
                else
                {
                    PXProcessing<SOOrder>.SetError($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID}, but shipment could not be created.");
                }
            }
            else
            {
                PXProcessing<SOOrder>.SetError($"Failed to assign OwnerID: No ContactID found for current user.");
            }
        }
        else
        {
            PXProcessing<SOOrder>.SetError($"Order {order.OrderNbr} not found.");
        }
    }

    private static bool PrintShipmentConfirmation(SOShipmentEntry shipmentGraph, string shipmentNbr)
    {
      bool success = false;

      if (string.IsNullOrEmpty(shipmentNbr))
      {
        throw new PXException("Shipment Number is required for printing.");
      }

      try
      {
          //////   THIS IS WHERE I AM STUCK ON HOW TO PRINT TO THE PRINTER AUTOMATICALLY    //////

          PXTrace.WriteInformation($"Shipment Confirmation for {shipmentNbr} has been sent to the printer '{printerName}'.");
          success = true;
      }
      catch (Exception ex)
      {
          PXTrace.WriteError($"Failed to print Shipment Confirmation for {shipmentNbr}. Error: {ex.Message}");
          throw new PXException($"Failed to print Shipment Confirmation for {shipmentNbr}. Please check the printer configuration.");
      }

      return success;

    }

    /// <summary>
    /// Retrieves the Shipment Number associated with the order.
    /// </summary>
    private static string GetShipmentNumber(SOOrder order)
    {
        var shipment = PXSelect<SOOrderShipment,
            Where<SOOrderShipment.orderType, Equal<Required<SOOrderShipment.orderType>>,
                And<SOOrderShipment.orderNbr, Equal<Required<SOOrderShipment.orderNbr>>>>>
            .SelectWindowed(new PXGraph(), 0, 1, order.OrderType, order.OrderNbr);

        return shipment != null ? ((SOOrderShipment)shipment).ShipmentNbr : null;
    }

    private static bool CreateShipmentFromButton(SOOrderEntry graph)
    {

          // Trigger the "Create Shipment" action on the graph (simulate button click)
          PXAction<SOOrder> createShipmentAction = graph.Actions["CreateShipment"] as PXAction<SOOrder>;
          if (createShipmentAction != null)
          {
              // If the button exists, press it programmatically
              PXTrace.WriteInformation($"Pressing button for create shipment");

              createShipmentActionPress(graph);

              return true;
          }
          else
          {
              PXTrace.WriteError("Create Shipment action not found.");
          }
          return false;

    }

    private static void createShipmentActionPress(SOOrderEntry graph)
    {
        try
        {
            PXAction<SOOrder> createShipmentAction = graph.Actions["CreateShipment"] as PXAction<SOOrder>;
            createShipmentAction?.Press();
        }
        catch (Exception ex)
        {
            PXTrace.WriteError("Error pressing Create Shipment button: " + ex.Message);
        }
    }

  }
}

Thanks for any advice,

 

Phil

2 replies

Forum|alt.badge.img+1
  • Author
  • Semi-Pro I
  • 134 replies
  • January 9, 2025

I can now print to a specific named printer on the device hub.  Code is below:

 

using System;
using PX.Data;
using PX.Objects.SO;
using PX.Data.BQL.Fluent;
using PX.TM;
using PX.Common;
using System.Collections.Generic;
using PX.Objects.CR;
using PX.SM;
using PX.Reports;
using System.Threading.Tasks;
using System.Threading;

namespace PPCustomOrderProcess
{

  public class AIProcessOrdersCust : PXGraph<AIProcessOrdersCust>
  {

    [PXString]
    [PXUIField(DisplayName = "Printer")]
    [PXSelector(
        typeof(Search<SMPrinter.printerID>),
        typeof(SMPrinter.printerName),
        typeof(SMPrinter.description),
        SubstituteKey = typeof(SMPrinter.printerName),
        DescriptionField = typeof(SMPrinter.description))]
    public virtual Guid? SelectedPrinter { get; set; }
    public abstract class selectedPrinter : PX.Data.BQL.BqlGuid.Field<selectedPrinter> { }

    public class wOOrderType: PX.Data.BQL.BqlString.Constant<wOOrderType> {
      public wOOrderType() : base("WO") { }
    }

    public class paymentMethodAFFIRM: PX.Data.BQL.BqlString.Constant<paymentMethodAFFIRM> {
      public paymentMethodAFFIRM() : base("AFFIRM") { }
    }

    public class paymentMethodAFTERPAY: PX.Data.BQL.BqlString.Constant<paymentMethodAFTERPAY> {
      public paymentMethodAFTERPAY() : base("AFTERPAY") { }
    }

    public class paymentMethodPAYPAL: PX.Data.BQL.BqlString.Constant<paymentMethodPAYPAL> {
      public paymentMethodPAYPAL() : base("PAYPAL") { }
    }

    public class paymentMethodCCARD: PX.Data.BQL.BqlString.Constant<paymentMethodCCARD> {
      public paymentMethodCCARD() : base("CCARD") { }
    }

    public class dropShipStart: PX.Data.BQL.BqlString.Constant<dropShipStart> {
      public dropShipStart() : base("DS%") { }
    }

    public class signatureWaived: PX.Data.BQL.BqlString.Constant<signatureWaived> {
      public signatureWaived() : base("%SIGNATURE WAIVED%") { }
    }

    public class intlShipVia: PX.Data.BQL.BqlString.Constant<intlShipVia> {
      public intlShipVia() : base("%intl%") { }
    }

    public class orderTotalMaxSpend: PX.Data.BQL.BqlDecimal.Constant<orderTotalMaxSpend> {
      public orderTotalMaxSpend() : base(500m) { }
    }

    public PXCancel<SOOrder> Cancel;

  // Create an alias for clarity
  public class OuterSOOrder : SOOrder { }

  public SelectFrom<SOOrder>
    .LeftJoin<Note>.On<SOOrder.noteID.IsEqual<Note.noteID>>
    .Where<
        SOOrder.orderType.IsEqual<wOOrderType>
        .And<
          SOOrder.shipVia.IsNotLike<intlShipVia>
        >
        .And<
          SOOrder.shipVia.IsNotLike<dropShipStart>
        >
        .And<
          SOOrder.status.IsEqual<SOOrderStatus.open>
        >
        .And<
          Note.noteID.IsNull
          .Or<Note.noteText.IsNull>
          .Or<Note.noteText.IsEqual<Empty>>
          .Or<Note.noteText.IsLike<signatureWaived>>
        >
        .And<
          SOOrder.curyOrderTotal.IsLessEqual<orderTotalMaxSpend>
        >
        .And<
            SOOrder.paymentMethodID.IsEqual<paymentMethodAFFIRM>
            .Or<SOOrder.paymentMethodID.IsEqual<paymentMethodPAYPAL>>
            .Or<SOOrder.paymentMethodID.IsEqual<paymentMethodAFTERPAY>>
            .Or<
                SOOrder.paymentMethodID.IsEqual<paymentMethodCCARD>
                .And<
                    Exists<SelectFrom<SOOrder>
                        .Where<
                            SOOrder.paymentMethodID.IsEqual<paymentMethodCCARD>
                            .And<
                                SOOrder.customerID.IsEqual<OuterSOOrder.customerID>
                            >
                            .And<
                                SOOrder.status.IsEqual<SOOrderStatus.shipping> // Change shipping to completed before final publish
                            >
                        >
                    >
                >
            >
        >
    >
    .ProcessingView AISOOrder;


    public AIProcessOrdersCust()
    {
      AISOOrder.SetProcessCaption("Assign");
      AISOOrder.SetProcessAllCaption("Assign All");

      AISOOrder.SetProcessDelegate(ProcessOrder);
    }

    public PXFilter<DetailsTable> DetailsView;

    [Serializable]
    public class DetailsTable : IBqlTable
    {

    }

    /// <summary>
    /// Processing Method to Assign Owner to Logged-in User
    /// </summary>
    private static void ProcessOrder(SOOrder order)
    {
        var graph = PXGraph.CreateInstance<SOOrderEntry>();

        // Select the order
        graph.Document.Current = graph.Document.Search<SOOrder.orderNbr>(order.OrderNbr, order.OrderType);

        if (graph.Document.Current != null)
        {
            int? currentUserContactID = PXAccess.GetContactID();
            if (currentUserContactID != null)
            {
                graph.Document.Cache.SetValueExt<SOOrder.ownerID>(graph.Document.Current, currentUserContactID);
                graph.Document.Cache.MarkUpdated(graph.Document.Current);

                // Log the new OwnerID for verification
                PXTrace.WriteInformation($"Setting OwnerID to {currentUserContactID} for Order {order.OrderNbr}");

                // Save the changes
                graph.Actions.PressSave();

                // Create a shipment for the order

                bool shipmentCreated = CreateShipmentFromButton(graph);

                // Confirm the shipmnent is created before pressing the new button

                if (shipmentCreated)
                {
                    // Retrieve the Shipment Number
                    var shipmentNbr = GetShipmentNumber(order);

                    if (!string.IsNullOrEmpty(shipmentNbr))
                    {
                      // Load shipment graph and print the shipment confirmation
                      var shipmentGraph = PXGraph.CreateInstance<SOShipmentEntry>();
                      var shipment = shipmentGraph.Document.Search<SOShipment.shipmentNbr>(shipmentNbr);

                      if (shipment != null)
                      {
                        bool printedShipmentConfirmation = PrintShipmentConfirmation(shipmentGraph, shipmentNbr);

                        if (printedShipmentConfirmation)
                        {
                          PXProcessing<SOOrder>.SetInfo($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID} and shipment {shipmentNbr} printed.");
                        }
                        else
                        {
                          PXProcessing<SOOrder>.SetInfo($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID} and shipment {shipmentNbr} created but not printed.");
                        }
                      }
                      else
                      {
                        PXProcessing<SOOrder>.SetError($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID}, but shipment could not be retrieved to print.");
                      }
                    }
                    else
                    {
                        PXProcessing<SOOrder>.SetWarning($"Shipment created, but no shipment number found for Order {order.OrderNbr}.");
                    }
                }
                else
                {
                    PXProcessing<SOOrder>.SetError($"Order {order.OrderNbr} successfully assigned to user with ContactID: {currentUserContactID}, but shipment could not be created.");
                }
            }
            else
            {
                PXProcessing<SOOrder>.SetError($"Failed to assign OwnerID: No ContactID found for current user.");
            }
        }
        else
        {
            PXProcessing<SOOrder>.SetError($"Order {order.OrderNbr} not found.");
        }
    }


    private static Guid GetPrinterIdByName(string printerName, PXGraph printGraph)
    {
        // Query the SMPrinter table for a printer with the specified printerName
        var printer = PXSelect<SMPrinter,
                                Where<SMPrinter.printerName, Equal<Required<SMPrinter.printerName>>>>.Select(printGraph, printerName);

        if (printer != null && printer.Count > 0)
        {
            SMPrinter smPrinter = printer[0];

            if (smPrinter.PrinterID.HasValue)
            {
                return smPrinter.PrinterID.Value; // Return the PrinterID as a non-nullable Guid
            }
            else
            {
                // Handle the case where PrinterID is null
                throw new Exception($"Printer '{printerName}' has a null PrinterID.");
            }
        }

        // Handle the case where the printer wasn't found
        throw new Exception($"Printer '{printerName}' not found.");
    }

    private static bool PrintShipmentConfirmation(SOShipmentEntry shipmentGraph, string shipmentNbr)
    {
      bool success = false;

      if (string.IsNullOrEmpty(shipmentNbr))
      {
        throw new PXException("Shipment Number is required for printing.");
      }

      try
      {
          const string reportID = "SO642000";
          const string description = "Shipment Confirmation";

          var parametersDictionary = new Dictionary<string, string>
          {
              { "ShipmentNbr", shipmentNbr },
              // Add other parameters if necessary
          };

          PXGraph printGraph = PXGraph.CreateInstance<SMPrintJobMaint>();
          // Step 2: Define the print settings
          PrintSettings printSettings = new PrintSettings()
          {
              PrinterID = GetPrinterIdByName("W2PDFHUB", printGraph), // Replace with logic to get the printer ID from field on processing screen
              NumberOfCopies = 1,
              PrintWithDeviceHub = true,
              DefinePrinterManually = true
          };

          // Step 3: Create the print job
          PXGraph.CreateInstance<SMPrintJobMaint>().CreatePrintJob(printSettings, reportID, parametersDictionary, "Shipment Confirmation", CancellationToken.None);

          // Log or handle any post-print actions
          PXTrace.WriteInformation("Shipment Confirmation print job initiated.");

      }
      catch (Exception ex)
      {
          PXTrace.WriteError($"Failed to print Shipment Confirmation for {shipmentNbr}. Error: {ex.Message}");
          throw new PXException($"Failed to print Shipment Confirmation for {shipmentNbr}. Please check the printer configuration.");
      }
      finally
      {
        success = true;
      }    
      return success;
    }

    /// <summary>
    /// Retrieves the Shipment Number associated with the order.
    /// </summary>
    private static string GetShipmentNumber(SOOrder order)
    {
        var shipment = PXSelect<SOOrderShipment,
            Where<SOOrderShipment.orderType, Equal<Required<SOOrderShipment.orderType>>,
                And<SOOrderShipment.orderNbr, Equal<Required<SOOrderShipment.orderNbr>>>>>
            .SelectWindowed(new PXGraph(), 0, 1, order.OrderType, order.OrderNbr);

        return shipment != null ? ((SOOrderShipment)shipment).ShipmentNbr : null;
    }

    private static bool CreateShipmentFromButton(SOOrderEntry graph)
    {

          // Trigger the "Create Shipment" action on the graph (simulate button click)
          PXAction<SOOrder> createShipmentAction = graph.Actions["CreateShipment"] as PXAction<SOOrder>;
          if (createShipmentAction != null)
          {
              // If the button exists, press it programmatically
              PXTrace.WriteInformation($"Pressing button for create shipment");

              createShipmentActionPress(graph);

              return true;
          }
          else
          {
              PXTrace.WriteError("Create Shipment action not found.");
          }
          return false;

    }

    private static void createShipmentActionPress(SOOrderEntry graph)
    {
        try
        {
            PXAction<SOOrder> createShipmentAction = graph.Actions["CreateShipment"] as PXAction<SOOrder>;
            createShipmentAction?.Press();
        }
        catch (Exception ex)
        {
            PXTrace.WriteError("Error pressing Create Shipment button: " + ex.Message);
        }
    }

  }
}

I’m now trying to get a field in the top of the processing screen that lists all printers on the devicehub and allows the user to pick one to print to when processing the Orders.

Any help or advice from anyone on how to do that would be gratefully received,

Phil


Chris Hackett
Community Manager
Forum|alt.badge.img
  • Acumatica Community Manager
  • 2810 replies
  • April 24, 2025

Hi ​@ppowell were you able to find a solution? Thank you!


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings