Creating Custom Autonumber Autogenerated Id In Acumatica

Creating custom Autonumber which will be auto generated ID in Acumatica

If you want to create custom Attribute for autonumbering field you need:

  1. Create Setup page for configuring autonumbering field or maybe you can use existing
  2. Create Attribute which you'll add to your entity
  3. Add attribute to field that you need increment 

Create Setup page for configuration autonumbering field

Setup.cs like this:

    [System.SerializableAttribute()]
    [PXPrimaryGraph(typeof(CurrencyMaint))]
    [PXCacheName("Your company Preferences")]
    public class Setup : PX.Data.IBqlTable
    {
        #region DocumentRefNbr
        public abstract class documentRefNbr : PX.Data.IBqlField
        {
        }
        protected string _DocumentRefNbr;
        [PXDBString(15, IsUnicode = true)]
        [PXDefault("00010")]
        [PXUIField(DisplayName = "Document Last Ref. Number")]
        public virtual string DocumentLastDocNbr
        {
            get
            {
                return this._DocumentRefNbr;
            }
            set
            {
                this._DocumentRefNbr = value;
            }
        }
        #endregion
        #region ReturnDocRefNbr
        public abstract class returnLastDocNbr : PX.Data.IBqlField
        {
        }
        protected string _ReturnLastDocRefNbr;
        [PXDBString(15, IsUnicode = true)]
        [PXDefault("00010")]
        [PXUIField(DisplayName = "Return Documnet Ref. Number")]
        public virtual string ReturnLastDocNbr
        {
            get
            {
                return this._ReturnLastDocRefNbr;
            }
            set
            {
                this._ReturnLastDocRefNbr = value;
            }
        }
        #endregion
        
        #region AutoNumbering
        public abstract class autoNumbering : PX.Data.IBqlField
        {
        }
        protected bool? _AutoNumbering;
        [PXDBBool()]
        [PXDefault(true, PersistingCheck = PXPersistingCheck.Nothing)]
        [PXUIField(DisplayName = "Auto Numbering")]
        public virtual bool? AutoNumbering
        {
            get
            {
                return this._AutoNumbering;
            }
            set
            {
                this._AutoNumbering = value;
            }
        }
        #endregion
    }

We create 3 fields:

  1. DocumentLastDocNbr - for setting start position to numbering;
  2. ReturnLastDocNbr - for get last number from db
  3. AutoNumbering bool field for setting auto or manual numbering

Next, create graph SetupMaint for Setup page:

public class SetupMaint : PXGraph<SetupMaint>
    {
        public PXSave<Setup> Save;
        public PXCancel<Setup> Cancel;
 
        public PXSelect<Setup> LastNumbers;
    }

Next, create view, page YC101000:

<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormView.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="YC101000.aspx.cs" Inherits="Page_YC101000" Title="Untitled Page" %>
<%@ MasterType VirtualPath="~/MasterPages/FormView.master" %>
 
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
	<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="CurrencyApplication.SetupMaint" PrimaryView="LastNumbers">
	</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" Runat="Server">
	<px:PXFormView ID="form" runat="server" DataSourceID="ds" Style="z-index100" Width="100%" DataMember="LastNumbers" TabIndex="800">
		<Template>
			<px:PXLayoutRule runat="server" StartRow="True" ControlSize="SM" LabelsWidth="SM"/>
			<px:PXTextEdit ID="edLastDocNbr" runat="server" DataField="DocumentLastDocNbr">
			</px:PXTextEdit>
			<px:PXTextEdit ID="edReturnLastDocNbr" runat="server" DataField="ReturnLastDocNbr">
			</px:PXTextEdit>
		   <px:PXCheckBox ID="edAutoNumbering" runat="server" AlignLeft="True" DataField="AutoNumbering" Text="Auto Numbering">
			</px:PXCheckBox>
		</Template>
		<AutoSize Container="Window" Enabled="True" MinHeight="200" />
	</px:PXFormView>
</asp:Content>

                     

Create Attribute

After it, you can create attribute:

