Skip to main content
Question

Silent Label Printing via DeviceHub in WMS (ReceivePutAway) – Not Working in 2025 R2

  • April 11, 2026
  • 1 reply
  • 26 views

We are implementing a custom WMS scan mode in the Receive Put Away screen to print labels automatically after scanning a PO Receipt, Inventory Item

 

Requirement:
We need to print labels silently (without opening the report UI) using Device Hub when the user scan the Item.

 

Environment:

  • Version: Acumatica 2025 R2 (25.200.0248)
  • Module: WMS (Receive Put Away) 

 

Code is :

 

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

using PX.Common;
using PX.Data;
using PX.Data.BQL;
using PX.Data.BQL.Fluent;
using PX.BarcodeProcessing;

using PX.Objects.AP;
using PX.Objects.IN;
using PX.Objects.IN.WMS;
using PX.SM;
using PX.Objects.Common;

namespace PX.Objects.PO.WMS
{
    using static PX.BarcodeProcessing.BarcodeDrivenStateMachine<ReceivePutAway, ReceivePutAway.Host>;
    using static PX.Objects.IN.WMS.INScanWarehousePath.ScanPathMode;
    using WMSBase = WarehouseManagementSystem<ReceivePutAway, ReceivePutAway.Host>;

    public class ReceivePutAwayExts : ReceivePutAway.ScanExtension
    {
        public static bool IsActive() => true;

        public sealed class PrintMode : ReceivePutAway.ScanMode
        {
            public const string Value = "PRTLBL";

            public override string Code => Value;
            public override string Description => Msg.Description;

            #region State Machine
            protected override IEnumerable<ScanState<ReceivePutAway>> CreateStates()
            {
                yield return new PrintMode.POReceiptState();
                yield return new PrintMode.InventoryItemState().Intercept.HandleAbsence.ByAppend((basis, barcode) =>
                {
                    if (basis.TryProcessBy<PrintMode.POReceiptState>(barcode,
                        StateSubstitutionRule.KeepPositiveReports |
                        StateSubstitutionRule.KeepApplication))
                        return AbsenceHandling.Done;
                    return AbsenceHandling.Skipped;
                });
                yield return new PrintMode.PrintQtyState();
                yield return new PrintMode.ConfirmState();

            }

            protected override IEnumerable<ScanTransition<ReceivePutAway>> CreateTransitions()
            {
                return StateFlow(flow => flow
                            .From<PrintMode.POReceiptState>()
                            .NextTo<PrintMode.InventoryItemState>()
                            .NextTo<PrintMode.PrintQtyState>());
            }

            protected override IEnumerable<ScanRedirect<ReceivePutAway>> CreateRedirects() => AllWMSRedirects.CreateFor<ReceivePutAway>();

            protected override void ResetMode(bool fullReset)
            {
                base.ResetMode(fullReset);

                Clear<PrintMode.POReceiptState>(when: fullReset);
                Clear<PrintMode.InventoryItemState>();
                Clear<PrintMode.PrintQtyState>();

                if (fullReset)
                {
                    Basis.RefNbr = null;
                    Basis.InventoryID = null;
                    Basis.Qty = null;

                    Basis.NoteID = null;
                }
            }
            #endregion

            #region States

            public sealed class POReceiptState : WMSBase.RefNbrState<POReceipt>
            {
                protected override string StatePrompt => Msg.Prompt;

                protected override POReceipt GetByBarcode(string barcode)
                {
                    POReceipt receipt =
                        SelectFrom<POReceipt>.
                        LeftJoin<Vendor>.On<POReceipt.vendorID.IsEqual<Vendor.bAccountID>>.SingleTableOnly.
                        Where<POReceipt.receiptNbr.IsEqual<@P.AsString>>.
                        View.ReadOnly.Select(Basis, barcode);
                    return receipt;
                }

                protected override void Apply(POReceipt receipt)
                {
                    Basis.ReceiptType = receipt.ReceiptType;
                    Basis.RefNbr = receipt.ReceiptNbr;
                    Basis.TranDate = receipt.ReceiptDate;
                    Basis.NoteID = receipt.NoteID;

                    Basis.Graph.Document.Current = receipt;
                }

                protected override void ClearState()
                {
                    Basis.Graph.Document.Current = null;

                    Basis.ReceiptType = null;
                    Basis.RefNbr = null;
                    Basis.TranDate = null;
                    Basis.NoteID = null;
                }

