Skip to main content
Solved

Customize Matrix Item grid


Forum|alt.badge.img+1

I’ve been asked to provide an estimate for a new customization.  I’ve done several advance things in Acumatica but this one I’m wondering if a) is this possible? and/or b) should it even be attempted?

  1. They want to use Matrix Items with 8 attributes in a template.  
  2. They want to be able to add cost/labor to each attribute value and have the price calculated during entry and saved directly to AR prices when item is created

Add custom fields to Attribute values:

 

Use these values to calculate a price during matrix entry:

 

This does not look like traditional graph extension with overrides.  I’ve tracked down some code PX.Objects\IN\Matrix\GraphExtensions\HeaderAndAttributesExt.cs 

which looks like where the fieldupdating event is being hooked up to each attribute value but I can’t see a way to sub-class or extend this abstract class.

I guess I’m at the point where I just need to hear if this is a fools errand and/or if there is some other possible way to accomplish this.  Customer does not want/need e-commerce integration nor do they want to purchase the product configurator.

Best answer by Zoltan Febert

First, I would try to inherit a class from CreateMatrixItemsHelper. CreateUpdateMatrixItems is a public virtual function, so it shouldn’t be a problem.

After, you can override GetCreateMatrixItemsHelper function, it is in SmartPanelExt graph.

public class MyCreateMatrixItemsHelper : CreateMatrixItemsHelper
{
    public MyCreateMatrixItemsHelper(PXGraph graph) : base(graph) { }

    public override void CreateUpdateMatrixItems(InventoryItemMaintBase graph, InventoryItem templateItem, IEnumerable<MatrixInventoryItem> itemsToCreateUpdate,
        bool create, Action<MatrixInventoryItem, InventoryItem> beforeSave = null)
    {
        base.CreateUpdateMatrixItems(graph, templateItem, itemsToCreateUpdate, create, beforeSave);
    }
}

// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class SmartPanelExtTest : MatrixEntryExt 
{
    protected override CreateMatrixItemsHelper GetCreateMatrixItemsHelper(PXGraph graph)
    {
        return new MyCreateMatrixItemsHelper(graph);
    }
}

If it doesn’t work, you can also wait for long operation:

PXLongOperation.WaitCompletion(Base.UID);

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

24 replies

Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3
  • Jr Varsity I
  • 175 replies
  • September 29, 2023

Hi @rjean09 

I customized Matrix Items popup before, but it was a total different requirement. Now I do not have any Acumatica Instance where I can try my idea, so I just share it with you, and I hope it will be useful.

using PX.Data;
using PX.Objects.IN.Matrix.DAC.Unbound;

// Namespace is important to be this
namespace PX.Objects.IN.GraphExtensions.SOOrderEntryExt
{
    public class SOMatrixEntryExt : MatrixEntryExt
    {
	    protected void _(Events.RowUpdated<MatrixInventoryItem> e)
	    {
		    // Maybe you can do something here?
	    }

	    protected override MatrixAttributeValues GetMatrixAttributeValues()
	    {
		    // Maybe you can do something here?
		    return null;
	    }         
    }
}

Please note the Namespace must be the same.


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • September 29, 2023
Zoltan Febert wrote:

Hi @rjean09 

I customized Matrix Items popup before, but it was a total different requirement. Now I do not have any Acumatica Instance where I can try my idea, so I just share it with you, and I hope it will be useful.

using PX.Data;
using PX.Objects.IN.Matrix.DAC.Unbound;

// Namespace is important to be this
namespace PX.Objects.IN.GraphExtensions.SOOrderEntryExt
{
    public class SOMatrixEntryExt : MatrixEntryExt
    {
	    protected void _(Events.RowUpdated<MatrixInventoryItem> e)
	    {
		    // Maybe you can do something here?
	    }

	    protected override MatrixAttributeValues GetMatrixAttributeValues()
	    {
		    // Maybe you can do something here?
		    return null;
	    }         
    }
}

Please note the Namespace must be the same.

Thanks!  Funny I already had a sub-class for MatrixEntryExt but just wasn’t sure where to go next. This gives me hope.


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 3, 2023

