Skip to main content
Solved

Copy Sales Order Line Field Values to Shipment Lines


Forum|alt.badge.img+2

Hi I have implemented copying some custom fields from sales order header to the shipment header screen which works as expected. 

Now I need some custom field values copied from sales order line to the shipment line screen, I’ve implemented the below but I get the error below. 

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using PX.Data;
using PX.Data.BQL;
using PX.Data.BQL.Fluent;
using PX.Data.WorkflowAPI;
using PX.Common;
using PX.Objects.AR;
using PX.Objects.CM;
using PX.Objects.CR;
using PX.Objects.CS;
using PX.Objects.EP;
using PX.Objects.GL;
using PX.Objects.IN;
using PX.SM;
using PX.Objects.IN.DAC.Accumulators;
using PX.Objects.IN.Overrides.INDocumentRelease;
using POLineType = PX.Objects.PO.POLineType;
using POReceiptLine = PX.Objects.PO.POReceiptLine;
using PX.CarrierService;
using PX.Data.DependencyInjection;
using PX.LicensePolicy;
using PX.Objects.SO.Services;
using PX.Objects.PO;
using PX.Objects.AR.MigrationMode;
using PX.Objects.Common;
using PX.Objects.Common.Discount;
using PX.Objects.Common.Extensions;
using PX.Common.Collection;
using PX.Objects.SO.GraphExtensions.CarrierRates;
using PX.Objects.SO.GraphExtensions.SOShipmentEntryExt;
using PX.Api;
using LocationStatus = PX.Objects.IN.Overrides.INDocumentRelease.LocationStatus;
using ShipmentActions = PX.Objects.SO.SOShipmentEntryActionsAttribute;
using PdfSharp.Pdf.IO;
using PX.Objects.IN.Attributes;
using PX.Concurrency;
using PX.Objects.SO.GraphExtensions.SOOrderEntryExt;
using PX.Objects.GL.FinPeriods.TableDefinition;
using PX.Objects.GL.FinPeriods;
using PX.Objects;
using PX.Objects.SO;

namespace PX.Objects.SO
{
  public class SOShipmentEntry_Extension : PXGraphExtension<PX.Objects.SO.SOShipmentEntry>
  {
    #region Event Handlers

  public delegate void CreateShipmentDelegate(CreateShipmentArgs args);

        [PXOverride]
        public void CreateShipment(CreateShipmentArgs args, CreateShipmentDelegate baseMethod)
        {
            // Call the original method to ensure default behavior
            baseMethod(args);

            // Retrieve the current shipment and order
            SOShipment ship = Base.Document.Current;
            SOOrder order = args.Order;

            if (ship != null && order != null)
            {
                // Use the extension cache to access the custom fields
                SOShipmentExt shipExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(ship);
                SOOrderExt orderExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(order);

                // Update custom fields in the shipment header
                shipExt.UsrCommission = orderExt.UsrCommission;
                shipExt.UsrCustomerPickUp = orderExt.UsrCustomerPickUp;
                shipExt.UsrDuty = orderExt.UsrDuty;
                shipExt.UsrFreight = orderExt.UsrFreight;
                shipExt.UsrInsurance = orderExt.UsrInsurance;
                shipExt.UsrQtyShow = orderExt.UsrQtyShow;
                shipExt.UsrQtyNote = orderExt.UsrQtyNote;

                // Update and save the shipment header
                Base.Document.Update(ship);
                Base.Save.Press();            
            }
        }


        // Event handler for SOShipLine RowPersisting
        protected virtual void SOShipLine_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
        {
            SOShipLine shipLine = (SOShipLine)e.Row;
            if (shipLine == null) return;

            // Query the Sales Order Line for the current shipment line
            SOLine orderLine = PXSelect<SOLine,
                Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
                And<SOLine.lineNbr, Equal<Required<SOLine.lineNbr>>>>>
                .Select(Base.Base, shipLine.SOOrderNbr, shipLine.SOOrderLineNbr);

            if (orderLine != null)
            {
                SOShipLineExt shipLineExt = PXCache<SOShipLine>.GetExtension<SOShipLineExt>(shipLine);
                SOLineExt lineExt = PXCache<SOLine>.GetExtension<SOLineExt>(orderLine);

                // Copy custom field value from SOLine to SOShipLine
                shipLineExt.UsrBrand = lineExt.UsrBrand;

                // Update the shipment line
                sender.Update(shipLine);
            }
        }





