Click or drag to resize
SequenceExtension Class
Provides way to implement an extension to CUSTOMTOOLS sequence making it possible to implement custom sequence number generator. This can be used for instance to implement logic to generate a number from number series generator of an external system such as from ERP.
Inheritance Hierarchy
SystemObject
  CTExtensions.InterfacesSequenceExtension

Namespace: CTExtensions.Interfaces
Assembly: CTInterface (in CTInterface.dll)
Syntax
public abstract class SequenceExtension : IExtensionInterface

The SequenceExtension type exposes the following members.

Methods
  NameDescription
Public methodGenerateNextValue
This is called when next number should be generated and reserved.
Public methodGetDependentPropertyID
If you wish to get value of some property when generating serial, return that property's id here so it value will be provided at PeakNextValue and GenerateNextValue calls.
Public methodGetName
Name of the sequence that is shown in CUSTOMTOOLS UI
Public methodGetParent
Return parent CTExtension of this options extension.
Public methodNotifyInitialized
Notification that is called when this sequence is initialized by the CT core. Note that ID might change and so DO NOT store any information based on this ID.
Public methodPeakNextValue
Provide next value if possible to preview it in the GetCode dialog box but don't reserve it yet. CUSTOMTOOLS supports showing the next number only when it's possible. You can also return an empty string here.
Public methodRollback
This is called when previously reserved number should be rolled back. This may occur if user cancels or an error occurs after number is generated (reserved) but before it's really used. In that case previously reserved number should be released to be re-used.
Top
Examples
This example shows how to use SequenceExtension to implement own number sequence generator in CUSTOMTOOLS. In this example, a text file to keep next available free number is automatically created in the same folder than the script itself is compiled. This example creates a new sequence CT API Number Generator that generates a number that is composed from three different elements. The first element is current year, the second one active project's number and the third one running number sequence expressed in five digits so that number is padded with leading zeros. This example doesn't work in multi-user environment as text file to keep record of next available number is saved to local computer for simplicity. Code is relatively easy to change for your own purposes. Typically, this is used to retrieve number from external system such as from ERP for example using REST ro SOAP web services.
C#
// @AUTO-REFERENCE { [CT_INSTALL_PATH]\CTInterface.dll }
// @AUTO-REFERENCE { [CT_INSTALL_PATH]\Interop.CTEngineLib.dll }
// @AUTO-REFERENCE { C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Windows.Forms.dll }

using ATR.CT.CTInterface;
using CTExtensions;
using CTEngineLib;
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using CTExtensions.Interfaces;

public class MyAddin : CTExtension
{
  /// <summary>
  /// One and only instance of number generator
  /// </summary>
  private NumberGenerator oMyNumberGenerator;

  /// <summary>
  /// Name of the number sequence that appears in user interface
  /// </summary>
  private string oSequenceName = "CT API Number Generator";

  public override bool Hook(CTInterface oCTInterface)
  {
    bool bHooked = false;
    if (oCTInterface != null)
    {
      // Hook to CTInterface events and set bHooked = true.
    }
    return bHooked;
  }

  public override void UnHook(CTInterface oCTInterface)
  {
    if (oCTInterface != null)
    {
      // Un-Hook all handlers from CTInterface
    }
  }

  /// <summary>
  /// Notification that Extension is now initialized by CUSTOMTOOLS and for instance Database and
  /// Profile members are now valid and thus safe to use. Inheritance may return false to prevent
  /// initialized status for eg. licensing purposes.
  /// </summary>
  /// <returns>True when this object is fully initialize and OK to use by CUSTOMTOOLS;
  /// otherwise false</returns>
  public override bool InitNotify()
  {
    Init();
    return base.InitNotify();
  }

  /// <summary>
  /// Implementation of this function should return objects implementing the requested  
  /// type in every case if this extension supports the interface, including when
  /// this extension is not initialized. If this extension is not initialized,
  /// the only function that might get called is GetParent().
  /// </summary>
  /// <param name="eType">Requested type</param>
  /// <returns>Object array corresponding to requested type.</returns>
  public override IExtensionInterface[] GetInterface2(ctExtensionInterface eType)
  {
    if(oMyNumberGenerator != null && eType == ctExtensionInterface.ctExtensionInterfaceSequence)
    {
      return new[] { oMyNumberGenerator };
    }

    return base.GetInterface2(eType);
  }

  /// <summary>
  /// Performs all initialization
  /// </summary>
  private void Init()
  {
    // Init number sequence generator
    oMyNumberGenerator = new NumberGenerator(this, oSequenceName);
  }
}

/// <summary>
/// Provides way to implement an extension to CUSTOMTOOLS sequence
/// </summary>
public class NumberGenerator : CTExtensions.Interfaces.SequenceExtension
{
  /// <summary>
  /// This is the name of the sequence shown in UI
  /// </summary>
  private string moName;

  /// <summary>
  /// CUSTOMTOOLS API Extension that has created me
  /// </summary>
  private MyAddin moCTExtension;

  /// <summary>
  /// The first number to book when using this example
  /// </summary>
  private int moSeedNumber = 1;

  /// <summary>
  /// Name of the text file in which to keep track of next available number. The
  /// file is automatically created in the script folder for simplicity. Thus this
  /// example doesn't support sharing numbers across multiple users. For that purpose using
  /// this example you could save the file in the script's additional files for instance.
  /// </summary>
  private string moSequenceTextFile = "CTAPISerial.txt";