I have my “new price” calculating in the matrix item grid row and now I need to grab that to set on sales order and inventory item base price and create sales prices for base and 3 price classes.  It looks like I need to override the ShowMatrixPanel action in PX.Objects.IN.Matrix.GraphExtensions.SmartPanelExt and AddItemsToOrder method, however, I can’t seem to figure out how to create a graph extension for this with the Graph and MainItemType types.  I would also like to default the view to Table View (instead of Matrix View) and also default the to a specific matrix item template.  It looks like I might be able to do this in ShowMatrixPanel if I can figure out how to extend or subclass and override that action.  

Any help appreciated!


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

Extend MatrixEntryExt instead of SmartPanelExt. Remember, you still need to use the correct namesapce.

using System;
using PX.Data;
using PX.Objects.IN.Matrix.DAC.Unbound;
using PX.Objects.SO;

namespace PX.Objects.IN.GraphExtensions.SOOrderEntryExt
{
    public class MyMatrixEntryExtExtension : PXGraphExtension<MatrixEntryExt, SOOrderEntry>
    {
	    [PXOverride]
	    protected virtual void AddItemsToOrder(int? siteID, Action<int?> baseMethod)
	    {
		    baseMethod?.Invoke(siteID); // if you need
		    // your code
	    }
    }
}

 


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 3, 2023
Zoltan Febert wrote:

Extend MatrixEntryExt instead of SmartPanelExt. Remember, you still need to use the correct namesapce.

using System;
using PX.Data;
using PX.Objects.IN.Matrix.DAC.Unbound;
using PX.Objects.SO;

namespace PX.Objects.IN.GraphExtensions.SOOrderEntryExt
{
    public class MyMatrixEntryExtExtension : PXGraphExtension<MatrixEntryExt, SOOrderEntry>
    {
	    [PXOverride]
	    protected virtual void AddItemsToOrder(int? siteID, Action<int?> baseMethod)
	    {
		    baseMethod?.Invoke(siteID); // if you need
		    // your code
	    }
    }
}

 

With only the code above (not adding any of my own code yet) I get the following error:

 

FYI, this is build 23.109.0023

UPDATE: got rid of this error by declaring override of AddItemsToOrder as public. Not sure if that is correct but it seems to be working.  I will continue and update with my progress. 

Thanks Zoltan!


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

I don’t get that error, anyway, you can overwrite it in the first class I gave you as well.

I tried it on 22R2, I can confirm the debugger stopped on base.AddItemsToOrder

using PX.Data;
using PX.Objects.IN.Matrix.DAC.Unbound;

namespace PX.Objects.IN.GraphExtensions.SOOrderEntryExt
{
    // Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
    public class SOMatrixEntryExt : MatrixEntryExt
    {
	    protected void _(Events.RowUpdated<MatrixInventoryItem> e)
	    {
		    // Maybe you can do something here?
	    }

	    protected override MatrixAttributeValues GetMatrixAttributeValues()
	    {
		    // Maybe you can do something here?
		    return null;
	    }

	    protected override void AddItemsToOrder(int? siteID)
	    {
		    base.AddItemsToOrder(siteID);
	    }
    }
}

 


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 3, 2023

It isn’t hitting the breakpoint for me on base.AddItemsToOrder.  The RowUpdated event is still working fine.  I’ll switch back to the other extension with the public override and try again.


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 10, 2023

The issue I am having now is that AddItemsToOrder has a PXLongOperation in it and I need to somehow wait for that to finish before I run my code after the invoke of the base method as I need the inventory items to exist so I can add to ARSalesPrice.

What would be ideal is if I could add code right after CreateMatrixItemsHelper.CreateUpdateMatrixItems is run from inside AddItemsToorder.

 


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3
  • Jr Varsity I
  • 175 replies
  • Answer
  • October 11, 2023

First, I would try to inherit a class from CreateMatrixItemsHelper. CreateUpdateMatrixItems is a public virtual function, so it shouldn’t be a problem.

After, you can override GetCreateMatrixItemsHelper function, it is in SmartPanelExt graph.

public class MyCreateMatrixItemsHelper : CreateMatrixItemsHelper
{
    public MyCreateMatrixItemsHelper(PXGraph graph) : base(graph) { }

