Initial commit: JDE Scoping Tool migration project

Set up repository with legacy .NET Framework 4.8 source (OLD/),
new .NET 10 Blazor solution (NEW/), OpenSpec specifications,
documentation, and project configuration.
This commit is contained in:
Joseph Doherty
2026-01-02 07:43:29 -05:00
commit 26ff8d9b4f
1761 changed files with 596509 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
using System;
namespace DataModel.Helpers
{
/// <summary>
/// JDE date/time conversion helpers
/// </summary>
public static class DateTimeHelpers
{
/// <summary>
/// Strips time component from datetime
/// </summary>
/// <param name="source">Datetime to strip time component from</param>
/// <returns>Date component of source datetime</returns>
public static int ToJDEDate(this DateTime source)
{
return (source.Year < 2000 ? 0 : 100000) + (source.Year % 100) * 1000 + source.DayOfYear;
}
public static DateTime FromJDEDate(this int sourceDate)
{
if (sourceDate == 0) { return new DateTime(1900, 1, 1); }
DateTime baseDate = new DateTime(1900, 1, 1);
try
{
string strSource = sourceDate.ToString();
if (strSource.Length < 5 || strSource.Length > 6) { throw new Exception($"invalid source date length ({strSource.Length})"); }
if (strSource.StartsWith("1"))
{
baseDate = new DateTime(2000, 1, 1);
}
baseDate = baseDate.AddYears(int.Parse(strSource.Substring(1, 2)));
baseDate = baseDate.AddDays(int.Parse(strSource.Substring(3)) - 1);
}
catch
{
//Do nothing
}
return baseDate;
}
/// <summary>
/// Strips date component from datetime
/// </summary>
/// <param name="source">Datetime to strip date component from</param>
/// <returns>Time component of source datetime</returns>
public static int ToJDETime(this DateTime source)
{
return source.Hour * 10000 + source.Minute * 100 + source.Second;
}
/// <summary>
/// Converts the JDE date + time components into a datetime
/// </summary>
/// <param name="sourceDate">JDE date component</param>
/// <param name="sourceTime">JDE time component</param>
/// <returns>Combined datetime from source JDE date/time components</returns>
public static DateTime FromJDEDateTime(this DateTime sourceDate, int sourceTime)
{
try
{
int hours = sourceTime / 10000;
int minutes = (sourceTime % 10000) / 100;
int seconds = sourceTime % 100;
return sourceDate.Date.AddHours(hours).AddMinutes(minutes).AddSeconds(seconds);
}
catch
{
return sourceDate;
}
}
}
}
+141
View File
@@ -0,0 +1,141 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace DataModel.Helpers
{
/// <summary>
/// Helper methods for encryption / decryption
/// </summary>
public class EncryptionHelper
{
/// <summary>
/// Default salt value
/// </summary>
private const string DEFAULT_SALT = ")kjdnl3k1jh234a9";
/// <summary>
/// Default hashing algorithm to use
/// </summary>
private const string DEFAULT_HASH_ALGORITHM = "SHA1";
/// <summary>
/// Default number of iterations to do
/// </summary>
private const int DEFAULT_PASSWORD_ITERATION = 2;
/// <summary>
/// Default initialization vector
/// </summary>
private const string DEFAULT_INITIAL_VECTOR = "X8pgVu239uOjdKH1";
/// <summary>
/// Default encryption keysize
/// </summary>
private const int DEFAULT_KEYSIZE = 256;
/// <summary>
/// Encrypts a string
/// </summary>
/// <param name="plainText">Text to be encrypted</param>
/// <param name="password">Password to encrypt with</param>
/// <param name="salt">Salt to encrypt with</param>
/// <param name="hashAlgorithm">Can be either SHA1 or MD5</param>
/// <param name="passwordIterations">Number of iterations to do</param>
/// <param name="initialVector">Needs to be 16 ASCII characters long</param>
/// <param name="keySize">Can be 128, 192, or 256</param>
/// <returns>An encrypted string</returns>
public static string Encrypt(string plainText, string password, string salt = DEFAULT_SALT, string hashAlgorithm = DEFAULT_HASH_ALGORITHM, int passwordIterations = DEFAULT_PASSWORD_ITERATION, string initialVector = DEFAULT_INITIAL_VECTOR, int keySize = DEFAULT_KEYSIZE)
{
if (string.IsNullOrEmpty(plainText))
{
return "";
}
byte[] initialVectorBytes = Encoding.ASCII.GetBytes(initialVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(salt);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes derivedPassword = new PasswordDeriveBytes(password, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = derivedPassword.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged
{
Mode = CipherMode.CBC
};
byte[] cipherTextBytes = null;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initialVectorBytes))
{
using (MemoryStream memStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherTextBytes = memStream.ToArray();
memStream.Close();
cryptoStream.Close();
}
}
}
symmetricKey.Clear();
return Convert.ToBase64String(cipherTextBytes);
}
/// <summary>
/// Decrypts a string
/// </summary>
/// <param name="encrypted">Text to be decrypted</param>
/// <param name="password">Password to decrypt with</param>
/// <param name="salt">Salt to decrypt with</param>
/// <param name="hashAlgorithm">Can be either SHA1 or MD5</param>
/// <param name="passwordIterations">Number of iterations to do</param>
/// <param name="initialVector">Needs to be 16 ASCII characters long</param>
/// <param name="keySize">Can be 128, 192, or 256</param>
/// <returns>A decrypted string</returns>
public static string Decrypt(string encrypted, string password, string salt = DEFAULT_SALT, string hashAlgorithm = DEFAULT_HASH_ALGORITHM, int passwordIterations = DEFAULT_PASSWORD_ITERATION, string initialVector = DEFAULT_INITIAL_VECTOR, int keySize = DEFAULT_KEYSIZE)
{
if (string.IsNullOrEmpty(encrypted))
{
return "";
}
byte[] initialVectorBytes = Encoding.ASCII.GetBytes(initialVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(salt);
byte[] cipherTextBytes = Convert.FromBase64String(encrypted);
PasswordDeriveBytes derivedPassword = new PasswordDeriveBytes(password, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = derivedPassword.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged
{
Mode = CipherMode.CBC
};
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int byteCount = 0;
using (ICryptoTransform decrypt = symmetricKey.CreateDecryptor(keyBytes, initialVectorBytes))
{
using (MemoryStream memStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memStream, decrypt, CryptoStreamMode.Read))
{
byteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memStream.Close();
cryptoStream.Close();
}
}
}
symmetricKey.Clear();
return Encoding.UTF8.GetString(plainTextBytes, 0, byteCount);
}
}
}
+234
View File
@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using Fasterflect;
namespace DataModel.Helpers
{
/// <summary>
/// IEnumerable extension helpers
/// </summary>
public static class GenericListDataReaderExtensions
{
/// <summary>
/// Generates generic data reader for bulk copies from list
/// </summary>
/// <typeparam name="T">Element data type</typeparam>
/// <param name="list">Source list</param>
/// <returns>Data reader to parse list contents</returns>
public static GenericListDataReader<T> GetDataReader<T>(this IEnumerable<T> list) where T : class
{
return new GenericListDataReader<T>(list);
}
}
/// <summary>
/// Data reader that parses list of generic elements
/// </summary>
public class GenericListDataReader : IDataReader
{
public int Count { get; set; }
private readonly IEnumerator<object> list = null;
private readonly List<PropertyInfo> properties = new List<PropertyInfo>();
private readonly Dictionary<string, int> nameLookup = new Dictionary<string, int>();
private readonly string[] propertyNames;
public GenericListDataReader(IEnumerable<object> list, Type type)
{
this.list = list.GetEnumerator();
properties.AddRange(type.GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly));
propertyNames = new string[properties.Count];
for (int i = 0; i < properties.Count; i++)
{
nameLookup[properties[i].Name] = i;
propertyNames[i] = properties[i].Name;
}
}
public void Close()
{
list.Dispose();
}
public int Depth => throw new NotImplementedException();
public DataTable GetSchemaTable()
{
throw new NotImplementedException();
}
public bool IsClosed => false;
public bool NextResult()
{
throw new NotImplementedException();
}
public bool Read()
{
bool readResult = list.MoveNext();
//Update counter
if (readResult)
{
Count++;
}
return readResult;
}
public int RecordsAffected => throw new NotImplementedException();
public void Dispose()
{
Close();
}
public int FieldCount => properties.Count;
public bool GetBoolean(int i)
{
return (bool)GetValue(i);
}
public byte GetByte(int i)
{
return (byte)GetValue(i);
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
return (char)GetValue(i);
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public IDataReader GetData(int i)
{
throw new NotImplementedException();
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public DateTime GetDateTime(int i)
{
return (DateTime)GetValue(i);
}
public decimal GetDecimal(int i)
{
return (decimal)GetValue(i);
}
public double GetDouble(int i)
{
return (double)GetValue(i);
}
public Type GetFieldType(int i)
{
return properties[i].PropertyType;
}
public float GetFloat(int i)
{
return (float)GetValue(i);
}
public Guid GetGuid(int i)
{
return (Guid)GetValue(i);
}
public short GetInt16(int i)
{
return (short)GetValue(i);
}
public int GetInt32(int i)
{
return (int)GetValue(i);
}
public long GetInt64(int i)
{
return (long)GetValue(i);
}
public string GetName(int i)
{
return properties[i].Name;
}
public int GetOrdinal(string name)
{
if (nameLookup.ContainsKey(name))
{
return nameLookup[name];
}
else
{
return -1;
}
}
public string GetString(int i)
{
return (string)GetValue(i);
}
public object GetValue(int i)
{
string name = propertyNames[i];
return list.Current.GetPropertyValue(name);
}
public int GetValues(object[] values)
{
int getValues = Math.Max(FieldCount, values.Length);
for (int i = 0; i < getValues; i++)
{
values[i] = GetValue(i);
}
return getValues;
}
public bool IsDBNull(int i)
{
return GetValue(i) == null;
}
public object this[string name] => list.Current.GetPropertyValue(name);
public object this[int i] => GetValue(i);
}
/// <summary>
/// Data reader that parses list of generic elements
/// </summary>
/// <typeparam name="T">Element data type</typeparam>
public class GenericListDataReader<T> : GenericListDataReader where T : class
{
public GenericListDataReader(IEnumerable<T> list)
: base(list, typeof(T))
{
}
}
}
+42
View File
@@ -0,0 +1,42 @@
using Newtonsoft.Json;
namespace DataModel.Helpers
{
/// <summary>
/// JSON serialization helpers
/// </summary>
public static class JsonHelpers
{
/// <summary>
/// Converts parameter to JSON
/// </summary>
/// <param name="value">Value to serialize</param>
/// <returns>Parameter value serialized in JSON format</returns>
public static string ToJSON<T>(this T value)
{
return value == null ?
"{}" :
JsonConvert.SerializeObject(value);
}
/// <summary>
/// Parses value from JSON
/// </summary>
/// <param name="json">JSON to parse</param>
/// <returns>Parsed value</returns>
public static T FromJSON<T>(string json)
{
T value = default;
try
{
value = JsonConvert.DeserializeObject<T>(json);
}
catch
{
//Do nothing
}
return value;
}
}
}
+59
View File
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
namespace DataModel.Helpers
{
/// <summary>
/// LINQ helper methods
/// </summary>
public static class LinqHelpers
{
/// <summary>
/// Filters for distinct elements by given key
/// </summary>
/// <typeparam name="TSource">Source data type</typeparam>
/// <typeparam name="TKey">Key data type</typeparam>
/// <param name="source">Source data</param>
/// <param name="keySelector">Key value function</param>
/// <returns>Distinct elements from source</returns>
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> keys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (keys.Add(keySelector(element)))
{
yield return element;
}
}
}
/// <summary>
/// Groups the data into batches of give size
/// </summary>
/// <typeparam name="T">Data type of data</typeparam>
/// <param name="allData">Data to break into batches</param>
/// <param name="batchSize">Size of batches to create</param>
/// <returns>Enumerable collection of batches to process</returns>
public static IEnumerable<List<T>> BatchGroup<T>(this IEnumerable<T> allData, int batchSize)
{
List<T> batchGroup = new List<T>();
//Loop through data and group into batches
foreach (T data in allData)
{
batchGroup.Add(data);
if (batchGroup.Count == batchSize)
{
//Return batch of data
yield return batchGroup;
batchGroup = new List<T>();
}
}
//Return remaining data
yield return batchGroup;
}
}
}
+53
View File
@@ -0,0 +1,53 @@
using System;
using System.Data.SqlClient;
namespace DataModel.Helpers
{
/// <summary>
/// SQL server ADO helpers
/// </summary>
public static class SqlHelpers
{
/// <summary>
/// Binds the parameter to the command
/// </summary>
/// <param name="command">Command to bind parameter to</param>
/// <param name="parameterName">Name of parameter</param>
/// <param name="parameterValue">Value of parameter</param>
/// <returns>Bound parameter</returns>
public static SqlParameter Bind(this SqlCommand command, string parameterName, object parameterValue)
{
return parameterValue != null
? command.Parameters.AddWithValue(parameterName, parameterValue)
: command.Parameters.AddWithValue(parameterName, DBNull.Value);
}
/// <summary>
/// Binds the parameter to the command
/// </summary>
/// <param name="command">Command to bind parameter to</param>
/// <param name="parameterName">Name of parameter</param>
/// <param name="parameterValue">Value of parameter</param>
/// <returns>Bound parameter</returns>
public static SqlParameter Bind(this SqlCommand command, string parameterName, string parameterValue)
{
return !string.IsNullOrEmpty(parameterValue) ?
command.Parameters.AddWithValue(parameterName, parameterValue) :
command.Parameters.AddWithValue(parameterName, DBNull.Value);
}
/// <summary>
/// Binds the parameter to the command
/// </summary>
/// <param name="command">Command to bind parameter to</param>
/// <param name="parameterName">Name of parameter</param>
/// <param name="parameterValue">Value of parameter</param>
/// <returns>Bound parameter</returns>
public static SqlParameter Bind(this SqlCommand command, string parameterName, byte[] parameterValue)
{
return parameterValue != null ?
command.Parameters.AddWithValue(parameterName, parameterValue) :
command.Parameters.AddWithValue(parameterName, DBNull.Value);
}
}
}