public class AutoNumberAttribute : PXEventSubscriberAttribute,
                                        IPXFieldDefaultingSubscriberIPXFieldVerifyingSubscriber,
                                        IPXRowPersistingSubscriberIPXRowPersistedSubscriber
    {
        public const string NewValue = "";
 
        private bool _AutoNumbering;
        private Type _AutoNumberingField;
        private BqlCommand _LastNumberCommand;
 
     
        public virtual Type LastNumberField { getprivate set; }
        public static void SetLastNumberField<Field>(PXCache sender, object row, Type lastNumberField)
            where Field : IBqlField
        {
            foreach (PXEventSubscriberAttribute attribute in sender.GetAttributes<Field>(row))
            {
                if (attribute is AutoNumberAttribute)
                {
                    AutoNumberAttribute attr = (AutoNumberAttribute)attribute;
                    attr.LastNumberField = lastNumberField;
                    attr.CreateLastNumberCommand();
                }
            }
        }
 
       public AutoNumberAttribute(Type autoNumbering)
        {
            if (autoNumbering != null &&
                (typeof(IBqlSearch).IsAssignableFrom(autoNumbering) ||
                 typeof(IBqlField).IsAssignableFrom(autoNumbering) && autoNumbering.IsNested))
            {
                _AutoNumberingField = autoNumbering;
            }
            else
            {
                throw new PXArgumentException("autoNumbering");
            }
        }
 
        public AutoNumberAttribute(Type autoNumbering, Type lastNumberField)
            : this(autoNumbering)
        {
            LastNumberField = lastNumberField;
            CreateLastNumberCommand();
        }
 
        private void CreateLastNumberCommand()
        {
            _LastNumberCommand = null;
 
            if (LastNumberField != null)
            {
                if (typeof(IBqlSearch).IsAssignableFrom(LastNumberField))
                    _LastNumberCommand = BqlCommand.CreateInstance(LastNumberField);
                else if (typeof(IBqlField).IsAssignableFrom(LastNumberField) && LastNumberField.IsNested)
                    _LastNumberCommand = BqlCommand.CreateInstance(typeof(Search<>), LastNumberField);
            }
 
            if (_LastNumberCommand == nullthrow new PXArgumentException("lastNumberField");
        }
 
        public override void CacheAttached(PXCache sender)
        {
            BqlCommand command = null;
            Type autoNumberingField = null;
            if (typeof(IBqlSearch).IsAssignableFrom(_AutoNumberingField))
            {
                command = BqlCommand.CreateInstance(_AutoNumberingField);
                autoNumberingField = ((IBqlSearch)command).GetField();
            }
            else
            {
                command = BqlCommand.CreateInstance(typeof(Search<>), _AutoNumberingField);
                autoNumberingField = _AutoNumberingField;
            }
            PXView view = new PXView(sender.Graph, true, command);
            object row = view.SelectSingle();
            if (row != null)
            {
                _AutoNumbering = (bool)view.Cache.GetValue(row, autoNumberingField.Name);
            }
        }
 
        public virtual void FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
        {
            if (_AutoNumbering)
            {
                e.NewValue = NewValue;
            }
        }
 
        public virtual void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
        {
            if (_AutoNumbering && PXSelectorAttribute.Select(sender, e.Row, _FieldName, e.NewValue) == null)
            {
                e.NewValue = NewValue;
            }
        }
 
        protected virtual string GetNewNumber(PXCache sender, Type setupType)
 
        { 
 
            if (_LastNumberCommand == null)
                CreateLastNumberCommand();
            PXView view = new PXView(sender.Graph, false, _LastNumberCommand);
            object row = view.SelectSingle();
            if (row == nullreturn null;
            long number;
 
            string lastNumber = (string)view.Cache.GetValue(row, LastNumberField.Name);
            number = Int64.Parse(lastNumber);
            number = number + 3;
 
         lastNumber = "0000" + Convert.ToString(number);
 
            view.Cache.SetValue(row, LastNumberField.Name, lastNumber);
            PXCache setupCache = sender.Graph.Caches[setupType];
            setupCache.Update(row);
            setupCache.PersistUpdated(row);
            return lastNumber;
        }
 
        public virtual void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
        {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert)
            {
                Type setupType = BqlCommand.GetItemType(_AutoNumberingField);
                string lastNumber = GetNewNumber(sender, setupType);
                if (lastNumber != null)
                {
                    sender.SetValue(e.Row, _FieldOrdinal, lastNumber);
                }
            }
        }
 
        public virtual void RowPersisted(PXCache sender, PXRowPersistedEventArgs e)
        {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert &&
                e.TranStatus == PXTranStatus.Aborted)
            {
                sender.SetValue(e.Row, _FieldOrdinal, NewValue);
                Type setupType = BqlCommand.GetItemType(_AutoNumberingField);
                sender.Graph.Caches[setupType].Clear();
            }
        }
    }

