Creating Custom Autonumber Autogenerated Id In Acumatica
If you want to create custom Attribute for autonumbering field you need:
- Create Setup page for configuring autonumbering field or maybe you can use existing
- Create Attribute which you'll add to your entity
- 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:
- DocumentLastDocNbr - for setting start position to numbering;
- ReturnLastDocNbr - for get last number from db
- 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-index: 100" 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, IPXFieldDefaultingSubscriber, IPXFieldVerifyingSubscriber, IPXRowPersistingSubscriber, IPXRowPersistedSubscriber { public const string NewValue = ""; private bool _AutoNumbering; private Type _AutoNumberingField; private BqlCommand _LastNumberCommand; public virtual Type LastNumberField { get; private 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 == null) throw 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 == null) return 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<CurrencyMaint, UsrCurrency> { public PXFilter<ExchangeDateFilter> Filter; public PXSelect<UsrCurrency, Where<UsrCurrency.currencyExchangeDate, Equal<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 7 months ago
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 6 months ago
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.