3

I have a request to change the number sequencer for a new Item based on the Class that is selected for the new item. So, the user selects a particular class and the system need to find the next value for the InventoryItem.InventoryCD by using a number sequence that is set on a field in a preferences form.

Any ideas on how to do this?

I've tried the FieldDefaulting() event for the InventoryCD and InventoryID fields but, it's not working.

TIA!

MikeNIke
  • 221
  • 1
  • 10

2 Answers2

1

A custom numbering attribute is required to achieve the functionality you desire. Please find working example below.

First we define the fields on setup which will hold our custom numbering sequences :

public class INSetupExtension : PXCacheExtension<INSetup>
{
    #region InventoryNumbering1ID

    public abstract class inventoryNumbering1ID : PX.Data.BQL.BqlString.Field<inventoryNumbering1ID>
    {
    }
    [PXDBString(10, IsUnicode = true)]
    [PXDefault("INV1")]
    [PXSelector(typeof(Numbering.numberingID), DescriptionField = typeof(Numbering.descr))]
    [PXUIField(DisplayName = "Inventory Numbering One", Visibility = PXUIVisibility.Visible)]
    public virtual String InventoryNumbering1ID { get; set; }
    #endregion

    #region InventoryNumbering1ID

    public abstract class inventoryNumbering2ID : PX.Data.BQL.BqlString.Field<inventoryNumbering2ID>
    {
    }
    [PXDBString(10, IsUnicode = true)]
    [PXDefault("INV2")]
    [PXSelector(typeof(Numbering.numberingID), DescriptionField = typeof(Numbering.descr))]
    [PXUIField(DisplayName = "Inventory Numbering Two", Visibility = PXUIVisibility.Visible)]
    public virtual String InventoryNumbering2ID { get; set; }
    #endregion
}

Next we define our custom Autonumbering attribute, here I have made the "default" numbering sequence the argument for the constructor declaration.

In the overridden RowPersisting event we utilize our criteria to determine the numbering sequence to use for the next persisted new item.

In my example I use one sequence if an item class is used, if no item class is selected I use another.

public class NonStockAutoNumbereAttribute : AutoNumberAttribute
{
    public NonStockAutoNumbereAttribute() : base(typeof(INSetupExtension.inventoryNumbering2ID), typeof(AccessInfo.businessDate))
    {
    }

    public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
        InventoryItem row = e.Row as InventoryItem;
        if(sender.GetStatus(row) == PXEntryStatus.Inserted)
        {
            base.RowPersisting(sender, e);

            INSetup setup = PXSelectBase<INSetup, PXSelectReadonly<INSetup>.Config>.Select(sender.Graph, Array.Empty<object>());
            INSetupExtension setupExt = setup.GetExtension<INSetupExtension>();
            string nextNumber;

            object newValue = sender.GetValue(e.Row, this._FieldOrdinal);
            if(!string.IsNullOrEmpty((string)newValue))
            {
                INItemClass itemClass = PXSelectReadonly<INItemClass, Where<INItemClass.itemClassID, Equal<Required<INItemClass.itemClassID>>>>.Select(sender.Graph, row.ItemClassID);
                if(itemClass != null)
                {
                    nextNumber = AutoNumberAttribute.GetNextNumber(sender, e.Row, setupExt.InventoryNumbering1ID, DateTime.Now);
                }
                else
                {
                    nextNumber = AutoNumberAttribute.GetNextNumber(sender, e.Row, setupExt.InventoryNumbering2ID, DateTime.Now);
                }
                sender.SetValue(e.Row, this._FieldName, nextNumber);
            }
        }
    }
}

On the Inventory Item maintenance page I have added a simple CacheAttached :

public class NonStockItemMaintExtension : PXGraphExtension<NonStockItemMaint>
{
    [PXDefault]
    [NonStockAutoNumbereAttribute]
    [InventoryRaw(typeof(Where<InventoryItem.stkItem.IsEqual<False>>), IsKey = true, DisplayName = "Inventory ID", Filterable = true)]
    protected virtual void _(Events.CacheAttached<InventoryItem.inventoryCD> e) { }
}

The results can be seen below :

No class numbering

Low Numbering Sequence No Class Numbering

Class Numbering

High Numbering Sequence Class Numbering

Joshua Van Hoesen
  • 1,684
  • 15
  • 30
0

You mentioned trying to do this on FieldDefaulting and also that you attempted on InventoryID and InventoryCD. Before we get into a couple of possible options, let's focus on the nature of InventoryID vs. InventoryCD and the FieldDefaulting event. Also, we need to consider the impact of AutoNunmber on InventoryCD.

InventoryID vs. InventoryCD

InventoryID is an Identity field. It is a sequence generated by the database on insert to allow us to uniquely identify the record even if we change other common values such as the InventoryCD, as you desire. The InventoryCD field is also known as the Inventory Code. This is the human readable number that adheres to our desired nomenclature for defining a part number. For instance, we use a 2 digit year to prefix a number sequence to clearly indicate how old the part number is. Because InventoryID is an identity field, NEVER try to update that field, or you may cause data integrity issues for all the related tables that point back to the InventoryItem record via the InventoryID field.

FieldDefaulting and SetDefaultExt

FieldDefaulting may be the place you want to set the value for InventoryCD. However, you don't know the value at the time you are creating the record. In fact, you don't even know the value when you are ready to press the save button. Unless you have configured numbering to allow manual entry, the number isn't assigned until the Persist. As the value is inserted into the database, the AutoNumber logic fires to identify the numbering sequence to generate the InventoryCD value. If you wish to use the FieldDefaulting event, you must first enable manual numbering for the Inventory numbering sequence. You then want to be sure to use SetDefaultExt on the FieldUpdated event of the item class field so that when you change item classes, you get a new inventory id. You also must decide if this value is allowed to change once assigned because the aforementioned logic will change the value every time you change the item class unless you put other measures in place.

Change ID Action

It is important to note that there is a Change ID action on the Action menu. It is intended for this very purpose, although it is a manual entry. I'd recommend that you explore the code behind that action to understand how Acumatica goes about altering the InventoryCD. Also, note that this is performed manually after the record has been created and and InventoryCD has been assigned already. Perhaps you have a way to identify that the InventoryCD value is still the default and needs to be changed, so you could do something around overriding Persist.

Custom Attribute Inheriting AutoNumber

If you are more adventurous than I am, you could consider a custom Attribute inheriting from the AutoNumber attribute and use a cacheattached to change to your attribute.

Business Events to Initiate Custom Action

Finally, you might consider a business event to watch for when an InventoryItem record has been inserted and then fire some custom action that simulates the Change ID action but using your logic for determining the new number based on the Item Class. If you only capture when the record is created and not when it is updated, then I suspect you can use the business event to make the initial InventoryCD change per Item Class and then require manual change through Change ID if needed again later.

Each of these points gives you some possible ways to approach making the change you need as well as some potential hazards. While this doesn't give you the code achieve your objective, I hope that it gives you some options to find the solution that works best for you. Please post a comment or answer once you reach a solution that works best for you.

Brian Stevens
  • 1,826
  • 1
  • 7
  • 16