                protected override void ReportMissing(string barcode) => Basis.ReportError(Msg.Missing, barcode);
                protected override void ReportSuccess(POReceipt receipt) => Basis.ReportInfo(Msg.Ready, receipt.ReceiptNbr);

                #region Messages
                [PXLocalizable]
                public abstract class Msg
                {
                    public const string Prompt = "Scan the PO receipt number.";
                    public const string Ready = "The {0} receipt is loaded and ready to be processed.";
                    public const string Missing = "The {0} receipt is not found.";
                    public const string InvalidType = "The {0} receipt cannot be processed because it has the {1} type.";
                    public const string InvalidOrderType = "The {0} receipt cannot be processed because it has the {1} order type.";
                    public const string MultiSites = "The {0} receipt should have only one warehouse to be processed.";
                    public const string HasNonStockKit = "The {0} receipt cannot be processed because it contains a non-stock kit item.";
                }
                #endregion
            }

            public sealed class InventoryItemState : EntityState<InventoryItem>
            {
                public const string Value = "ITEM";
                public override string Code => Value;
                protected override string StatePrompt => Msg.Prompt;

                protected override InventoryItem GetByBarcode(string barcode)
                {
                    InventoryItem item =
                        SelectFrom<InventoryItem>.
                        InnerJoin<INItemXRef>.On<INItemXRef.FK.InventoryItem>.
                        Where<
                            INItemXRef.alternateID.IsEqual<@P.AsString>.
                            And<INItemXRef.alternateType.IsEqual<INAlternateType.barcode>>.
                            And<InventoryItem.itemStatus.IsNotIn<InventoryItemStatus.inactive, InventoryItemStatus.markedForDeletion>>>.
                        OrderBy<INItemXRef.alternateType.Asc>.
                        View.ReadOnly
                        .Select(Basis, barcode).FirstOrDefault();

                    if (item == null)
                    {
                        var inventory = IN.InventoryItem.UK.Find(Basis, barcode);
                        if (inventory != null && inventory.ItemStatus.IsNotIn(InventoryItemStatus.Inactive, InventoryItemStatus.MarkedForDeletion))
                            return inventory;
                    }

                    return item;
                }

                protected override Validation Validate(InventoryItem entity)
                {
                    POReceiptLine row = SelectFrom<POReceiptLine>
                        .Where<POReceiptLine.receiptNbr.IsEqual<@P.AsString>
                            .And<POReceiptLine.receiptType.IsEqual<@P.AsString>>
                            .And<POReceiptLine.inventoryID.IsEqual<@P.AsInt>>>
                        .View.ReadOnly.Select(Basis, Basis.RefNbr, Basis.ReceiptType, entity.InventoryID).FirstOrDefault();

                    if (row != null)
                    {
                        return Validation.Ok;
                    }
                    else
                    {
                        return Validation.Fail(Msg.NotOnReceipt, entity.InventoryCD.Trim());
                    }
                }


                protected override void ReportMissing(string barcode) => Basis.Reporter.Error(Msg.Missing, barcode);

                protected override void Apply(InventoryItem entity)
                {
                    Basis.InventoryID = entity.InventoryID;
                }
                protected override void ClearState()
                {
                    Basis.InventoryID = null;
                }

                protected override void ReportSuccess(InventoryItem entity) => Basis.Reporter.Info(Msg.Ready, entity.InventoryCD.Trim());

                #region Messages
                [PXLocalizable]
                public abstract class Msg
                {
                    public const string Prompt = "Scan the barcode of an item on the receipt.";
                    public const string Ready = "The {0} item is selected.";
                    public const string Missing = "The {0} item barcode is not found.";
                    public const string NotOnReceipt = "The label cannot be printed. {0} item is not found in the receipt.";
                }
                #endregion
            }

            public sealed class PrintQtyState : WMSBase.EntityState<int?>
            {
                public const string Value = "PRTQTY";
                public class value : BqlString.Constant<value> { public value() : base(SetNextIndexState.Value) { } }

                public override string Code => Value;
                protected override string StatePrompt => Msg.Prompt;

                protected override int? GetByBarcode(string barcode) => int.TryParse(barcode, out int qty) ? qty : (int?)null;
                protected override void ReportMissing(string barcode) => Basis.ReportError(Msg.BadFormat, barcode);