    public override void CreateUpdateMatrixItems(InventoryItemMaintBase graph, InventoryItem templateItem, IEnumerable<MatrixInventoryItem> itemsToCreateUpdate,
        bool create, Action<MatrixInventoryItem, InventoryItem> beforeSave = null)
    {
        base.CreateUpdateMatrixItems(graph, templateItem, itemsToCreateUpdate, create, beforeSave);
    }
}

// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class SmartPanelExtTest : MatrixEntryExt 
{
    protected override CreateMatrixItemsHelper GetCreateMatrixItemsHelper(PXGraph graph)
    {
        return new MyCreateMatrixItemsHelper(graph);
    }
}

If it doesn’t work, you can also wait for long operation:

PXLongOperation.WaitCompletion(Base.UID);


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 12, 2023

 

 

Thanks again Zoltan. You have helped me get very close.  The GetCreateMatrixItemsHelper method was exactly what I needed.

So I now have my sales prices creating for each price class after the base.CreateUpateMatrixItems method is called.  That is working fantastic.

However, now the client wants me to update the LastCost on the new SKU at the same time.  I have added this code:

 

INItemCost itemCost = new INItemCost()
{
	InventoryID = item.InventoryID,
	LastCostDate = DateTime.Now,
	LastCost = cost,
	CuryID = "USD",
	QtyOnHand = 0M,
	TotalCost = 0M,
	MinCost = 0M,
	MaxCost = 0M
};

itemGraph.ItemCosts.Insert(itemCost);
itemGraph.Persist();

But it gives me this error:

The item has a warehouse and all UOM is set to EA.  Valuation is set to FIFO.  I can enter Last Cost on the same Inventory Item Price/Cost tab on the screen and it saves fine.  Any ideas?

 


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 12, 2023

If I do a slight variation where I update the LastCost and LastCostDate AFTER the insert I don’t get an error, however, I still don’t get any Last Cost data to save anywhere.  INItemCost, INItemStats, INCostStatus are all empty for the newly added InventoryItem.

 

INItemCost itemCost = new INItemCost()
{
	InventoryID = item.InventoryID,
	CuryID = "USD",
	QtyOnHand = 0M,
	TotalCost = 0M,
	MinCost = 0M,
	MaxCost = 0M
};

itemGraph.ItemCosts.Insert(itemCost);
itemCost.LastCost = cost;
itemCost.LastCostDate = DateTime.Now;

// itemGraph.Persist();
itemGraph.Save.Press();

 


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

Hi @rjean09,

It looks like you modify LastCost after inserting the record, but it never arrives the cache.
Please see my comments, you need to implement only one of them.

INItemCost itemCost = new INItemCost()
{
	InventoryID = item.InventoryID,
	CuryID = "USD",
	QtyOnHand = 0M,
	TotalCost = 0M,
	MinCost = 0M,
	MaxCost = 0M
    // LastCost and LastCostDate are not here.
};

itemGraph.ItemCosts.Insert(itemCost);
itemCost.LastCost = cost;
itemCost.LastCostDate = DateTime.Now;
// itemGraph.ItemCosts.Update(itemCost); is missing.

// itemGraph.Persist();
itemGraph.Save.Press();

 


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 12, 2023

Guess I never pasted the error in before.  Unfortunately, simply by adding the update to push to the cache the error is back:

 


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

This error message comes from this file: App_Data/CodeRepository/PX.Objects/IN/GraphExtensions/InventoryItemMaintBaseExt/UnitsOfMeasure.cs, from ValidateUnitConversions function:

if (baseConversions.Count() != 1)
	throw new PXException(Messages.BaseConversionNotFound,...

Maybe you already have an ItemCost record, and you need to update it, instead of inserting a new one?


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 13, 2023

So it appears that INUnit is not populated after CreateUpdateMatrixItems and that is the problem.

INUnit is there after the full AddOrderItems stack is run.  So I just need to find out where that gets added and if I can add it myself without causing an issue.


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 13, 2023

Update: INUnit IS populated before it gets to my code.  So not sure why ValidateUnitConversions would not find it.


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

ValidateUnitConversions checks if there is only one INUnit exists. If you insert a second one with your code it will find two, and will throw an error. Have you tried to get the INUnit record and update it, instead of inserting a new one?


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 13, 2023

Not trying to add to INUnit, just INItemCost.  I set a breakpoint and verified only 1 INUnit exists in cache and in database before my code runs (After CreateUpdateMatrixItems)

Ignore my earlier comment where I was thinking this was not the case.  Once I realized it was there I never actually tried adding to INUnit.


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

Sorry, I thought we are talking about INUnitCost.

var itemCost = INItemCost.PK.Find(itemGraph, item.InventoryID, "USD");
itemCost.LastCost = cost;
itemCost.LastCostDate = DateTime.Now;

itemGraph.ItemCosts.Update(itemCost);
itemGraph.Save.Press();

 


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 13, 2023

Problem solved.  Bonehead mistake. 

I had:

graph.Item.Current = item

instead of

itemGraph.Item.Current = item

So I was setting Item.Current on the wrong instance of the InventoryItemMaint graph.  

Thanks again for all your help.  Really appreciate it. 

I am going to mark your earlier reply (before all this LastCost fiasco) as the best answer.  If we ever meet at an Acumatica conference I will buy you a beer (or two or three) 😄


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • October 13, 2023

For anyone else interested, setting the defaults on the Matrix Item popup was relatively easy using Events.CacheAttached:

setting matrix popup defaults

Changing SmartPanelType to default to “E” causes it to open in Table View instead of Matrix View


Zoltan Febert
Jr Varsity I
Forum|alt.badge.img+3

@rjean09 You are very welcome, it was a nice two weeks of effort :)
Welcome to the club, now you can use the “I Customized the Matrix” badge :)


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

The GetCreateMatrixItemsHelper method is now being called a different way and bypassing my SmartPanelExt subclass.  It’s using a FindImplementation to load it’s clone and call the method so my class never gets called.

		protected virtual void AddItemsToOrder(int? siteID)
		{
			var templateItem = InventoryItem.PK.Find(Base, Header.Current.TemplateItemID);
			var itemsToCreate = MatrixItems.Cache.Cached.RowCast<MatrixInventoryItem>().Where(mi => mi.New == true).ToList();

			if (templateItem != null)
			{
				var clone = Base.Clone();

				PXLongOperation.StartOperation(Base, delegate ()
				{
// this no longer finds my helper extension:
					var ext = clone.FindImplementation<SmartPanelExt<Graph, MainItemType>>();
					if (ext == null)
						throw new PXArgumentException(nameof(SmartPanelExt<Graph, MainItemType>));

					var inventoryItemGraph = (templateItem.StkItem == true) ?
						(InventoryItemMaintBase)PXGraph.CreateInstance<InventoryItemMaint>() :
						PXGraph.CreateInstance<NonStockItemMaint>();
					inventoryItemGraph.DefaultSiteFromItemClass = false;

// this no longer finds my helper extension:
					var helper = ext.GetCreateMatrixItemsHelper(inventoryItemGraph);
					var attributeGroupHelper = ext.GetAttributeGroupHelper(inventoryItemGraph);

					PXLongOperation.SetCustomInfo(clone);

// This no longer calls my method
					helper.CreateUpdateMatrixItems(inventoryItemGraph, templateItem, itemsToCreate, true,
						(t, item) => attributeGroupHelper.OnNewItem(templateItem, item, t.AttributeIDs, t.AttributeValues));

Don’t know what to do now.  Any ideas?


Forum|alt.badge.img+1
  • Author
  • Semi-Pro III
  • 76 replies
  • September 24, 2024

The key to get this working in recent versions was using PXProtectedAccess attribute on my smartpanel extension class and GetCreatMatrixItemsHelper method (thanks Acumatica dev support for this)

 

    [PXProtectedAccess]
    public abstract class SmartPanelExt_ProtectedExtension : MatrixEntryExt
    {
        [PXProtectedAccess(typeof(SmartPanelExt<SOOrderEntry, SOOrder>))]
        protected override CreateMatrixItemsHelper GetCreateMatrixItemsHelper(PXGraph graph)
        {
            return new CreateMatrixItemsHelperExt(graph);
        }
    }

 


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