2026-05-02 17:08:06 +00:00
|
|
|
|
using System;
|
2026-05-02 18:43:32 +00:00
|
|
|
|
using System.Collections.Generic;
|
2026-05-02 17:08:06 +00:00
|
|
|
|
using System.Data;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Sozsoft.Languages.Entities;
|
|
|
|
|
|
using Sozsoft.Languages.Languages;
|
|
|
|
|
|
using Sozsoft.Platform.Entities;
|
|
|
|
|
|
using Sozsoft.Platform.Enums;
|
|
|
|
|
|
using Sozsoft.Platform.ListForms;
|
|
|
|
|
|
using Volo.Abp.Data;
|
|
|
|
|
|
using Volo.Abp.DependencyInjection;
|
|
|
|
|
|
using Volo.Abp.Domain.Repositories;
|
|
|
|
|
|
using Volo.Abp.MultiTenancy;
|
|
|
|
|
|
using Volo.Abp.PermissionManagement;
|
|
|
|
|
|
using static Sozsoft.Platform.PlatformConsts;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Sozsoft.Platform.Data.Seeds;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Wizard ile oluşturulan konfigürasyonları Seeds/WizardData/*.json dosyalarından okuyarak veritabanına aktarır.
|
|
|
|
|
|
/// Wizard çalıştıktan sonra oluşturulan JSON dosyaları projeye commit edildikten sonra,
|
|
|
|
|
|
/// veritabanı silinip yeniden oluşturulduğunda bu seeder tüm wizard konfigürasyonlarını geri yükler.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly IRepository<LanguageKey, Guid> _repoLangKey;
|
|
|
|
|
|
private readonly IRepository<LanguageText, Guid> _repoLangText;
|
|
|
|
|
|
private readonly IRepository<PermissionGroupDefinitionRecord, Guid> _repoPermGroup;
|
|
|
|
|
|
private readonly IRepository<PermissionDefinitionRecord, Guid> _repoPerm;
|
|
|
|
|
|
private readonly IRepository<Menu, Guid> _repoMenu;
|
|
|
|
|
|
private readonly IRepository<DataSource, Guid> _repoDataSource;
|
|
|
|
|
|
private readonly IRepository<ListForm, Guid> _repoListForm;
|
|
|
|
|
|
private readonly IRepository<ListFormField, Guid> _repoListFormField;
|
2026-06-07 19:42:02 +00:00
|
|
|
|
private readonly IRepository<ListFormWorkflow, string> _repoListFormWorkflow;
|
2026-05-02 17:08:06 +00:00
|
|
|
|
private readonly ILogger<WizardDataSeeder> _logger;
|
|
|
|
|
|
|
|
|
|
|
|
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
|
|
|
|
|
|
private readonly string _appName = PlatformConsts.AppName;
|
|
|
|
|
|
|
|
|
|
|
|
public WizardDataSeeder(
|
|
|
|
|
|
IRepository<LanguageKey, Guid> repoLangKey,
|
|
|
|
|
|
IRepository<LanguageText, Guid> repoLangText,
|
|
|
|
|
|
IRepository<PermissionGroupDefinitionRecord, Guid> repoPermGroup,
|
|
|
|
|
|
IRepository<PermissionDefinitionRecord, Guid> repoPerm,
|
|
|
|
|
|
IRepository<Menu, Guid> repoMenu,
|
|
|
|
|
|
IRepository<DataSource, Guid> repoDataSource,
|
|
|
|
|
|
IRepository<ListForm, Guid> repoListForm,
|
|
|
|
|
|
IRepository<ListFormField, Guid> repoListFormField,
|
2026-06-07 19:42:02 +00:00
|
|
|
|
IRepository<ListFormWorkflow, string> repoListFormWorkflow,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
ILogger<WizardDataSeeder> logger)
|
|
|
|
|
|
{
|
|
|
|
|
|
_repoLangKey = repoLangKey;
|
|
|
|
|
|
_repoLangText = repoLangText;
|
|
|
|
|
|
_repoPermGroup = repoPermGroup;
|
|
|
|
|
|
_repoPerm = repoPerm;
|
|
|
|
|
|
_repoMenu = repoMenu;
|
|
|
|
|
|
_repoDataSource = repoDataSource;
|
|
|
|
|
|
_repoListForm = repoListForm;
|
|
|
|
|
|
_repoListFormField = repoListFormField;
|
2026-06-07 19:42:02 +00:00
|
|
|
|
_repoListFormWorkflow = repoListFormWorkflow;
|
2026-05-02 17:08:06 +00:00
|
|
|
|
_logger = logger;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task SeedAsync(DataSeedContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
var wizardDataPath = Path.Combine(Directory.GetCurrentDirectory(), "Seeds", "WizardData");
|
|
|
|
|
|
if (!Directory.Exists(wizardDataPath))
|
|
|
|
|
|
{
|
2026-05-05 17:59:30 +00:00
|
|
|
|
_logger.LogInformation("WizardData directory not found, skipping.");
|
2026-05-02 17:08:06 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
|
|
|
|
|
|
if (jsonFiles.Length == 0)
|
|
|
|
|
|
{
|
2026-05-05 17:59:30 +00:00
|
|
|
|
_logger.LogInformation("No JSON files found in WizardData directory, skipping.");
|
2026-05-02 17:08:06 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-02 19:31:43 +00:00
|
|
|
|
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", jsonFiles.Length);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
|
2026-05-02 17:08:06 +00:00
|
|
|
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var filePath in jsonFiles)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var json = await File.ReadAllTextAsync(filePath);
|
|
|
|
|
|
var seedFile = JsonSerializer.Deserialize<WizardSeedFileDto>(json, options);
|
|
|
|
|
|
|
|
|
|
|
|
if (seedFile?.Wizard == null)
|
|
|
|
|
|
{
|
2026-05-02 19:31:43 +00:00
|
|
|
|
_logger.LogWarning("Invalid file skipped: {FilePath}", filePath);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var wizardName = seedFile.Wizard.WizardName?.Trim();
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(wizardName))
|
|
|
|
|
|
{
|
2026-05-02 19:31:43 +00:00
|
|
|
|
_logger.LogWarning("WizardName is empty, skipped: {FilePath}", filePath);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-02 18:43:32 +00:00
|
|
|
|
var fileName = Path.GetFileName(filePath);
|
|
|
|
|
|
|
2026-05-02 17:08:06 +00:00
|
|
|
|
// Zaten seeded mi kontrol et (ListForm var mı?)
|
|
|
|
|
|
if (await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode))
|
|
|
|
|
|
{
|
2026-05-02 19:31:43 +00:00
|
|
|
|
_logger.LogInformation("[{File}] '{WizardName}' already exists, skipped.", fileName, wizardName);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-02 19:31:43 +00:00
|
|
|
|
_logger.LogInformation("[{File}] '{WizardName}' is being applied...", fileName, wizardName);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
await ApplyWizardSeedAsync(seedFile);
|
2026-05-02 19:31:43 +00:00
|
|
|
|
_logger.LogInformation("[{File}] '{WizardName}' has been successfully applied.", fileName, wizardName);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-05-26 09:29:46 +00:00
|
|
|
|
_logger.LogError(ex, $"Error - {filePath}: {ex.Message}");
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task ApplyWizardSeedAsync(WizardSeedFileDto seedFile)
|
|
|
|
|
|
{
|
|
|
|
|
|
var input = seedFile.Wizard;
|
|
|
|
|
|
var isDeleted = seedFile.IsDeletedField;
|
|
|
|
|
|
var isCreated = seedFile.IsCreatedField;
|
2026-05-27 19:09:05 +00:00
|
|
|
|
input.SubForms ??= new List<SubFormDto>();
|
|
|
|
|
|
input.Widgets ??= new List<WidgetEditDto>();
|
2026-07-16 14:01:18 +00:00
|
|
|
|
input.Criteria ??= [];
|
|
|
|
|
|
EnsureUniqueWorkflowCriteriaTitles(input.Criteria);
|
|
|
|
|
|
var workflow = new WorkflowDto
|
2026-06-07 19:42:02 +00:00
|
|
|
|
{
|
2026-07-16 14:01:18 +00:00
|
|
|
|
ApprovalUserFieldName = input.ApprovalUserFieldName,
|
|
|
|
|
|
ApprovalIsFilterUserName = input.ApprovalIsFilterUserName,
|
|
|
|
|
|
ApprovalDateFieldName = input.ApprovalDateFieldName,
|
|
|
|
|
|
ApprovalStatusFieldName = input.ApprovalStatusFieldName,
|
|
|
|
|
|
ApprovalDescriptionFieldName = input.ApprovalDescriptionFieldName,
|
|
|
|
|
|
Criteria = input.Criteria,
|
|
|
|
|
|
};
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
|
|
|
|
|
var wizardName = input.WizardName.Trim();
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
|
|
|
|
|
? WizardConsts.WizardKey(wizardName)
|
|
|
|
|
|
: input.MenuCode.Trim();
|
|
|
|
|
|
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
|
|
|
|
|
? code
|
|
|
|
|
|
: input.ListFormCode.Trim();
|
2026-07-16 14:01:18 +00:00
|
|
|
|
NormalizeWorkflowCriteriaIds(listFormCode, input.Criteria);
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var titleLangKey = $"{listFormCode}.Title";
|
|
|
|
|
|
var nameLangKey = code;
|
|
|
|
|
|
var descLangKey = $"{listFormCode}.Desc";
|
|
|
|
|
|
var permCreateName = $"{code}.Create";
|
|
|
|
|
|
var permUpdateName = $"{code}.Update";
|
|
|
|
|
|
var permDeleteName = $"{code}.Delete";
|
|
|
|
|
|
var permExportName = $"{code}.Export";
|
|
|
|
|
|
var permImportName = $"{code}.Import";
|
|
|
|
|
|
var permNoteName = $"{code}.Note";
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
|
|
|
|
|
// Dil - Language Keys
|
|
|
|
|
|
await CreateLangKeyAsync(nameLangKey, input.LanguageTextMenuEn, input.LanguageTextMenuTr);
|
|
|
|
|
|
await CreateLangKeyAsync(titleLangKey, input.LanguageTextTitleEn, input.LanguageTextTitleTr);
|
|
|
|
|
|
await CreateLangKeyAsync(descLangKey, input.LanguageTextDescEn, input.LanguageTextDescTr);
|
|
|
|
|
|
|
|
|
|
|
|
// Permission Group
|
|
|
|
|
|
var groupName = input.PermissionGroupName ?? AppName;
|
|
|
|
|
|
if (!await _repoPermGroup.AnyAsync(a => a.Name == groupName))
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoPermGroup.InsertAsync(
|
|
|
|
|
|
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
|
2026-06-06 15:22:14 +00:00
|
|
|
|
if (string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
await EnsureLangKeyAsync(groupName);
|
|
|
|
|
|
else
|
|
|
|
|
|
await CreateLangKeyAsync(groupName, groupName, groupName);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-02 18:43:32 +00:00
|
|
|
|
// Permissions - tek seferde mevcut permission'ları çek, sonra her birini kontrol et
|
2026-05-02 17:08:06 +00:00
|
|
|
|
var permQueryable = await _repoPerm.GetQueryableAsync();
|
|
|
|
|
|
var existingPerms = permQueryable.Where(a => a.GroupName == groupName).ToList();
|
|
|
|
|
|
|
2026-05-02 18:43:32 +00:00
|
|
|
|
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
|
|
|
|
|
|
if (permRead == null)
|
|
|
|
|
|
permRead = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-05-02 17:08:06 +00:00
|
|
|
|
Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: true);
|
|
|
|
|
|
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var permCreate = existingPerms.FirstOrDefault(a => a.Name == permCreateName);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
if (permCreate == null)
|
|
|
|
|
|
permCreate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Guid.NewGuid(), groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, true, MultiTenancySides.Both), autoSave: true);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == permUpdateName);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
if (permUpdate == null)
|
|
|
|
|
|
permUpdate = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Guid.NewGuid(), groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: true);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var permDelete = existingPerms.FirstOrDefault(a => a.Name == permDeleteName);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
if (permDelete == null)
|
|
|
|
|
|
permDelete = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Guid.NewGuid(), groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, true, MultiTenancySides.Both), autoSave: true);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var permExport = existingPerms.FirstOrDefault(a => a.Name == permExportName);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
if (permExport == null)
|
|
|
|
|
|
permExport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Guid.NewGuid(), groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, true, MultiTenancySides.Both), autoSave: true);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var permImport = existingPerms.FirstOrDefault(a => a.Name == permImportName);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
if (permImport == null)
|
|
|
|
|
|
permImport = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Guid.NewGuid(), groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, true, MultiTenancySides.Both), autoSave: true);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var permNote = existingPerms.FirstOrDefault(a => a.Name == permNoteName);
|
2026-05-02 18:43:32 +00:00
|
|
|
|
if (permNote == null)
|
|
|
|
|
|
permNote = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Guid.NewGuid(), groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: true);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
2026-05-02 19:31:43 +00:00
|
|
|
|
// // Permission Grants - Admin role için, sadece eksik olanları ekle
|
|
|
|
|
|
// var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
|
|
|
|
|
|
// var existingGrantNames = new HashSet<string>(existingGrants.Select(g => g.Name));
|
|
|
|
|
|
|
|
|
|
|
|
// var grantsToInsert = new[]
|
|
|
|
|
|
// {
|
|
|
|
|
|
// permRead.Name, permCreate.Name, permUpdate.Name,
|
|
|
|
|
|
// permDelete.Name, permExport.Name, permImport.Name, permNote.Name
|
|
|
|
|
|
// }
|
|
|
|
|
|
// .Where(name => !existingGrantNames.Contains(name))
|
|
|
|
|
|
// .Select(name => new PermissionGrant(Guid.NewGuid(), name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName))
|
|
|
|
|
|
// .ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// if (grantsToInsert.Count > 0)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// await _permissionGrantRepository.InsertManyAsync(grantsToInsert, autoSave: true);
|
|
|
|
|
|
// }
|
2026-05-02 17:08:06 +00:00
|
|
|
|
|
|
|
|
|
|
// Menu Parent
|
2026-05-02 18:43:32 +00:00
|
|
|
|
var menuQueryable = await _repoMenu.GetQueryableAsync();
|
2026-05-02 17:08:06 +00:00
|
|
|
|
var menuParent = await _repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
|
|
|
|
|
|
if (menuParent == null)
|
|
|
|
|
|
{
|
2026-05-02 18:43:32 +00:00
|
|
|
|
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
|
2026-06-06 15:22:14 +00:00
|
|
|
|
var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
|
|
|
|
|
|
? input.MenuParentIcon
|
|
|
|
|
|
: !string.IsNullOrWhiteSpace(input.MenuIcon)
|
|
|
|
|
|
? input.MenuIcon
|
|
|
|
|
|
: WizardConsts.MenuIcon;
|
|
|
|
|
|
await CreateLangKeyAsync(input.MenuParentCode, input.LanguageTextMenuParentEn, input.LanguageTextMenuParentTr);
|
2026-05-02 17:08:06 +00:00
|
|
|
|
menuParent = await _repoMenu.InsertAsync(new Menu
|
|
|
|
|
|
{
|
|
|
|
|
|
Code = input.MenuParentCode,
|
2026-06-06 15:22:14 +00:00
|
|
|
|
DisplayName = input.MenuParentCode,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
IsDisabled = false,
|
2026-06-06 15:22:14 +00:00
|
|
|
|
Icon = menuParentIcon,
|
2026-05-02 18:43:32 +00:00
|
|
|
|
Order = maxRootOrder + 1,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Menu
|
2026-05-02 18:43:32 +00:00
|
|
|
|
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0;
|
|
|
|
|
|
var existingMenu = await _repoMenu.FirstOrDefaultAsync(a => a.Code == code);
|
|
|
|
|
|
if (existingMenu == null)
|
2026-05-02 17:08:06 +00:00
|
|
|
|
{
|
|
|
|
|
|
await _repoMenu.InsertAsync(new Menu
|
|
|
|
|
|
{
|
|
|
|
|
|
Code = code,
|
|
|
|
|
|
DisplayName = nameLangKey,
|
|
|
|
|
|
IsDisabled = false,
|
|
|
|
|
|
ParentCode = menuParent.Code,
|
|
|
|
|
|
Icon = input.MenuIcon ?? WizardConsts.MenuIcon,
|
|
|
|
|
|
Target = null,
|
|
|
|
|
|
ElementId = null,
|
|
|
|
|
|
CssClass = null,
|
|
|
|
|
|
Url = WizardConsts.MenuUrl(code),
|
2026-05-02 18:43:32 +00:00
|
|
|
|
RequiredPermissionName = permRead.Name,
|
|
|
|
|
|
Order = maxChildOrder + 1,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DataSource
|
2026-05-02 18:43:32 +00:00
|
|
|
|
var existingDataSource = await _repoDataSource.FirstOrDefaultAsync(a => a.Code == input.DataSourceCode);
|
|
|
|
|
|
if (existingDataSource == null)
|
2026-05-02 17:08:06 +00:00
|
|
|
|
{
|
|
|
|
|
|
await _repoDataSource.InsertAsync(new DataSource
|
|
|
|
|
|
{
|
|
|
|
|
|
Code = input.DataSourceCode,
|
|
|
|
|
|
DataSourceType = input.DataSourceConnectionString != null &&
|
|
|
|
|
|
input.DataSourceConnectionString.IndexOf("Server", StringComparison.OrdinalIgnoreCase) >= 0
|
|
|
|
|
|
? DataSourceTypeEnum.Mssql
|
|
|
|
|
|
: DataSourceTypeEnum.Postgresql,
|
|
|
|
|
|
ConnectionString = input.DataSourceConnectionString
|
|
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EditingFormJson
|
|
|
|
|
|
var editingFormDtos = input.Groups
|
|
|
|
|
|
.Select((g, gi) => new EditingFormDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Order = gi + 1,
|
|
|
|
|
|
Caption = g.Caption,
|
|
|
|
|
|
ColCount = g.ColCount,
|
|
|
|
|
|
ColSpan = g.ColCount,
|
2026-07-17 08:59:51 +00:00
|
|
|
|
ItemType = "group"
|
2026-05-02 17:08:06 +00:00
|
|
|
|
})
|
2026-07-17 08:59:51 +00:00
|
|
|
|
.Where((_, index) => input.Groups[index].Items.Any(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName))
|
2026-05-02 17:08:06 +00:00
|
|
|
|
.ToList();
|
|
|
|
|
|
|
2026-05-02 18:43:32 +00:00
|
|
|
|
// ListForm - varsa sil, yeniden ekle
|
|
|
|
|
|
var existingListForm = await _repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
|
|
|
|
|
if (existingListForm != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoListForm.DeleteAsync(existingListForm, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var existingListFormFields = await _repoListFormField.GetListAsync(a => a.ListFormCode == input.ListFormCode);
|
|
|
|
|
|
if (existingListFormFields.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoListFormField.DeleteManyAsync(existingListFormFields, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-07 19:42:02 +00:00
|
|
|
|
var existingWorkflowCriteria = await _repoListFormWorkflow.GetListAsync(a => a.ListFormCode == input.ListFormCode);
|
|
|
|
|
|
if (existingWorkflowCriteria.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 13:58:58 +00:00
|
|
|
|
// EditingForm'da required olan alanları tespit et, ValidationRuleJson eklemek için
|
|
|
|
|
|
var editingFormFieldNames = input.Groups
|
|
|
|
|
|
.SelectMany(g => g.Items)
|
2026-07-17 08:59:51 +00:00
|
|
|
|
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
|
|
|
|
|
|
.Select(i => new { FieldName = i.FieldName, Isrequired = i.IsRequired })
|
2026-06-09 13:58:58 +00:00
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// Heigth ve Width hesaplama
|
|
|
|
|
|
var groupLayouts = input.Groups
|
|
|
|
|
|
.Select(g =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var items = g.Items
|
2026-07-17 08:59:51 +00:00
|
|
|
|
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
|
2026-06-09 13:58:58 +00:00
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
var columnCount = g.ColCount;
|
|
|
|
|
|
|
|
|
|
|
|
if (columnCount <= 0)
|
|
|
|
|
|
columnCount = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 3 kolonu geçmesin
|
|
|
|
|
|
columnCount = Math.Min(columnCount, WizardConsts.EditFormMaxColumnCount);
|
|
|
|
|
|
|
|
|
|
|
|
// Örneğin 4 item / 3 kolon = 2 satır
|
|
|
|
|
|
var rowCount = (int)Math.Ceiling(items.Count / (double)columnCount);
|
|
|
|
|
|
|
|
|
|
|
|
return new
|
|
|
|
|
|
{
|
|
|
|
|
|
Group = g,
|
|
|
|
|
|
Items = items,
|
|
|
|
|
|
ColumnCount = columnCount,
|
|
|
|
|
|
RowCount = rowCount
|
|
|
|
|
|
};
|
|
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// Form genelinde kullanılacak kolon sayısı
|
|
|
|
|
|
var formColumnCount = groupLayouts.Any()
|
|
|
|
|
|
? groupLayouts.Max(x => x.ColumnCount)
|
|
|
|
|
|
: 1;
|
|
|
|
|
|
|
|
|
|
|
|
// Width hesabı
|
|
|
|
|
|
var formWidth = (WizardConsts.EditFormMaxWidth / WizardConsts.EditFormMaxColumnCount) * formColumnCount;
|
|
|
|
|
|
|
|
|
|
|
|
// Toplam satır sayısı
|
|
|
|
|
|
var totalRowCount = groupLayouts.Sum(x => x.RowCount);
|
|
|
|
|
|
|
|
|
|
|
|
// Height hesabı
|
|
|
|
|
|
var formHeight = WizardConsts.EditFormHeightDefault + (totalRowCount * WizardConsts.EditFormHeightByRow);
|
|
|
|
|
|
|
2026-05-02 17:08:06 +00:00
|
|
|
|
// ListForm
|
|
|
|
|
|
await _repoListForm.InsertAsync(new ListForm
|
|
|
|
|
|
{
|
|
|
|
|
|
ListFormType = ListFormTypeEnum.List,
|
|
|
|
|
|
PageSize = 10,
|
|
|
|
|
|
ExportJson = WizardConsts.DefaultExportJson,
|
|
|
|
|
|
IsSubForm = false,
|
2026-07-16 14:01:18 +00:00
|
|
|
|
ShowNote = input.SubForms.Count > 0 || input.Criteria.Count > 0,
|
|
|
|
|
|
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Card, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler, input.Todo),
|
2026-05-02 17:08:06 +00:00
|
|
|
|
CultureName = LanguageCodes.En,
|
|
|
|
|
|
ListFormCode = input.ListFormCode,
|
|
|
|
|
|
Name = nameLangKey,
|
|
|
|
|
|
Title = titleLangKey,
|
|
|
|
|
|
Description = descLangKey,
|
|
|
|
|
|
DataSourceCode = input.DataSourceCode,
|
|
|
|
|
|
IsTenant = input.IsTenant,
|
|
|
|
|
|
IsBranch = input.IsBranch,
|
|
|
|
|
|
IsOrganizationUnit = input.IsOrganizationUnit,
|
|
|
|
|
|
SelectCommandType = input.SelectCommandType,
|
|
|
|
|
|
SelectCommand = input.SelectCommand,
|
|
|
|
|
|
KeyFieldName = input.KeyFieldName,
|
|
|
|
|
|
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
|
|
|
|
|
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
|
|
|
|
|
SortMode = GridOptions.SortModeSingle,
|
2026-06-26 20:53:30 +00:00
|
|
|
|
FilterRowJson = WizardConsts.DefaultFilterRowJson(),
|
|
|
|
|
|
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson(),
|
|
|
|
|
|
SearchPanelJson = WizardConsts.DefaultSearchPanelJson(),
|
|
|
|
|
|
GroupPanelJson = WizardConsts.DefaultGroupPanelJson(),
|
2026-07-16 14:01:18 +00:00
|
|
|
|
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.Criteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
|
2026-05-02 17:08:06 +00:00
|
|
|
|
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
|
|
|
|
|
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
|
|
|
|
|
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
|
|
|
|
|
DeleteFieldsDefaultValueJson = isDeleted
|
|
|
|
|
|
? WizardConsts.DefaultDeleteFieldsDefaultValueJson(input.KeyFieldDbSourceType)
|
|
|
|
|
|
: WizardConsts.DefaultFieldsJsonOnlyId(input.KeyFieldDbSourceType),
|
|
|
|
|
|
InsertFieldsDefaultValueJson = isCreated
|
|
|
|
|
|
? WizardConsts.DefaultInsertFieldsDefaultValueJson(input.KeyFieldDbSourceType)
|
|
|
|
|
|
: WizardConsts.DefaultFieldsJsonOnlyId(input.KeyFieldDbSourceType),
|
|
|
|
|
|
PagerOptionJson = WizardConsts.DefaultPagerOptionJson,
|
2026-06-09 13:58:58 +00:00
|
|
|
|
EditingOptionJson = WizardConsts.DefaultEditingOptionJson(titleLangKey, formWidth, formHeight, input.AllowDeleting, input.AllowAdding, input.AllowUpdating, input.ConfirmDelete, false, input.AllowDetail),
|
2026-05-02 17:08:06 +00:00
|
|
|
|
EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null,
|
2026-05-27 19:09:05 +00:00
|
|
|
|
SubFormsJson = input.SubForms.Count > 0 ? JsonSerializer.Serialize(input.SubForms) : null,
|
|
|
|
|
|
WidgetsJson = input.Widgets.Count > 0 ? JsonSerializer.Serialize(input.Widgets) : null,
|
2026-07-16 14:01:18 +00:00
|
|
|
|
WorkflowJson = HasWorkflow(workflow, input.Criteria) ? JsonSerializer.Serialize(workflow) : null,
|
|
|
|
|
|
TodoOptionJson = (input.Todo || input.DefaultLayout == "todo") &&
|
|
|
|
|
|
!string.IsNullOrEmpty(input.TodoTitleExpr) &&
|
|
|
|
|
|
!string.IsNullOrEmpty(input.TodoStatusExpr)
|
|
|
|
|
|
? JsonSerializer.Serialize(new TodoOptionDto
|
|
|
|
|
|
{
|
|
|
|
|
|
TitleExpr = input.TodoTitleExpr,
|
|
|
|
|
|
StatusExpr = input.TodoStatusExpr,
|
|
|
|
|
|
DescriptionExpr = input.TodoDescriptionExpr,
|
|
|
|
|
|
DueDateExpr = input.TodoDueDateExpr,
|
|
|
|
|
|
TagExpr = input.TodoTagExpr,
|
|
|
|
|
|
AssigneeExpr = input.TodoAssigneeExpr,
|
|
|
|
|
|
PriorityExpr = input.TodoPriorityExpr,
|
|
|
|
|
|
CompletedExpr = input.TodoCompletedExpr,
|
|
|
|
|
|
OrderExpr = input.TodoOrderExpr,
|
|
|
|
|
|
StatusOrder = input.TodoStatusOrder,
|
|
|
|
|
|
AllowDragging = input.TodoAllowDragging,
|
|
|
|
|
|
})
|
|
|
|
|
|
: null,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
|
|
|
|
|
|
// ListFormFields
|
|
|
|
|
|
var fieldOrder = 0;
|
2026-07-17 08:59:51 +00:00
|
|
|
|
var editGroupOrder = 0;
|
2026-05-02 17:08:06 +00:00
|
|
|
|
foreach (var group in input.Groups)
|
|
|
|
|
|
{
|
2026-07-17 08:59:51 +00:00
|
|
|
|
editGroupOrder++;
|
|
|
|
|
|
var editOrder = 0;
|
2026-05-02 17:08:06 +00:00
|
|
|
|
foreach (var item in group.Items)
|
|
|
|
|
|
{
|
|
|
|
|
|
fieldOrder++;
|
2026-07-17 08:59:51 +00:00
|
|
|
|
if (item.IncludeInEditingForm && item.FieldName != input.KeyFieldName)
|
|
|
|
|
|
{
|
|
|
|
|
|
editOrder++;
|
|
|
|
|
|
}
|
2026-05-02 17:08:06 +00:00
|
|
|
|
await _repoListFormField.InsertAsync(new ListFormField
|
|
|
|
|
|
{
|
|
|
|
|
|
ListFormCode = input.ListFormCode,
|
2026-07-17 08:59:51 +00:00
|
|
|
|
FieldName = item.FieldName,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
CaptionName = item.CaptionName,
|
2026-07-17 08:59:51 +00:00
|
|
|
|
Visible = item.FieldName != input.KeyFieldName,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
IsActive = true,
|
2026-05-04 12:14:16 +00:00
|
|
|
|
Width = 0, //Fit columns için hepsine sıfır veriyorum.
|
2026-05-02 17:08:06 +00:00
|
|
|
|
AllowSearch = true,
|
|
|
|
|
|
ListOrderNo = fieldOrder,
|
2026-07-17 08:59:51 +00:00
|
|
|
|
EditGroupOrderNo = item.IncludeInEditingForm ? editGroupOrder : null,
|
|
|
|
|
|
EditOrderNo = item.IncludeInEditingForm && item.FieldName != input.KeyFieldName ? editOrder : null,
|
|
|
|
|
|
EditorType2 = item.IncludeInEditingForm ? item.EditorType : null,
|
|
|
|
|
|
ColSpan = item.IncludeInEditingForm ? item.ColSpan : null,
|
|
|
|
|
|
EditorOptions = string.IsNullOrWhiteSpace(item.EditorOptions) ? null : item.EditorOptions,
|
|
|
|
|
|
EditorScript = string.IsNullOrWhiteSpace(item.EditorScript) ? null : item.EditorScript,
|
2026-05-02 17:08:06 +00:00
|
|
|
|
SourceDbType = item.DbSourceType,
|
|
|
|
|
|
CultureName = PlatformConsts.DefaultLanguage,
|
|
|
|
|
|
PermissionJson = WizardConsts.DefaultFieldPermissionJson(code),
|
|
|
|
|
|
ColumnCustomizationJson = WizardConsts.DefaultColumnCustomizationJson,
|
|
|
|
|
|
ColumnFilterJson = WizardConsts.DefaultColumnFilteringJson,
|
|
|
|
|
|
PivotSettingsJson = WizardConsts.DefaultPivotSettingsJson,
|
|
|
|
|
|
LookupJson = !string.IsNullOrWhiteSpace(item.LookupQuery)
|
|
|
|
|
|
? WizardConsts.DefaultLookupJson(item.LookupDataSourceType, item.DisplayExpr, item.ValueExpr, item.LookupQuery)
|
|
|
|
|
|
: null,
|
2026-07-17 08:59:51 +00:00
|
|
|
|
ValidationRuleJson = editingFormFieldNames.Any(f => f.FieldName == item.FieldName && f.Isrequired)
|
2026-06-09 08:31:01 +00:00
|
|
|
|
? WizardConsts.DefaultValidationRuleRequiredJson
|
|
|
|
|
|
: null
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
|
|
|
|
|
|
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-07 19:42:02 +00:00
|
|
|
|
|
2026-07-16 14:01:18 +00:00
|
|
|
|
foreach (var criteria in input.Criteria)
|
2026-06-07 19:42:02 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(criteria.Id))
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("Workflow criteria skipped because Id is empty. ListFormCode: {ListFormCode}, Title: {Title}", input.ListFormCode, criteria.Title);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await _repoListFormWorkflow.InsertAsync(new ListFormWorkflow(criteria.Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
ListFormCode = string.IsNullOrWhiteSpace(criteria.ListFormCode) ? input.ListFormCode : criteria.ListFormCode,
|
|
|
|
|
|
Kind = criteria.Kind,
|
|
|
|
|
|
Title = criteria.Title,
|
|
|
|
|
|
CompareColumn = criteria.CompareColumn,
|
|
|
|
|
|
CompareOperator = criteria.CompareOperator,
|
|
|
|
|
|
CompareValue = criteria.CompareValue,
|
|
|
|
|
|
Approver = criteria.Approver,
|
|
|
|
|
|
NextOnStart = criteria.NextOnStart,
|
|
|
|
|
|
NextOnTrue = criteria.NextOnTrue,
|
|
|
|
|
|
NextOnFalse = criteria.NextOnFalse,
|
|
|
|
|
|
NextOnApprove = criteria.NextOnApprove,
|
|
|
|
|
|
NextOnReject = criteria.NextOnReject,
|
|
|
|
|
|
PositionX = criteria.PositionX,
|
|
|
|
|
|
PositionY = criteria.PositionY,
|
|
|
|
|
|
CompareOutcomesJson = JsonSerializer.Serialize(criteria.CompareOutcomes ?? []),
|
|
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-27 19:09:05 +00:00
|
|
|
|
private static bool HasWorkflow(WorkflowDto workflow, List<ListFormWorkflowCriteriaDto> criteria)
|
|
|
|
|
|
{
|
|
|
|
|
|
return workflow != null && (
|
|
|
|
|
|
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
|
|
|
|
|
!string.IsNullOrWhiteSpace(workflow.ApprovalDateFieldName) ||
|
|
|
|
|
|
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
|
|
|
|
|
!string.IsNullOrWhiteSpace(workflow.ApprovalDescriptionFieldName) ||
|
|
|
|
|
|
(criteria?.Count ?? 0) > 0
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-06 22:22:35 +00:00
|
|
|
|
private static void EnsureUniqueWorkflowCriteriaTitles(List<ListFormWorkflowCriteriaDto> criteria)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (criteria == null || criteria.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var baseTitles = criteria
|
|
|
|
|
|
.Select(x => string.IsNullOrWhiteSpace(x.Title) ? NormalizeWorkflowTitleFallback(x.Kind) : x.Title.Trim())
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
var duplicateTitles = baseTitles
|
|
|
|
|
|
.GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
|
.Where(x => x.Count() > 1)
|
|
|
|
|
|
.Select(x => x.Key)
|
|
|
|
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
var titleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
var usedTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in criteria)
|
|
|
|
|
|
{
|
|
|
|
|
|
var baseTitle = string.IsNullOrWhiteSpace(item.Title) ? NormalizeWorkflowTitleFallback(item.Kind) : item.Title.Trim();
|
|
|
|
|
|
var title = baseTitle;
|
|
|
|
|
|
|
|
|
|
|
|
if (duplicateTitles.Contains(baseTitle))
|
|
|
|
|
|
{
|
|
|
|
|
|
titleCounts.TryGetValue(baseTitle, out var count);
|
|
|
|
|
|
count++;
|
|
|
|
|
|
titleCounts[baseTitle] = count;
|
|
|
|
|
|
title = $"{baseTitle}{count}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (usedTitles.Contains(title))
|
|
|
|
|
|
{
|
|
|
|
|
|
var index = 1;
|
|
|
|
|
|
var candidate = $"{title}{index}";
|
|
|
|
|
|
while (usedTitles.Contains(candidate))
|
|
|
|
|
|
{
|
|
|
|
|
|
index++;
|
|
|
|
|
|
candidate = $"{title}{index}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
title = candidate;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
item.Title = title;
|
|
|
|
|
|
usedTitles.Add(title);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string NormalizeWorkflowTitleFallback(string kind)
|
|
|
|
|
|
{
|
|
|
|
|
|
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 11:53:23 +00:00
|
|
|
|
private static void NormalizeWorkflowCriteriaIds(string listFormCode, List<ListFormWorkflowCriteriaDto> criteria)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(listFormCode) || criteria == null || criteria.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in criteria)
|
|
|
|
|
|
{
|
|
|
|
|
|
item.Id = NormalizeWorkflowCriteriaId(listFormCode, item.Id);
|
|
|
|
|
|
item.ListFormCode = listFormCode;
|
|
|
|
|
|
item.NextOnStart = NormalizeWorkflowTargetId(listFormCode, item.NextOnStart);
|
|
|
|
|
|
item.NextOnTrue = NormalizeWorkflowTargetId(listFormCode, item.NextOnTrue);
|
|
|
|
|
|
item.NextOnFalse = NormalizeWorkflowTargetId(listFormCode, item.NextOnFalse);
|
|
|
|
|
|
item.NextOnApprove = NormalizeWorkflowTargetId(listFormCode, item.NextOnApprove);
|
|
|
|
|
|
item.NextOnReject = NormalizeWorkflowTargetId(listFormCode, item.NextOnReject);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var outcome in item.CompareOutcomes ?? [])
|
|
|
|
|
|
{
|
|
|
|
|
|
outcome.TargetId = NormalizeWorkflowTargetId(listFormCode, outcome.TargetId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string NormalizeWorkflowTargetId(string listFormCode, string id)
|
|
|
|
|
|
{
|
|
|
|
|
|
return string.IsNullOrWhiteSpace(id)
|
|
|
|
|
|
? string.Empty
|
|
|
|
|
|
: NormalizeWorkflowCriteriaId(listFormCode, id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string NormalizeWorkflowCriteriaId(string listFormCode, string id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var trimmed = id?.Trim();
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(trimmed))
|
|
|
|
|
|
{
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
{
|
|
|
|
|
|
return trimmed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return trimmed.StartsWith("N", StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
|
? $"{listFormCode}-{trimmed}"
|
|
|
|
|
|
: trimmed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-02 17:08:06 +00:00
|
|
|
|
private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(key)) return;
|
|
|
|
|
|
|
|
|
|
|
|
var langKey = await _repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == _appName && a.Key == key)
|
|
|
|
|
|
?? await _repoLangKey.InsertAsync(new LanguageKey { ResourceName = _appName, Key = key }, autoSave: true);
|
|
|
|
|
|
|
|
|
|
|
|
var existingTexts = await _repoLangText.GetListAsync(a => a.ResourceName == _appName && a.Key == langKey.Key);
|
|
|
|
|
|
|
|
|
|
|
|
var existingEn = existingTexts.FirstOrDefault(a => a.CultureName == _cultureNameDefault);
|
|
|
|
|
|
if (existingEn == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoLangText.InsertAsync(new LanguageText
|
|
|
|
|
|
{
|
|
|
|
|
|
ResourceName = _appName,
|
|
|
|
|
|
Key = langKey.Key,
|
|
|
|
|
|
CultureName = _cultureNameDefault,
|
|
|
|
|
|
Value = textEn ?? key
|
|
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var existingTr = existingTexts.FirstOrDefault(a => a.CultureName == LanguageCodes.Tr);
|
|
|
|
|
|
if (existingTr == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoLangText.InsertAsync(new LanguageText
|
|
|
|
|
|
{
|
|
|
|
|
|
ResourceName = _appName,
|
|
|
|
|
|
Key = langKey.Key,
|
|
|
|
|
|
CultureName = LanguageCodes.Tr,
|
|
|
|
|
|
Value = textTr ?? key
|
|
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-06 15:22:14 +00:00
|
|
|
|
|
|
|
|
|
|
private async Task EnsureLangKeyAsync(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(key)) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (!await _repoLangKey.AnyAsync(a => a.ResourceName == _appName && a.Key == key))
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repoLangKey.InsertAsync(new LanguageKey { ResourceName = _appName, Key = key }, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-02 17:08:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|