                protected override void Apply(int? qty) => Basis.Qty = qty;
                protected override void ClearState() => Basis.Qty = null;
                protected override void ReportSuccess(int? qty) => Basis.ReportInfo(Msg.Ready, qty);

                #region Messages
                [PXLocalizable]
                public abstract class Msg
                {
                    public const string Prompt = "Enter the label Qty.";
                    public const string Ready = "The print qty is set to {0}.";
                    public const string BadFormat = "{0} is not a valid print qty.";
                }
                #endregion
            }

            public sealed class ConfirmState : WMSBase.ConfirmationState
            {
                public override string Prompt => Basis.Localize(Msg.Prompt, Basis.Qty, Basis.RefNbr, Basis.SightOf<WMSScanHeader.inventoryID>());

                protected override FlowStatus PerformConfirmation() => Get<Logic>().Confirm();

                public class Logic : ScanExtension
                {
                    public static bool IsActive() => true;

              public virtual FlowStatus Confirm()
            {
              UserPreferences userPreferences =
                SelectFrom<UserPreferences>
                .Where<UserPreferences.userID.IsEqual<@P.AsGuid>>
                .View.Select(this.Graph, this.Graph.Accessinfo.UserID);
                  
                    PXTrace.WriteInformation($"RefNbr={Basis.RefNbr}, ReceiptType={Basis.ReceiptType}");

    if (userPreferences?.DefaultPrinterID == null)
        return FlowStatus.Fail(Msg.FailedToPrint, "No default printer.");

    if (!PXAccess.FeatureInstalled<CS.FeaturesSet.deviceHub>())
        return FlowStatus.Fail(Msg.FailedToPrint, "DeviceHub not enabled.");

    var parameters = new Dictionary<string, string>
    {
        { "ReceiptNbr", Basis.RefNbr },
        { "ReceiptType", Basis.ReceiptType },
        { "InventoryID", Basis.InventoryID?.ToString() }
    };
 //      throw new PXReportRequiredException(parameters, "PO646000", "Print Label");
    try
    {
       SMPrinter printer = PXSelect<SMPrinter,
    Where<SMPrinter.printerID, Equal<Required<SMPrinter.printerID>>>>
    .Select(this.Graph, userPreferences.DefaultPrinterID);

if (printer == null)
    return FlowStatus.Fail(Msg.FailedToPrint, "Printer not found.");

DeviceHubTools.PrintReportViaDeviceHub<PX.Objects.CR.BAccount>(
    this.Graph,
    "PO646000",
    parameters,
    printer.PrinterName,
    null,
    System.Threading.CancellationToken.None
);

        Basis.ReportInfo("Label printed successfully.");
        return FlowStatus.Ok;
    }
    catch (Exception ex)
    {
        PXTrace.WriteError(ex);
        return FlowStatus.Fail(Msg.FailedToPrint, ex.Message);
    }
}

                }

                [PXLocalizable]
                new public abstract class Msg
                {
                    public const string Prompt = "Printing {0} labels for {1}: {2}";
                    public const string ItemOrReceiptPrompt = "Scan the barcode of the item or the next receipt number.";
                    public const string FailedToPrint = "Failed to print labels: {0}";
                }
            }

            #endregion

            #region Redirect
            public sealed class RedirectFrom<TForeignBasis> : WMSBase.RedirectFrom<TForeignBasis>.SetMode<PrintMode>
              where TForeignBasis : PXGraphExtension, IBarcodeDrivenStateMachine
            {
                public override string Code => "PRTLBL";
                public override string DisplayName => Msg.DisplayName;

                private string RefNbr { get; set; }

                public override bool IsPossible
                {
                    get
                    {
                        return true;
                    }
                }

                protected override bool PrepareRedirect()
                {
                    if (Basis is ReceivePutAway rpa && rpa.RefNbr != null)
                    {
                        if (rpa.FindMode<ReceivePutAwayExts.PrintMode>().TryValidate(rpa.Receipt).By<ReceivePutAway.ReceiptState>() is Validation valid && valid.IsError == true)
                        {
                            rpa.ReportError(valid.Message, valid.MessageArgs);
                            return false;
                        }
                        else
                            RefNbr = rpa.RefNbr;
                    }

                    return true;
                }

                protected override void CompleteRedirect()
                {
                    if (Basis is ReceivePutAway rpa && rpa.CurrentMode.Code != ReceivePutAway.ReturnMode.Value && this.RefNbr != null)
                        if (rpa.TryProcessBy(ReceivePutAway.ReceiptState.Value, RefNbr, StateSubstitutionRule.KeepAll & ~StateSubstitutionRule.KeepPositiveReports))
                        {
                            rpa.SetDefaultState();
                            RefNbr = null;
                        }
                }

                #region Messages
                [PXLocalizable]
                public abstract class Msg
                {
                    public const string DisplayName = "Print Label";
                }
                #endregion
            }
            #endregion