Pay attention that for correct work you must implement interfaces, and ovveride standart methods(RowPersisting.... etc, all that need)

Main function for generating new walue is GetNewNumber; New walue will be bigger on 3;

Ok, here we described method - 

SetLastNumberField()

This method we call from graph before when we click save data to database in event RowPersisting:

Add attribute to field that you need increment;

public class CurrencyMaint : PXGraph<CurrencyMaintUsrCurrency>
    {
 
        public PXFilter<ExchangeDateFilter> Filter;
        public PXSelect<UsrCurrencyWhere<UsrCurrency.currencyExchangeDateEqual<Current<ExchangeDateFilter.exchangeDate>>>> Currencies;
 
        public PXSetup<Setup> AutoNumSetup;
 
 
        public CurrencyMaint()
        {
            Setup setup = AutoNumSetup.Current;
        }
 
        protected virtual void UsrCurrency_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
        {
            UsrCurrency doc = (UsrCurrency)e.Row;
            if (sender.GetStatus(doc) == PXEntryStatus.Inserted)
            {
                AutoNumberAttribute.SetLastNumberField<UsrCurrency.extRefNbr>(
                    sender, doc,
                    typeof(Setup.returnLastDocNbr));
            }
        }
    }

So for example I have DAC class and field ExtRefNbr I want to set autonumber attrbute, to do this try: [AutoNumber(typeof(Setup.autoNumbering))]

Of course don`t forget to create aspx page :)

[System.SerializableAttribute()]
    public class UsrCurrency : PX.Data.IBqlTable
    {
        
        public abstract class extRefNbr : PX.Data.IBqlField
        {
 
        }
        protected string _ExtRefNbr;
        [PXDBString(40, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
        [PXDefault("00000000")]
        [PXUIField(DisplayName = "Document Ref.")]
         [AutoNumber(typeof(Setup.autoNumbering))]
        public virtual string ExtRefNbr
        {
            get
            {
                return this._ExtRefNbr;
            }
            set
            {
                this._ExtRefNbr = value;
 
            }
        }
 
        #region CurrencyID
        #endregion         #region CurrencyName         #endregion         #region CurrencyRate             #endregion         #region CurrencyCC                 #endregion     }

And after configure Setup page:

And try to add new rows, and test autonumber:

Enjoy the result )

2 Comments

  • Angie said

    Hi Yuriy,

    Excuse me, I have a question...

    What is ExchangeDateFilter?.

    But I don't know how implement these filters

    I hope your answer. Thank very much :)

  • docotor said

    Update:

    below goes class ExchangeDateFilter:

    [Serializable]
    public class ExchangeDateFilter : IBqlTable
    {
    #region ExchangeDate
    public abstract class exchangeDate : PX.Data.IBqlField
    {
    }
    [PXDate()]
    [PXDefault( typeof(AccessInfo.businessDate))]
    [PXUIField(DisplayName = "Currency ExchangenDate")]
    [PXSelector(
    typeof(Search<UsrCurrency.currencyExchangeDate>))]

    public virtual DateTime? ExchangeDate { get; set; }
    #endregion
    }

    This filter not need for autonumbering, it is for select all from UsrCurrency table where date equal current date.

Add a Comment
Comments are closed