    #endregion
  }
}
[2024-07-29 04:58:51.153] \App_Code\Caches\SOShipmentEntry.cs(100): error CS1061: 'SOShipmentEntry' does not contain a definition for 'Base' and no accessible extension method 'Base' accepting a first argument of type 'SOShipmentEntry' could be found (are you missing a using directive or an assembly reference?)
[2024-07-29 04:58:51.153] \App_Code\Caches\SOShipmentEntry.cs(100): error CS1061: 'SOShipLine' does not contain a definition for 'SOOrderNbr' and no accessible extension method 'SOOrderNbr' accepting a first argument of type 'SOShipLine' could be found (are you missing a using directive or an assembly reference?)
[2024-07-29 04:58:51.154] \App_Code\Caches\SOShipmentEntry.cs(100): error CS1061: 'SOShipLine' does not contain a definition for 'SOOrderLineNbr' and no accessible extension method 'SOOrderLineNbr' accepting a first argument of type 'SOShipLine' could be found (are you missing a using directive or an assembly reference?)
[2024-07-29 04:58:51.156] \App_Code\Caches\SOShipmentEntry.cs(100): error CS1061: 'SOShipmentEntry' does not contain a definition for 'Base' and no accessible extension method 'Base' accepting a first argument of type 'SOShipmentEntry' could be found (are you missing a using directive or an assembly reference?)
[2024-07-29 04:58:51.158] Compiler time, in seconds: 19.5998106

 

How can I achieve this functionality? Any help would be appreciated, thank you!

Best answer by TharidhiP

Hi @Chris Hackett this issue was sorted via this community question posted. 

Thank you!

View original
Did this topic help you find an answer to your question?

13 replies

Nilkanth Dipak
Jr Varsity III
Forum|alt.badge.img+7

Hi @TharidhiP ,

I believe you need to change below code snippet 

 // Query the Sales Order Line for the current shipment line
            SOLine orderLine = PXSelect<SOLine,
                Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
                And<SOLine.lineNbr, Equal<Required<SOLine.lineNbr>>>>>
                .Select(Base.Base, shipLine.SOOrderNbr, shipLine.SOOrderLineNbr);

to 

   // Query the Sales Order Line for the current shipment line
   SOLine orderLine = PXSelect<SOLine,
       Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
       And<SOLine.lineNbr, Equal<Required<SOLine.lineNbr>>>>>
       .Select(Base, shipLine.OrigOrderNbr, shipLine.OrigLineNbr);

You  are accessing Base.Base is not appropriate. Instead, we should declare the base graph directly to resolve the issue you are facing.
You are accessing SOOrderNbr and SOOrderLineNbr from SOShipLine. These fields are not part of the SOShipLine DAC, and attempting to access them will lead to errors. We need to ensure that we are only accessing fields that are defined within the relevant DACs.

Hope, it helps!


Forum|alt.badge.img

Hi @TharidhiP 

At the DAC level, you can use the PXDefault() attribute to handle updating custom field values. The following code snippet demonstrates how to copy values from a sales order line to the shipment line screen:

 #region UsrKNValue1
        [PXDBString(50, IsUnicode = true)]
        [PXDefault(typeof(Search<SOLineExt.usrKNValue1,
                              Where<SOLine.orderType, Equal<Current<SOShipLine.origOrderType>>,
                            And<SOLine.orderNbr, Equal<Current<SOShipLine.origOrderNbr>>,
                             And<SOLine.lineNbr, Equal<Current<SOShipLine.origLineNbr>>>>>>), PersistingCheck = PXPersistingCheck.Nothing)]
        [PXUIField(DisplayName = "Value 1")]
        public string UsrKNValue1 { get; set; }
        public abstract class usrKNValue1 : PX.Data.BQL.BqlString.Field<usrKNValue1> { }
        #endregion

 


RohitRattan88
Acumatica Moderator
Forum|alt.badge.img+4
  • Acumatica Moderator
  • 245 replies
  • July 29, 2024

andriitkachenko
Jr Varsity I
Forum|alt.badge.img+5

In addition to the @Dipak Nilkanth answer, here’s the most likely solution for the missing fields:

SOLine orderLine = PXSelect<SOLine,
    Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
    And<SOLine.lineNbr, Equal<Required<SOLine.lineNbr>>>>>
    .Select(Base, shipLine.OrigOrderNbr, shipLine.OrigLineNbr);
  1. Base.Base doesn’t exist. You need just Base
  2. shipLine doesn’t have SOOrderNbr field, but it’s stored for reference in the OrigOrderNbr
  3. Similarly for SOOrderLineNbr - use OrigLineNbr instead.

