Skip to main content

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
}
}
p2024-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?)
p2024-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?)
p2024-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?)
p2024-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?)
p2024-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!

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!


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

 


@TharidhiP

See following discussions:

Copying User Defined Fields acrross the sales order, shipment and invoice screen | Community (acumatica.com)

acumatica - How to paste custom field values from Sales Order details to Shipment and AR Invoice? - Stack Overflow


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.

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!


@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!



@TharidhiP please note that dPXDefault] works only when you create a new record, so if you want to copy your data between 2 existing entities, dPXDefault] 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.


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!


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


@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.


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();
}
}

 


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


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

Thank you!


Reply