using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using GB5Shared.DTO.Framework.CodeDefine; using GB5Shared.DTO.Framework.Entity; using GB5Shared.DTO.Framework.Login; using GB5Shared.GB5Exception; using GB5Shared.Query.CodeGeneration; using GB5Shared.QueryExecutor; namespace GB5Shared.GenerateAutoNumber { /// /// Generic Code Generation Service for all entities /// Supports 4 generation types: Auto(0), Manual(1), Formula(2), Hidden(3) /// public class CodeGeneration { private readonly IQueryExecutor _queryExecutor; public CodeGeneration(IQueryExecutor queryExecutor) { _queryExecutor = queryExecutor; } /// /// Assigns code to any entity object based on CodeDefine configuration /// Works with any DTO: EmployeeDTO, CustomerDTO, ProductDTO, etc. /// /// Entity DTO type /// Entity name (e.g., "Employee", "Customer", "Product") /// Entity object /// Login context /// Object with assigned code public async Task AssignCode(string entityName, T obj, LoginDTO loginDto) { if (string.IsNullOrWhiteSpace(entityName)) throw new ArgumentException("Entity name is required", nameof(entityName)); if (obj == null) throw new ArgumentNullException(nameof(obj)); if (loginDto == null) throw new ArgumentNullException(nameof(loginDto)); var originalDatabase = loginDto.DatabaseName; try { string entityPrefix = ExtractEntityPrefix(obj.GetType().ToString()); var entityDto = await GetEntityByCodeAsync(entityName, loginDto); if (entityDto == null) throw new NotFoundException($"Entity '{entityName}' not found in MENTITY table"); if (entityDto.EntityIsCodeApplicable == 1) return obj; var codeDefines = await LoadCodeDefinesAsync(entityDto, obj, loginDto); var codeProperty = obj.GetType().GetProperty(entityPrefix + "Code"); var currentCode = codeProperty?.GetValue(obj, null) as string; // If no CodeDefine exists — fallback to manual only if code is provided if (codeDefines == null || codeDefines.Count == 0) { if (!string.IsNullOrWhiteSpace(currentCode)) return obj; throw new NotFoundException( $"No CodeDefine found and no manual code entered for {entityName}"); } if (codeDefines.Count > 1) { // List the actual conflicting rows instead of a bare "expected only one" — // makes it immediately clear which CodeDefineId to send, and whether the // category filter ran at all (if this lists every configured category // unfiltered, the caller's chosen category value never made it through). string candidates = string.Join("; ", codeDefines.Select(cd => $"CodeDefineId={cd.CodeDefineId} ({cd.CodeDefineCategoryCode} - {cd.CodeDefineCategoryName})")); throw new FoundException( $"Entity '{entityName}' has {codeDefines.Count} CodeDefine categories configured, " + $"and no single one was selected: {candidates}. " + $"Set the '{entityPrefix}CodeDefineId' field to one of the CodeDefineId values above and resend."); } var codeDefine = codeDefines[0]; // Manual Mode: GENERATIONTYPE = 1 if (codeDefine.CodeDefineGenerationType == 1) { if (!string.IsNullOrWhiteSpace(currentCode)) { // Set CodeDefineId if property exists var codeDefineIdProp = obj.GetType().GetProperty("CodeDefineId"); if (codeDefineIdProp != null && codeDefineIdProp.PropertyType == typeof(int)) { codeDefineIdProp.SetValue(obj, codeDefine.CodeDefineId, null); } return obj; // Use manual code } throw new NotFoundException( $"GENERATIONTYPE is MANUAL. Please enter a code for {entityName}."); } // Auto Mode: GENERATIONTYPE = 0 or 2 await ProcessCodeGeneration(codeDefine, obj, entityPrefix, loginDto); // Set CodeDefineId if property exists var autoCodeDefineIdProp = obj.GetType().GetProperty("CodeDefineId"); if (autoCodeDefineIdProp != null && autoCodeDefineIdProp.PropertyType == typeof(int)) { autoCodeDefineIdProp.SetValue(obj, codeDefine.CodeDefineId, null); } return obj; } catch { loginDto.DatabaseName = originalDatabase; throw; } finally { loginDto.DatabaseName = originalDatabase; } } /// /// Extract entity prefix from full DTO type name /// Example: "PayRollDAL.DTO.Employee.EmployeeDTO" -> "Employee" /// private string ExtractEntityPrefix(string fullTypeName) { string[] parts = fullTypeName.Split('.'); string typeName = parts[parts.Length - 1]; // Remove "DTO" suffix if present if (typeName.Length > 3 && typeName.EndsWith("DTO", StringComparison.OrdinalIgnoreCase)) { return typeName.Substring(0, typeName.Length - 3); } return typeName; } /// /// Load CodeDefine records based on entity configuration /// Handles both simple and category-based scenarios /// private async Task> LoadCodeDefinesAsync( EntityDTO entityDto, T obj, LoginDTO loginDto) { // MENTITY.ISMULTICATEGORY/CATEGORYMEMBERID have proven unreliable/stale for some // tenants — e.g. EMPLOYEE here has ISMULTICATEGORY=0, yet genuinely has 2 MCODEDEFINE // rows configured (UNIEMP, Employee). So instead of trusting that flag to decide the // branch up front, always load every MCODEDEFINE row for this EntityId first, and only // fall back to category-based disambiguation when more than one row actually comes back. var allDefines = await QueryListAsync( loginDto, CodeGenerationQB.GET_CODEDEFINE_BY_ENTITYID, new { entityid = entityDto.EntityId }); if (allDefines == null || allDefines.Count <= 1) return allDefines ?? new List(); // More than one CodeDefine row exists for this entity — must disambiguate via category. // NOTE: -1 is this system's sentinel for "not set" — every other value, including large // negative IDs (e.g. -1399995678), is a real, valid foreign key. Do not use `<= 0` here. if (entityDto.EntityCategoryMemberId == -1) throw new FoundException( $"Multiple CodeDefine entries found for entity '{entityDto.EntityCode}', but MENTITY.CATEGORYMEMBERID " + "is not configured to disambiguate them."); var entityMember = await _queryExecutor.QuerySingleAsync( loginDto, CodeGenerationQB.GET_ENTITYMEMBERS, new { entitymemberid = entityDto.EntityCategoryMemberId }); if (entityMember == null) throw new NotFoundException("EntityMember not found in MENTITYMEMBER table"); // Extract the caller-specified category value from the object // (e.g. EmployeeDTO.EmployeeCodeDefineId, sent by the caller to pick which // CodeDefine category — UNIEMP vs Employee — applies to this save). var categoryValue = ExtractCategoryValue(obj, entityMember.EntityMemberMemberName); int? categoryCodeDefineId = categoryValue == null ? null : Convert.ToInt32(categoryValue); var matches = allDefines .Where(cd => categoryCodeDefineId.HasValue && cd.CodeDefineId == categoryCodeDefineId.Value) .ToList(); return matches; } /// /// Extract category value from object property (handles nested Id properties) /// private object ExtractCategoryValue(T obj, string propertyName) { var property = obj.GetType().GetProperty(propertyName); if (property == null) throw new NotFoundException($"Property '{propertyName}' not found in {obj.GetType().Name}"); object value = property.GetValue(obj, null); // Handle nested objects with Id property (e.g., Department.DepartmentId) if (value != null) { var valueType = value.GetType(); if (valueType.FullName != null && valueType.FullName.Contains("Unisoft.")) { var idProperty = valueType.GetProperty("Id"); if (idProperty != null) { value = idProperty.GetValue(value, null); } } } return value; } /// /// Main code generation orchestrator - handles all 4 generation types /// Uses MCODEDEFINE.GENERATIONTYPE to determine logic flow /// private async Task ProcessCodeGeneration( CodeDefineDTO codeDefine, T obj, string entityPrefix, LoginDTO loginDto) { // Find the Code property dynamically (e.g., "EmployeeCode", "CustomerCode") var codeProperty = obj.GetType().GetProperty(entityPrefix + "Code"); if (codeProperty == null) { throw new NotFoundException( $"Code property '{entityPrefix}Code' not found in {obj.GetType().Name}. " + $"Expected property name based on entity prefix."); } // Get current code value (may be null or empty) object currentValue = codeProperty.GetValue(obj, null); string currentCode = currentValue as string; // Process based on GENERATIONTYPE from MCODEDEFINE table // 0 = Auto, 1 = Manual, 2 = Formula, 3 = Hidden switch (codeDefine.CodeDefineGenerationType) { case 0: // AUTO - System generates code automatically { string generatedCode = await GenerateAutoCodeAsync(codeDefine, loginDto); codeProperty.SetValue(obj, generatedCode, null); break; } case 1: // MANUAL - User must provide code (validate only) { if (string.IsNullOrWhiteSpace(currentCode)) { throw new NotFoundException( $"{entityPrefix}Code is required. GENERATIONTYPE is MANUAL (1) - user must provide code value."); } // Validate the manually provided code against constraints ValidateManualCode(currentCode.Trim(), codeDefine, entityPrefix); codeProperty.SetValue(obj, currentCode.Trim(), null); break; } case 2: // FORMULA - Generate based on formula from MFORMULA table { string generatedCode = await GenerateFormulaBasedCodeAsync(codeDefine, loginDto); codeProperty.SetValue(obj, generatedCode, null); break; } case 3: // HIDDEN - Auto-generate but typically hidden in UI { string generatedCode = await GenerateAutoCodeAsync(codeDefine, loginDto); codeProperty.SetValue(obj, generatedCode, null); break; } case 4: // SEGMENT - Delegates to PROC_GENERATE_CODE stored procedure { string generatedCode = await GenerateSegmentCodeAsync(codeDefine, obj, loginDto); codeProperty.SetValue(obj, generatedCode, null); break; } default: throw new MethodNotAllowedException( $"Invalid GENERATIONTYPE '{codeDefine.CodeDefineGenerationType}' in MCODEDEFINE table. " + $"Valid values: 0=Auto, 1=Manual, 2=Formula, 3=Hidden, 4=Segment"); } } /// /// Generate AUTO code (GenerationType 0 or 3) /// Uses: CATPREFIX + Zero-padded Number + CATSUFFIX from MCODEDEFINE /// Updates: CATLASTNO in MCODEDEFINE table /// private async Task GenerateAutoCodeAsync(CodeDefineDTO codeDefine, LoginDTO loginDto) { // Atomically increment CATLASTNO and read back the new value in one round trip — // see INCREMENT_AND_GET_CATLASTNO for why this must not be a separate read+write. int newLastNumber = await IncrementAndGetLastNumberAsync(codeDefine.CodeDefineId, loginDto); // Validate against CATENDNO if (codeDefine.CodeDefineCatEndNo.HasValue && newLastNumber > codeDefine.CodeDefineCatEndNo.Value) { throw new MethodNotAllowedException( $"Code generation failed: Next number ({newLastNumber}) exceeds CATENDNO ({codeDefine.CodeDefineCatEndNo}). " + $"Update MCODEDEFINE.CATENDNO for CODEDEFINEID={codeDefine.CodeDefineId}"); } // Build code using CATPREFIX, number, CATSUFFIX, and CODESIZE string generatedCode = BuildCodeString( newLastNumber, codeDefine.CodeDefineCodeSize, codeDefine.CodeDefineCatPrefix, codeDefine.CodeDefineCatSuffix); return generatedCode; } /// /// Generate FORMULA-based code (GenerationType 2) /// Uses: Formula from MFORMULA table referenced by FORMULAID /// private async Task GenerateFormulaBasedCodeAsync(CodeDefineDTO codeDefine, LoginDTO loginDto) { if (!codeDefine.CodeDefineFormulaId.HasValue || codeDefine.CodeDefineFormulaId.Value <= 0) { throw new MissingMethodException( $"FORMULAID not set in MCODEDEFINE (CODEDEFINEID={codeDefine.CodeDefineId}). " + $"Formula-based generation requires valid FORMULAID."); } // Load formula from MFORMULA table var formula = await GetFormulaByIdAsync(codeDefine.CodeDefineFormulaId.Value, loginDto); if (formula == null) { throw new NotFoundException($"Formula not found in MFORMULA for FORMULAID={codeDefine.CodeDefineFormulaId}"); } // Evaluate formula to get numeric result int calculatedNumber = await EvaluateFormulaAsync(formula, loginDto); // Increment the calculated number int newLastNumber = calculatedNumber + 1; // Validate against CATENDNO if (codeDefine.CodeDefineCatEndNo.HasValue && newLastNumber > codeDefine.CodeDefineCatEndNo.Value) { throw new MethodNotAllowedException( $"Formula-based generation failed: Calculated number ({newLastNumber}) exceeds CATENDNO ({codeDefine.CodeDefineCatEndNo})"); } // Build code string generatedCode = BuildCodeString( newLastNumber, codeDefine.CodeDefineCodeSize, codeDefine.CodeDefineCatPrefix, codeDefine.CodeDefineCatSuffix); // Update CATLASTNO await UpdateLastNumberAsync(codeDefine.CodeDefineId, newLastNumber, loginDto); return generatedCode; } /// /// Validate MANUAL code (GenerationType 1) /// Checks: Length, Prefix, Suffix constraints from MCODEDEFINE /// private void ValidateManualCode(string code, CodeDefineDTO codeDefine, string entityName) { // Check CODESIZE constraint if (code.Length > codeDefine.CodeDefineCodeSize) { throw new MethodNotAllowedException( $"Manual code '{code}' (length={code.Length}) exceeds CODESIZE ({codeDefine.CodeDefineCodeSize}) " + $"defined in MCODEDEFINE for {entityName}"); } // Check CATPREFIX constraint (if defined) string prefix = codeDefine.CodeDefineCatPrefix ?? string.Empty; if (!string.IsNullOrEmpty(prefix) && !code.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { throw new MethodNotAllowedException( $"Manual code '{code}' must start with CATPREFIX '{prefix}' as defined in MCODEDEFINE"); } // Check CATSUFFIX constraint (if defined) string suffix = codeDefine.CodeDefineCatSuffix ?? string.Empty; if (!string.IsNullOrEmpty(suffix) && !code.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) { throw new MethodNotAllowedException( $"Manual code '{code}' must end with CATSUFFIX '{suffix}' as defined in MCODEDEFINE"); } } /// /// Build code string using MCODEDEFINE values /// Format: CATPREFIX + Zero-padded Number + CATSUFFIX /// Example: "EMP" + "0001" + "" = "EMP0001" (if CODESIZE=7) /// private string BuildCodeString(int number, int totalSize, string prefix, string suffix) { prefix = prefix ?? string.Empty; suffix = suffix ?? string.Empty; string numberStr = number.ToString(); // Calculate total fixed length int fixedLength = prefix.Length + suffix.Length + numberStr.Length; if (fixedLength > totalSize) { string candidate = prefix + numberStr + suffix; throw new MethodNotAllowedException( $"Generated code '{candidate}' (length={candidate.Length}) exceeds CODESIZE ({totalSize}). " + $"Adjust CATPREFIX, CATSUFFIX, or CODESIZE in MCODEDEFINE table."); } // Calculate zero padding needed int zerosNeeded = totalSize - prefix.Length - suffix.Length - numberStr.Length; string zeros = new string('0', Math.Max(0, zerosNeeded)); return prefix + zeros + numberStr + suffix; } /// /// Update CATLASTNO in MCODEDEFINE table after successful code generation /// private async Task UpdateLastNumberAsync(int codeDefineId, int newLastNumber, LoginDTO loginDto) { await _queryExecutor.ExecuteAsync( loginDto, CodeGenerationQB.UPDATE_CATLASTNO, new { catlastno = newLastNumber, codedefineid = codeDefineId }); } /// /// Atomically increments CATLASTNO and returns the new value — see /// INCREMENT_AND_GET_CATLASTNO for why plain auto-numbering must use this, not /// UpdateLastNumberAsync, to avoid duplicate codes within a single request. /// private async Task IncrementAndGetLastNumberAsync(int codeDefineId, LoginDTO loginDto) { return await _queryExecutor.ExecuteScalarAsync( loginDto, CodeGenerationQB.INCREMENT_AND_GET_CATLASTNO, new { codedefineid = codeDefineId }); } /// /// Evaluate formula from MFORMULA table to get numeric value /// private async Task EvaluateFormulaAsync(dynamic formula, LoginDTO loginDto) { if (formula.FormulaType == 0) { throw new MissingMethodException("FormulaType 0 not supported for code generation"); } string expression; if (formula.FormulaType == 1) // Function-based formula { if (formula.DataType == 0 || formula.DataType == 2) // Numeric data types { expression = loginDto.DatabaseType == 0 ? $"ISNULL({formula.FunctionName}, 0)" // SQL Server : $"NVL({formula.FunctionName}, 0)"; // Oracle } else { throw new MissingMethodException( "Only numeric DataType supported for formula-based code generation"); } } else // Expression-based formula { expression = formula.Expression; } // Execute SELECT to get numeric value string selectSql = loginDto.DatabaseType == 0 ? $"SELECT {expression} AS EntityId" : $"SELECT {expression} AS EntityId FROM dual"; var results = await QueryListAsync(loginDto, selectSql, null); if (results == null || results.Count == 0) { throw new NotFoundException("Formula evaluation returned no results"); } return results[0].EntityId; } /// /// Load entity metadata from MENTITY table /// private async Task GetEntityByCodeAsync(string entityCode, LoginDTO loginDto) { string sql = CodeGenerationQB.GET_ENTITY_BY_CODE; return await _queryExecutor.QuerySingleAsync( loginDto, sql, new { entitycode = entityCode }); } /// /// Load formula from MFORMULA table - MUST BE IMPLEMENTED /// Expected properties: FormulaType, DataType, FunctionName, Expression /// private async Task GetFormulaByIdAsync(int formulaId, LoginDTO loginDto) { // TODO: Implement loading formula from MFORMULA table // Example SQL: "SELECT * FROM MFORMULA WHERE FORMULAID = @formulaid" string sql = "SELECT * FROM MFORMULA WHERE FORMULAID = @formulaid"; var formula = await _queryExecutor.QuerySingleAsync( loginDto, sql, new { formulaid = formulaId }); if (formula == null) { throw new NotFoundException($"Formula not found in MFORMULA for FORMULAID={formulaId}"); } return formula; } /// /// Generate SEGMENT code (GenerationType 4). /// Serializes the entity DTO to JSON and calls PROC_GENERATE_CODE via a /// DECLARE/SELECT wrapper so the OUTPUT parameter is returned as a result set, /// avoiding the need for DynamicParameters. /// private async Task GenerateSegmentCodeAsync( CodeDefineDTO codeDefine, T obj, LoginDTO loginDto) { string inputJson = JsonSerializer.Serialize(obj); var parameters = new { categorycode = codeDefine.CodeDefineCategoryCode, inputjson = inputJson }; var result = await _queryExecutor.QuerySingleAsync( loginDto, CodeGenerationQB.EXECUTE_PROC_GENERATE_CODE, parameters); string generatedCode = result?.GeneratedCode; if (string.IsNullOrWhiteSpace(generatedCode)) throw new NotFoundException( $"PROC_GENERATE_CODE returned empty code for CODEDEFINEID={codeDefine.CodeDefineId}. " + $"Check MCODEDEFINESEGMENT rows and that mandatory segments have values."); return generatedCode; } /// Minimal result DTO for capturing PROC_GENERATE_CODE output. private sealed class GeneratedCodeResult { public string GeneratedCode { get; set; } } #region Helper Methods private async Task> QueryListAsync(LoginDTO loginDto, string sql, object parameters) { var result = await _queryExecutor.QueryAsync(loginDto, sql, parameters); return result?.ToList() ?? new List(); } #endregion } } //using System; //using System.Collections.Generic; //using System.Linq; //using System.Threading.Tasks; //using GB5Shared.DTO.Framework.CodeDefine; //using GB5Shared.DTO.Framework.Criteria; //using GB5Shared.DTO.Framework.Entity; //using GB5Shared.DTO.Framework.Login; //using GB5Shared.GB5Exception; //using GB5Shared.Query.CodeGeneration; //using GB5Shared.QueryExecutor; //using static GB5Shared.DTO.Framework.Enum.FrameworkEnumDTO; //using static GB5Shared.GB5Constant.Constant; //namespace GB5Shared.GenerateAutoNumber //{ // public class CodeGeneration // { // private readonly IQueryExecutor _queryExecutor; // public CodeGeneration(IQueryExecutor queryExecutor) // { // _queryExecutor = queryExecutor; // } // /// // /// Assigns code to the given object based on CodeDefine configuration. // /// Logic preserved from original synchronous implementation but corrected & hardened. // /// // public async Task AssignCode(string objectName, T obj, LoginDTO loginDto) // { // if (string.IsNullOrWhiteSpace(objectName)) // throw new ArgumentException("objectName required", nameof(objectName)); // if (obj == null) // throw new ArgumentNullException(nameof(obj)); // if (loginDto == null) // throw new ArgumentNullException(nameof(loginDto)); // var originalDatabase = loginDto.DatabaseName; // try // { // // Determine prefix from POCO type name (strip trailing "DTO") // string prefix = ""; // string entityObjectCode = obj.GetType().ToString(); // string[] splitPocoName = entityObjectCode.Split('.'); // string entityCode = splitPocoName[splitPocoName.Length - 1]; // if (entityCode.Length > 3) // { // var dtoSuffix = entityCode.Substring(entityCode.Length - 3); // if (dtoSuffix.Equals("DTO", StringComparison.OrdinalIgnoreCase)) // { // prefix = entityCode.Remove(entityCode.Length - 3, 3); // } // } // Type persistentType = typeof(T); // // Load entity metadata // var entityDto = await GetEntityByCodeAsync(objectName, loginDto); // if (entityDto == null) // throw new NotFoundException("Entity Entry is not found for the given entity"); // // If code generation not applicable (1 => No according to your logic) // if (entityDto.EntityIsCodeApplicable == 1) // return obj; // // Prepare SQL and parameters to load code define(s) // string sqlToLoadCodeDefines; // object parametersForCodeDefine = null!; // if (entityDto.EntityCategoryMemberId == -1) // { // sqlToLoadCodeDefines = CodeGenerationQB.GET_ENTITY_BY_ID; // parametersForCodeDefine = new { entityid = entityDto.EntityId }; // } // else // { // // Load the entity member (category) to find the category member name // sqlToLoadCodeDefines = CodeGenerationQB.GET_ENTITYMEMBERS; // var para = new { entitymemberid = entityDto.EntityCategoryMemberId }; // var entityMember = await _queryExecutor.QuerySingleAsync(loginDto, CodeGenerationQB.GET_ENTITYMEMBERS, para); // if (entityMember == null) // throw new NotFoundException("Entry Not Found For the supplied object in EntityMember Table"); // // Reflect to get the value of the member from the object // var propInfo = obj.GetType().GetProperty(entityMember.EntityMemberMemberName); // if (propInfo == null) // throw new NotFoundException(entityMember.EntityMemberMemberName + " is not Found in given object and CategoryMemberId field is set to Entity object"); // object tempCategoryValue = propInfo.GetValue(obj, null)!; // if (tempCategoryValue != null) // { // var tempCategoryType = tempCategoryValue.GetType(); // if (tempCategoryType.FullName != null && tempCategoryType.FullName.Contains("Unisoft.")) // { // var idProp = tempCategoryType.GetProperty("Id"); // if (idProp == null) // throw new NotFoundException("Id is not Found in given object and CategoryMemberId field is set to Entity object"); // tempCategoryValue = idProp.GetValue(tempCategoryValue, null)!; // } // } // // Prepare SQL that loads codedefines by entityid + codedefineid (category id) // sqlToLoadCodeDefines = CodeGenerationQB.LOAD_CODE_DEFINE_BY_PARAMS_BASED_ON_CATEGORY_ID_SQL; // parametersForCodeDefine = new // { // entityid = entityDto.EntityId, // codedefineid = tempCategoryValue // }; // } // // Query code define rows (expect 0,1 or >1 results) // var codeDefinePocos = await QueryListAsync(loginDto, sqlToLoadCodeDefines, parametersForCodeDefine); // if (codeDefinePocos == null) codeDefinePocos = new List(); // if (codeDefinePocos.Count == 1) // { // var codeDefine = codeDefinePocos[0]; // // Ensure target property exists: Prefix + "Code" // var codeProp = obj.GetType().GetProperty(prefix + "Code"); // if (codeProp == null) // throw new NotFoundException("Code is not found in the Save object but Entry is found.."); // // current code value (may be null) // object currentCodeValue = codeProp.GetValue(obj, null)!; // // Decide generation type and set Code on object // switch (codeDefine.CodeDefineGenerationType) // adapt property name if different // { // case 0: // Auto // case 3: // Hidden (also auto) // { // string generatedCode = GenerateCode(codeDefine, loginDto); // codeProp.SetValue(obj, generatedCode, null); // break; // } // case 1: // Manual // { // if (currentCodeValue is string s && !string.IsNullOrEmpty(s)) // { // codeProp.SetValue(obj, s, null); // } // else // { // throw new NotFoundException("Code is Not Supplied..Since Code generation type is Manual"); // } // break; // } // case 2: // Formula // { // string generatedCode = GenerateCode(codeDefine, loginDto); // codeProp.SetValue(obj, generatedCode, null); // break; // } // default: // throw new MethodNotAllowedException("Unknown GenerationType in CodeDefine."); // } // } // else if (codeDefinePocos.Count > 1) // { // throw new FoundException("More Than one Entries found in CodeDefine table based on Given EntityCode >> " + objectName); // } // else // 0 rows // { // var targetProp = obj.GetType().GetProperty(prefix + "Code"); // if (targetProp != null) // { // throw new NotFoundException("No Entries found in CodeDefine table based on Given EntityCode >> " + objectName); // } // } // // restore DB name if modified earlier // loginDto.DatabaseName = originalDatabase; // return obj; // } // catch // { // throw; // } // } // /// // /// Helper to load Entity metadata by entity code. // /// // private async Task GetEntityByCodeAsync(string objectName, LoginDTO loginDto) // { // try // { // string sql = CodeGenerationQB.GET_ENTITY_BY_CODE; // var param = new { entitycode = objectName }; // return await _queryExecutor.QuerySingleAsync(loginDto, sql, param); // } // catch // { // throw; // } // } // /// // /// Generates a code according to CodeDefine DTO. This implements the same logic as your original GenerateCode, // /// but uses async update for CATLASTNO and clearer variable names. // /// // private string GenerateCode(CodeDefineDTO codeDefine, LoginDTO loginDto) // { // if (codeDefine == null) throw new ArgumentNullException(nameof(codeDefine)); // try // { // string resultCode = string.Empty; // int totalLength = codeDefine.CodeDefineCodeSize; // int lastNumber = codeDefine.CodeDefineCatLastNo; // string prefix = codeDefine.CodeDefineCatPrefix ?? ""; // string suffix = codeDefine.CodeDefineCatSuffix ?? ""; // // Helper to format code with leading zeros as required // string BuildFromNumber(int number) // { // string numberStr = number.ToString(); // int fixedPartLength = prefix.Length + suffix.Length + numberStr.Length; // if (fixedPartLength > totalLength) // { // string candidate = prefix + numberStr + suffix; // throw new MethodNotAllowedException($"Code can not be generated since generated code >> {candidate} is more than the size specified in Entity Table for the given entity. Please correct."); // } // int zerosNeeded = totalLength - (prefix.Length + suffix.Length + numberStr.Length); // // zerosNeeded may be zero or more; if >0 produce string with zeros // string zeros = new string('0', Math.Max(0, zerosNeeded)); // return prefix + zeros + numberStr + suffix; // } // // Auto or Hidden generation // if (codeDefine.CodeDefineGenerationType == 0 || codeDefine.CodeDefineGenerationType == 3) // { // lastNumber = lastNumber + 1; // if (lastNumber > codeDefine.CodeDefineCatEndNo) // throw new MethodNotAllowedException("Check the setting in Code Define table,lastnumber is greater than end number"); // resultCode = BuildFromNumber(lastNumber); // // Persist updated last number // string updateSql = $"UPDATE MCODEDEFINE SET CATLASTNO = {lastNumber} WHERE CodeDefineId = {codeDefine.CodeDefineId}"; // // Use async execute in your real environment - here call a sync helper for compatibility // ExecuteNonQueryAsync(loginDto, updateSql).GetAwaiter().GetResult(); // } // else if (codeDefine.CodeDefineGenerationType == 2) // formula based // { // // Evaluate formula // // Note: I assume CodeDefineDTO.CodeDefineFormulaId exists (per original code). Adapt property names. // if (codeDefine.CodeDefineFormulaId == null) // { // throw new MissingMethodException("Formula id not set for formula based generation."); // } // // Load formula object - replace GetFormulaById with your actual access method // var formula = GetFormulaById(codeDefine.CodeDefineFormulaId, loginDto); // if (formula == null) // throw new NotFoundException("Formula not found."); // string finalExpression; // if (formula.FormulaType == 0) // original code threw if Formula type was 0 (strange) // { // throw new MissingMethodException("Only formula type is supported.."); // } // else if (formula.FormulaType == 1) // Function // { // if (formula.DataType == 0 || formula.DataType == 2) // { // if (loginDto.DatabaseType == 0) // SQL Server // finalExpression = $"ISNULL({formula.FunctionName},0)"; // else // Oracle // finalExpression = $"NVL({formula.FunctionName},0)"; // } // else // { // throw new MissingMethodException("Only number datatype supported.."); // } // } // else // { // finalExpression = formula.Expression; // } // // Execute select to get the numeric value // string selectSql = loginDto.DatabaseType == 0 // ? $"SELECT {finalExpression} AS EntityId" // : $"SELECT {finalExpression} AS EntityId FROM dual"; // var rows = ExceuteSelectQuery(selectSql, loginDto); // if (rows != null && rows.Count > 0) // { // lastNumber = rows[0].EntityId; // } // lastNumber = lastNumber + 1; // if (lastNumber > codeDefine.CodeDefineCatEndNo) // throw new MethodNotAllowedException("Check the setting in Code Define table,lastnumber is greater than end number"); // resultCode = BuildFromNumber(lastNumber); // // Persist CATLASTNO // string updateSql = $"UPDATE MCODEDEFINE SET CATLASTNO = {lastNumber} WHERE CodeDefineId = {codeDefine.CodeDefineId}"; // ExecuteNonQueryAsync(loginDto, updateSql).GetAwaiter().GetResult(); // } // else // { // throw new MethodNotAllowedException("Unsupported CodeDefineGenerationType."); // } // return resultCode; // } // catch // { // throw; // } // } // #region --- Helper placeholders - adapt these to your actual QueryExecutor implementation --- // // QueryListAsync: returns list of DTOs for a SQL and parameters. // // Replace with your IQueryExecutor method that returns a collection. // private async Task> QueryListAsync(LoginDTO loginDto, string sql, object parameters) // { // // TODO: Replace this with the correct call to your IQueryExecutor that returns a list. // // Example: return await _queryExecutor.QueryAsync(loginDto, sql, parameters); // return (await _queryExecutor.QueryAsync(loginDto, sql, parameters)).ToList(); // } // // Execute a non-query (update) - adapt to your QueryExecutor // private async Task ExecuteNonQueryAsync(LoginDTO loginDto, string sql) // { // // TODO: Replace with whatever method executes an update/insert (and does not return rows) // await _queryExecutor.ExecuteAsync(loginDto, sql, null!); // } // // Execute a select and return a list synchronously (used in GenerateCode for formula path) // private IList ExceuteSelectQuery(string sql, LoginDTO loginDto) // { // // TODO: Replace with synchronous or asynchronous call as per your environment. // // Here I call async and wait because original code expects synchronous call. // return QueryListAsync(loginDto, sql, null!).GetAwaiter().GetResult(); // } // // Load formula by id - placeholder. Replace with your real method or query. // private dynamic GetFormulaById(int formulaId, LoginDTO loginDto) // { // // TODO: implement loading of formula object from DB // // For example: // // return _queryExecutor.QuerySingleAsync(loginDto, "SELECT ... WHERE Id=@id", new { id = formulaId }).GetAwaiter().GetResult(); // throw new NotImplementedException("GetFormulaById must be implemented to load the formula metadata."); // } // #endregion // } //}