Forum|alt.badge.img+2
  • Author
  • Semi-Pro III
  • 96 replies
  • August 1, 2024

Thank you @saikrishnav35 for your helpful advice, would this be the same logic to be applied if I wanted to copy the shipment line field value to the invoice screen as well?

Thank you!


Forum|alt.badge.img+2
  • Author
  • Semi-Pro III
  • 96 replies
  • August 1, 2024

@saikrishnav35 adding to this how can I implement this same method to copy custom field values in the purchase order screen to the purchase receipt header screen. Thank you!


andriitkachenko
Jr Varsity I
Forum|alt.badge.img+5


@TharidhiP please note that [PXDefault] works only when you create a new record, so if you want to copy your data between 2 existing entities, [PXDefault] won’t help you do that - you need to copy it through the event as you did it in the original message.

All you need is to fix the arguments of your query.

What I’d suggest to make it easier to work with - if you are expecting only 1 record to be found by your query, you can do this:

SOLine orderLine = PXSelect<SOLine,
    Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
        And<SOLine.lineNbr, Equal<Required<SOLine.lineNbr>>>>>
    .Select(Base, shipLine.OrigOrderNbr, shipLine.OrigLineNbr)
    .FirstTableItems
    .FirstOrDefault();

You should also think about your case to understand what kind of event you want to use to set your values - for example, RowPersisting is called only if the record has been changed in any way and requires an update in the database.

If you want your data to be copied regardless of other changes - you’re left with RowSelected or RowInserting.

RowInserting is triggered when new record is being created, so if you need to change old records, this event won’t help you.

RowSelected is one of the most triggered events, so you’d need to be cautious and add validation to avoid copying data 10+ times before it is saved. You could just check if the object you want data to be copied to already has it. If it is - then return. If done properly, it will ensure that you only transfer your values once and won’t create a performance bottleneck.

​As I don’t know your application, I can only guess which event is right for you.


Forum|alt.badge.img+2
  • Author
  • Semi-Pro III
  • 96 replies
  • August 9, 2024

Thank you @andriitkachenko for your advice. I’ve modified accordingly for achieving the copying of values to the shipment line. I’ve tried this approach for copying values from purchase order header screen to the purchase receipt header screen but the values are not getting copied. Do you have any idea why? 

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using PX.Common;
using PX.Data;
using PX.Objects.CM.Extensions;
using PX.Objects.GL;
using PX.Objects.CS;
using PX.Objects.IN;
using PX.Objects.AP;
using PX.Objects.AR;
using PX.Objects.CR;
using PX.Objects.PM;
using PX.Objects.TX;
using PX.Objects.EP;
using SOOrder = PX.Objects.SO.SOOrder;
using SOLine4 = PX.Objects.SO.SOLine4;
using PX.Objects.SO;
using System.Linq;
using CRLocation = PX.Objects.CR.Standalone.Location;
using SiteStatus = PX.Objects.IN.Overrides.INDocumentRelease.SiteStatus;
using LocationStatus = PX.Objects.IN.Overrides.INDocumentRelease.LocationStatus;
using LotSerialStatus = PX.Objects.IN.Overrides.INDocumentRelease.LotSerialStatus;
using ItemLotSerial = PX.Objects.IN.Overrides.INDocumentRelease.ItemLotSerial;
using SiteLotSerial = PX.Objects.IN.Overrides.INDocumentRelease.SiteLotSerial;
using PX.Objects.AP.MigrationMode;
using PX.Objects.Common;
using PX.CS.Contracts.Interfaces;
using PX.Data.DependencyInjection;
using PX.LicensePolicy;
using PX.TaxProvider;
using PX.Objects.GL.FinPeriods.TableDefinition;
using PX.Objects.GL.FinPeriods;
using PX.Objects.Common.Extensions;
using PX.Objects.PO.LandedCosts;
using PX.Objects.PO.GraphExtensions.POReceiptEntryExt;
using System.Runtime.Serialization;
using PX.Data.ReferentialIntegrity.Attributes;
using PX.Objects.Common.Bql;
using PX.Objects.Extensions.CostAccrual;
using PX.Data.BQL.Fluent;
using PX.Data.BQL;
using PX.Data.WorkflowAPI;
using PX.Objects.IN.Services;
using PX.Objects.Extensions.MultiCurrency;
using PX.Objects.CR.Extensions;
using PX.Objects.IN.DAC.Accumulators;
using PX.Objects;
using PX.Objects.PO;