            #region Messages
            [PXLocalizable]
            public abstract class Msg
            {
                public const string Description = "Print Labels";
            }
            #endregion
        }

        [PXOverride]
        public bool get_DocumentIsEditable(Func<bool> base_DocumentIsEditable)
        {
            if (Base1.Header.Mode == PrintMode.Value)
            {
                return true;
            }
            else
            {
                return base_DocumentIsEditable();
            }
        }

        [PXOverride]
        public virtual IEnumerable<ScanMode<ReceivePutAway>> CreateScanModes(Func<IEnumerable<ScanMode<ReceivePutAway>>> base_CreateScanModes)
        {
            foreach (var mode in base_CreateScanModes())
                yield return mode;

            yield return new PrintMode();
        }


        [PXOverride]
        public virtual ScanMode<ReceivePutAway> DecorateScanMode(ScanMode<ReceivePutAway> original, Func<ScanMode<ReceivePutAway>, ScanMode<ReceivePutAway>> base_DecorateScanMode)
        {
            var mode = base_DecorateScanMode(original);
            if (mode is ReceivePutAway.ReceiveMode receiveMode)
            {
                receiveMode
                    .Intercept.CreateRedirects.ByAppend(() => new ScanRedirect<ReceivePutAway>[] { new PrintMode.RedirectFrom<ReceivePutAway>() });
            }
            if (mode is ReceivePutAway.PutAwayMode putawayMode)
            {
                putawayMode
                    .Intercept.CreateRedirects.ByAppend(() => new ScanRedirect<ReceivePutAway>[] { new PrintMode.RedirectFrom<ReceivePutAway>() });
            }
            return mode;
        }
          
   

    }
}

 

 

1 reply

Forum|alt.badge.img
  • Jr Varsity I
  • April 13, 2026

@purva59  

Yes, you can implement silent label printing (no report UI) via Device Hub in a custom WMS scan mode on the Receive Put Away screen (INPutAway, likely WSReceivePutAway graph) after scanning PO Receipt / Inventory Item. Use SMPrintJobMaint.CreatePrintJobGroup to queue the report job directly to Device Hub printer on scan event (e.g., FieldVerifying or RowPersisting in your scan flow).

Device Hub setup:

  1. Install/run Device Hub (admin mode); set Device Hub ID, Acumatica URL/credentials.

  2. Add printer (Raw mode for labels/ZPL); Update Printer List (SM206510).

  3. Assign Device Hub printer in user profile or report settings

Label Report: Create Generic Inquiry + Report (e.g., for POReceiptLineLot/Serial); design as label

  1. Customization Code
    Extend ReceivePutAway graph (e.g., ReceivePutAwayExt). Trigger on Item scan state transition (e.g., InventoryItemState):

public class ReceivePutAwayExt : PXGraphExtension<ReceivePutAway>
{
    protected void _(Events.FieldVerifying<INPutAwayScan.inventoryID> e)
    {
        // After scan validation/success
        PrintLabelsSilently();
    }

    private void PrintLabelsSilently()
    {
        var graph = Base;
        var receiptLine = /* Get current POReceiptLine from scan context */;
        var reportID = "YourLabelReportID";  // e.g., "INReceiptLabel"
        var parameters = new Dictionary<string, string>
        {
            { "POReceiptLine.ReceiptNbr", receiptLine.ReceiptNbr },
            { "POReceiptLine.LineNbr", receiptLine.LineNbr.ToString() },
            { "Qty", "1" }  // Or scanned qty
        };

        // Silent print to Device Hub (no UI)
        SMPrintJobMaint.CreatePrintJobGroup(
            new PrintSettings { PrinterID = "YourDeviceHubPrinterID" },  // Or user default
            new PXReportRequiredException(reportID, parameters),
            reportID);

        // Clear exception to avoid UI
    }
}

 

Integrate into your CreateTransitions / ScanListene