  /// <summary>
  /// Use this to construct a new custom number generator
  /// </summary>
  /// <param name="parent">CUSTOMTOOLS API Extension that creates me.</param>
  /// <param name="sequenceName">Name of the sequence that appears in user interface to identify me</param>
  public NumberGenerator(MyAddin parent, string sequenceName)
  {
    moCTExtension = parent;
    moName = sequenceName;
  }

  /// <summary>
  /// This is called when next number should be generated
  /// </summary>
  /// <param name="poActiveProject">Currently active CUSTOMTOOLS project. This can be used to generate
  /// a project specific number. This may be null for instance in the Options.</param>
  /// <param name="bsDependentPropVal">Value of the dependent custom property if defined in the options that number
  /// should be generated per value of a custom property.</param>
  /// <param name="pbsExternalID">Specify ID for the sequence you generated. This is passed to Rollback method if
  /// user later on cancels number generation and we should release the number to be re-used.</param>
  /// <returns>Formatted serial number</returns>
  public override string GenerateNextValue(CTProject poActiveProject, string bsDependentPropVal, out string pbsExternalID)
  {
    // The pbsExternalID is how you can identify the serial you return here if it's going to get rolled back, so
    // you might want to have a Dictionary for pbsExternalID to what ever data you need to roll back the generated number.

    pbsExternalID = Guid.NewGuid().ToString();

    return GetNextAvailableNumber(poActiveProject, true);
  }

  /// <summary>
  /// Name of this sequence that is shown in CUSTOMTOOLS UI
  /// </summary>
  /// <returns>Identifying name of this sequence.</returns>
  public override string GetName()
  {
    return moName;
  }

  /// <summary>
  /// Return parent CTExtension of this options extension.
  /// </summary>
  /// <returns>Return parent CTExtension of this options extension.</returns>
  public override ICTExtension GetParent()
  {
    return moCTExtension;
  }

  /// <summary>
  /// Notification that is called when this sequence is initialized by the CT core.
  /// Note that ID might change and so DO NOT store any information based on this ID.
  /// </summary>
  /// <param name="lSequenceID">ID of the sequence for this session.</param>
  public override void NotifyInitialized(int lSequenceID)
  {
    // Make sure that local text file exists to keep record of next available number
    string sequenceTextFile = GetTextFileFullPath;

    try
    {
      if (!File.Exists(sequenceTextFile))
      {
        File.WriteAllText(sequenceTextFile, moSeedNumber.ToString());
      }
    }
    catch(Exception e)
    {
      MessageBox.Show(string.Format("Failed to initialize number generator. Reason: {0}",
        e.Message));
    }
  }

  /// <summary>
  /// Provide next value if possible to preview it in the GetCode dialog box but don't
  /// reserve it yet. CUSTOMTOOLS supports showing the next number only when it's possible.
  /// You can also return an empty string here.
  /// </summary>
  /// <param name="poActiveProject">Currently active CUSTOMTOOLS project. This can be used to generate
  /// a project specific number. This may be null for instance in the Options.</param>
  /// <param name="bsDependentPropVal">Value of the dependent custom property if requested.</param>
  /// <returns>Formatted serial number</returns>
  public override string PeakNextValue(CTProject poActiveProject, string bsDependentPropVal)
  {
    return GetNextAvailableNumber(poActiveProject, false);
  }

  /// <summary>
  /// This is called when previously reserved number should be rolled back. This may occur if user cancels
  /// or an error occurs after number is generated (reserved) but before it's really used. In that case
  /// previously reserved number should be released to be re-used.
  /// </summary>
  /// <param name="bsExternalID">ID that matches external ID given at GenerateNextValue</param>
  public override void Rollback(string bsExternalID)
  {
    // This example doesn't support rolling back numbers.
  }

  #region Helper Functions

  /// <summary>
  /// Gets next available number in format year-project number-number. For example 2020-251-00001.
  /// </summary>
  /// <param name="project">Currently selected active CUSTOMTOOLS project. This is null for instance
  /// in the Options dialog box.</param>
  /// <param name="reserve">True to also reserve number; false to just get it.</param>
  /// <returns>Next available number in format year-project number- number if successful; 
  /// otherwise an empty string.</returns>
  private string GetNextAvailableNumber(CTProject project, bool reserve)
  {
    string generatedNumber = string.Empty;
    string sequenceTextFile = GetTextFileFullPath;

    try
    {
      int nextAvailableNumber = int.Parse(File.ReadAllText(sequenceTextFile));
      generatedNumber = string.Format("{0}-{2}-{1:00000}", 
        DateTime.Now.Year, nextAvailableNumber, 
        project == null ? string.Empty : project.ProjectNumber);
      if (reserve)
      {
        File.WriteAllText(sequenceTextFile, (++nextAvailableNumber).ToString());
      }
    }
    catch(Exception e)
    {
      MessageBox.Show(string.Format("Failed to get next available number. Reason: {0}",
        e.Message));
    }

    return generatedNumber;
  }

  /// <summary>
  /// Call this to resolve path of text file that keeps track of next available number.
  /// Text file resides in the same directory than this script is compiled. Text file
  /// should only contain next available number.
  /// </summary>
  /// <returns>Full path to the text file to keep the record of next available number.</returns>
  private string GetTextFileFullPath
  {
    get
    {
      string codeBase = Assembly.GetExecutingAssembly().CodeBase;
      UriBuilder uri = new UriBuilder(codeBase);
      string path = Uri.UnescapeDataString(uri.Path);
      return Path.Combine(Path.GetDirectoryName(path), moSequenceTextFile);
    }
  }
  #endregion
}
Availability

CUSTOMTOOLS 2019 SP0


See Also