namespace PX.Objects.PO
{
  public class POReceiptEntry_Extension : PXGraphExtension<PX.Objects.PO.POReceiptEntry>
  {
    #region Event Handlers

    // This method will be triggered when a Purchase Receipt is created
        protected virtual void POReceipt_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
        {
            POReceipt receipt = e.Row as POReceipt;
            if (receipt == null) return;

            // Retrieve the related Purchase Order based on receipt
            POOrder order = PXSelect<POOrder,
                Where<POOrder.orderNbr, Equal<Required<POOrder.orderNbr>>>>.Select(Base, receipt.OrigPONbr);

            if (order != null)
            {
                // Use the extension cache to access the custom fields
                POOrderExt orderExt = PXCache<POOrder>.GetExtension<POOrderExt>(order);
                POReceiptExt receiptExt = PXCache<POReceipt>.GetExtension<POReceiptExt>(receipt);

                // Copy custom field values from the Purchase Order to the Purchase Receipt
                receiptExt.UsrCallForShipment = orderExt.UsrCallForShipment;
                receiptExt.UsrConfirmedOnBoard = orderExt.UsrConfirmedOnBoard;
                receiptExt.UsrContainerNo = orderExt.UsrContainerNo;
                receiptExt.UsrOrderType = orderExt.UsrOrderType;
                receiptExt.UsrStatus = orderExt.UsrStatus;
            }
        }



    #endregion
  }
}

Thank you for your help!


Forum|alt.badge.img+7
  • Captain II
  • 295 replies
  • August 9, 2024

Hi @TharidhiP 

 

Might be worth putting a base.Update(receipt) to update in the cache, but i am not too sure.

 

A field updated event could do a better job at achieving what you are aiming for.

 

Aleks


andriitkachenko
Jr Varsity I
Forum|alt.badge.img+5

@TharidhiP Did you try to debug it? Possible issues (as I didn’t troubleshoot it myself I can tell you the exact culprit:

  • receipt.OrigPONbr could be null, and if (order != null) condition didn’t fulfil;  
  • PXCache<POOrder>.GetExtension could return null if the extension isn’t found. I’m more used to order.GetExtension<POOrderExt>(), but I don’t think it’s an issue with the method you use

I’d place a breakpoint at the start of the event and see how it works to detect an issue.


Forum|alt.badge.img+2
  • Author
  • Semi-Pro III
  • 96 replies
  • August 14, 2024

Hi @andriitkachenko and @aiwan thanks for the helpful tips, I’ve tried modifying the RowSelected event but the result is the same. Is there a way I can use PXOverride and update the header fields on purchase receipt creation.

 

For example this is an implementation I did for copying field values from sales order to shipment screen.

 public delegate void CreateShipmentDelegate(CreateShipmentArgs args);

        [PXOverride]
        public void CreateShipment(CreateShipmentArgs args, CreateShipmentDelegate baseMethod)
        {
            // Call the original method to ensure default behavior
            baseMethod(args);

            // Retrieve the current shipment and order
            SOShipment ship = Base.Document.Current;
            SOOrder order = args.Order;

            if (ship != null && order != null)
            {
                // Use the extension cache to access the custom fields
                SOShipmentExt shipExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(ship);
                SOOrderExt orderExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(order);

                // Update custom fields in the shipment header
                shipExt.UsrCommission = orderExt.UsrCommission;
                shipExt.UsrCustomerPickUp = orderExt.UsrCustomerPickUp;
                shipExt.UsrDuty = orderExt.UsrDuty;
                shipExt.UsrFreight = orderExt.UsrFreight;
                shipExt.UsrInsurance = orderExt.UsrInsurance;
                shipExt.UsrQtyShow = orderExt.UsrQtyShow;
                shipExt.UsrQtyNote = orderExt.UsrQtyNote;
                shipExt.UsrShippingTermsINCOTERM = orderExt.UsrShippingTermsINCOTERM;
                shipExt.UsrCurrencyTerms = orderExt.UsrCurrencyTerms;

                // Update and save the shipment header
                Base.Document.Update(ship);
                Base.Save.Press();            
            }
        }

 


Chris Hackett
Community Manager
Forum|alt.badge.img
  • Acumatica Community Manager
  • 2640 replies
  • September 9, 2024

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


Forum|alt.badge.img+2
  • Author
  • Semi-Pro III
  • 96 replies
  • Answer
  • September 12, 2024

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