Skip to main content

I am trying to get a custom field (Absolute Variance Quantity in INPIDetail/INPIDetailExt) to get summed up and have that amount sent to a custom header (Total Absolute Variance Quantity in INPIHeader/INPIHeaderExt). 

The population of Absolute Variance Quantity works just fine, I’m just stumped by the process of having it update Total Absolute Variance Quantity.

Here is my code so far:

And as text:

public class INPIReviewExtension2 : INPIHeader
  {
    //PXGraphExtension<PX.Objects.IN.INPIReview>,
    gPXFilterable]
    ePXImport(typeof(INPIHeader))]
    public
      SelectFrom<INPIDetail>.
      LeftJoin<InventoryItem>.On<INPIDetail.FK.InventoryItem>.
      LeftJoin<INSubItem>.On<INPIDetail.FK.SubItem>.
      Where<
        INPIDetail.pIID.IsEqual<INPIHeader.pIID.AsOptional>.
        And<
          INPIDetail.inventoryID.IsNull.
          Or<InventoryItem.inventoryID.IsNotNull>>>.
      OrderBy<INPIDetail.lineNbr.Asc>.
      View PIDetail;

    public
      SelectFrom<INPIDetail>.
      Where<INPIDetail.FK.PIHeader.SameAsCurrent>.
      View PIDetailPure;

    public
      SelectFrom<INPIHeader>.
      Where<INPIHeader.pIID.IsEqual<INPIHeader.pIID.FromCurrent>>.
      View PIHeaderInfo;

    public SelectFrom<INSetup>.View INSetup;

    public
      PXSetup<INSite>.
      Where<INSite.siteID.IsEqual<INPIHeader.siteID.FromCurrent>>
      insite;

    public PXFilter<PIGeneratorSettings> GeneratorSettings;
    public INBarCodeItemLookup<INBarCodeItem> AddByBarCode;
    public PXSetup<INSetup> Setup;

    public PXSelect<INPIHeader, Where<INPIHeader.status, Equal<INPIHdrStatus.counting>>> PIHeader;
      
    protected void INPIDetail_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
    {
      
      var row = (INPIHeader)e.Row;
      if(row==null){
        return;
      }
      
      decimal total_var_abs_qty = (decimal)0.00;

      foreach (INPIDetail detail in PIDetailPure.Select())
      {
        if (detail == null || detail.Status == INPIDetStatus.Skipped)
          continue;

        if(detail.VarQty < 0){
          total_var_abs_qty += (decimal)detail.VarQty*-1;
        }else{
          total_var_abs_qty += (decimal)detail.VarQty;       
        }
      }

      //TODO: Sending total_var_qty to INPIHeaderExt.usrTotalAbsVarQty
      var header = (INPIHeader)cache.Current;
      
      cache.SetValueExt<INPIHeaderExt.usrTotalAbsVarQty>(e.Row, total_var_abs_qty);
               
      return;
    }
  
    
  }

The current code compiles fine but my total absolute variance value remains at zero even when the absolute variance value for a row is updated. There’s probably a lot of code in the Class section I don’t need, but I can clean that up once the main issue is solved.

I tried borrowing some of the methodology used in the source code for how it updates Variance Quantity.

Any ideas? Thanks in advance.

A couple problems:

  • e.Row will never be INPIHeader because the event handler is handling FieldUpdated of INPIDetail
  • Using e.Cache in an INPIDetail event handler will only be able to update INPIDetail rows; to update INPIHeader, you need to reference Base.PIHeader.Cache (or whatever the primary view is called)

Try something like this:

protected void INPIDetail_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (INPIDetail)e.Row;
if (row is null) return;

//...

//TODO: Sending total_var_qty to INPIHeaderExt.usrTotalAbsVarQty
INPIHeader header = Base.PIHeader.Current; // (or whatever the primary view is called)

Base.PIHeader.SetValueExt<INPIHeaderExt.usrTotalAbsVarQty>(header, total_var_abs_qty);
}

 


A couple problems:

  • e.Row will never be INPIHeader because the event handler is handling FieldUpdated of INPIDetail
  • Using e.Cache in an INPIDetail event handler will only be able to update INPIDetail rows; to update INPIHeader, you need to reference Base.PIHeader.Cache (or whatever the primary view is called)

Try something like this:

protected void INPIDetail_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (INPIDetail)e.Row;
if (row is null) return;

//...

//TODO: Sending total_var_qty to INPIHeaderExt.usrTotalAbsVarQty
INPIHeader header = Base.PIHeader.Current; // (or whatever the primary view is called)

Base.PIHeader.SetValueExt<INPIHeaderExt.usrTotalAbsVarQty>(header, total_var_abs_qty);
}

 

I changed

public class INPIReviewExtension2 : INPIHeader

 to 

public class INPIReviewExtension2 : PXGraphExtension<PX.Objects.IN.INPIReview>

in order for Base to be able to be referenced, but now I am getting this error:

 


Wow, I totally missed that you weren’t even using a GraphExtension 😀

You shouldn’t need to redefine all the views. Have you taken the Application Developer training?


Wow, I totally missed that you weren’t even using a GraphExtension 😀

You shouldn’t need to redefine all the views. Have you taken the Application Developer training?

I have not. I do have previous experience with C# and application development, but I am mostly learning and researching this architecture as I go. I’ll try and add some null exception handlers here.


If you’re familiar with C#, the syntax will feel familiar, but even so, the framework has very specific syntax. You’ll benefit a lot from even a speed run.


If you’re familiar with C#, the syntax will feel familiar, but even so, the framework has very specific syntax. You’ll benefit a lot from even a speed run.

I’ll keep that in mind. Thanks for your help and input.


In the DAC for the detail custom field you can add the PXFormula attribute to that field:

    ÂPXFormula(
      typeof(Mult<SOLine.orderQty, SOLineExt.usrStdCost>),
      typeof(SumCalc<SOOrderExt.usrTotalStdCost>)
    )]

The first typeof() is the formula used to calculate the detail custom field. You can adjust that formula as required.

The second typeof() is where the ‘’magic’ happens. It tells the graph to update the Parent DAC with the sum of the detail line values (which were calculated by the first typeof() formula).

You can also set the first calculation to null if you need to write a more complicated calculation for the detail field.

    tPXFormula(
      null,
      typeof(SumCalc<SOOrderExt.usrTotalTPStdCost>)
    )]

One gotcha that can happen with this method is when copying records (like copying an order) the summed values will be copied in the header and so new transactions added to the new order will increment the total rather than the document starting at zero. You just have to make sure that the code that is copying the record sets the header value to zero and then you’re good.

And I’ll second ​@darylbowman’s advice on going through the developer documentation/courses - there are many subtleties to the framework. Fortunately, the training documentation is well written.


In the DAC for the detail custom field you can add the PXFormula attribute to that field:

    ÂPXFormula(
      typeof(Mult<SOLine.orderQty, SOLineExt.usrStdCost>),
      typeof(SumCalc<SOOrderExt.usrTotalStdCost>)
    )]

This is the best solution in a master-detail relationship scenario. It does require that a key field of the detail DAC be marked with the mPXParent] attribute, but it is in this scenario:

 


Reply