sozsoft-platform/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs

706 lines
32 KiB
C#
Raw Normal View History

2026-08-17 05:56:52 +00:00
using System;
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;
2026-08-17 05:56:52 +00:00
using Microsoft.Extensions.Configuration;
2026-05-02 17:08:06 +00:00
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;
private readonly IRepository<ListFormWorkflow, string> _repoListFormWorkflow;
2026-08-17 05:56:52 +00:00
private readonly IConfiguration _configuration;
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,
IRepository<ListFormWorkflow, string> repoListFormWorkflow,
2026-08-17 05:56:52 +00:00
IConfiguration configuration,
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;
_repoListFormWorkflow = repoListFormWorkflow;
2026-08-17 05:56:52 +00:00
_configuration = configuration;
2026-05-02 17:08:06 +00:00
_logger = logger;
}
public async Task SeedAsync(DataSeedContext context)
{
2026-08-17 05:56:52 +00:00
var wizardDataPath = SeedPathResolver.GetPath(_configuration, "WizardData");
2026-05-02 17:08:06 +00:00
if (!Directory.Exists(wizardDataPath))
{
_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)
{
_logger.LogInformation("No JSON files found in WizardData directory, skipping.");
2026-05-02 17:08:06 +00:00
return;
}
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", jsonFiles.Length);
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)
{
_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))
{
_logger.LogWarning("WizardName is empty, skipped: {FilePath}", filePath);
2026-05-02 17:08:06 +00:00
continue;
}
var fileName = Path.GetFileName(filePath);
2026-07-17 20:23:43 +00:00
await BackfillMenuParentShortNameAsync(seedFile.Wizard);
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))
{
_logger.LogInformation("[{File}] '{WizardName}' already exists, skipped.", fileName, wizardName);
2026-05-02 17:08:06 +00:00
continue;
}
_logger.LogInformation("[{File}] '{WizardName}' is being applied...", fileName, wizardName);
2026-05-02 17:08:06 +00:00
await ApplyWizardSeedAsync(seedFile);
_logger.LogInformation("[{File}] '{WizardName}' has been successfully applied.", fileName, wizardName);
2026-05-02 17:08:06 +00:00
}
catch (Exception ex)
{
_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;
input.SubForms ??= new List<SubFormDto>();
input.Widgets ??= new List<WidgetEditDto>();
2026-07-28 18:36:37 +00:00
input.WorkflowDto ??= new WorkflowDto();
input.WorkflowDto.Criteria ??= [];
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowDto.Criteria);
var workflow = input.WorkflowDto;
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-28 18:36:37 +00:00
NormalizeWorkflowCriteriaIds(listFormCode, workflow.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
}
// 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();
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);
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);
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);
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);
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);
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);
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
// // 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
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)
{
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-07-17 20:23:43 +00:00
ShortName = input.MenuParentShortName,
2026-06-06 15:22:14 +00:00
Icon = menuParentIcon,
Order = maxRootOrder + 1,
2026-05-02 17:08:06 +00:00
}, autoSave: true);
}
// Menu
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0;
var menuOrder = input.MenuOrder > 0 ? input.MenuOrder : maxChildOrder + 1;
input.MenuOrder = menuOrder;
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),
RequiredPermissionName = permRead.Name,
Order = menuOrder,
2026-05-02 17:08:06 +00:00
}, autoSave: true);
}
else if (existingMenu.Order != menuOrder)
{
existingMenu.Order = menuOrder;
await _repoMenu.UpdateAsync(existingMenu, autoSave: true);
}
2026-05-02 17:08:06 +00:00
// DataSource
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,
ItemType = "group"
2026-05-02 17:08:06 +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();
// 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);
}
var existingWorkflowCriteria = await _repoListFormWorkflow.GetListAsync(a => a.ListFormCode == input.ListFormCode);
if (existingWorkflowCriteria.Count > 0)
{
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
}
// EditingForm'da required olan alanları tespit et, ValidationRuleJson eklemek için
var editingFormFieldNames = input.Groups
.SelectMany(g => g.Items)
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
.Select(i => new { FieldName = i.FieldName, Isrequired = i.IsRequired })
.ToList();
// Heigth ve Width hesaplama
var groupLayouts = input.Groups
.Select(g =>
{
var items = g.Items
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
.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-28 18:36:37 +00:00
ShowNote = input.SubForms.Count > 0 || workflow.Criteria.Count > 0,
2026-07-16 14:01:18 +00:00
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-28 18:36:37 +00:00
SelectionJson = WizardConsts.DefaultSelectionSingleJson(workflow.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)
2026-07-17 20:23:43 +00:00
: WizardConsts.DefaultDeleteFieldsJsonOnlyId(input.KeyFieldDbSourceType),
2026-05-02 17:08:06 +00:00
InsertFieldsDefaultValueJson = isCreated
? WizardConsts.DefaultInsertFieldsDefaultValueJson(input.KeyFieldDbSourceType)
2026-07-17 20:23:43 +00:00
: WizardConsts.DefaultInsertFieldsJsonOnlyId(input.KeyFieldDbSourceType),
2026-05-02 17:08:06 +00:00
PagerOptionJson = WizardConsts.DefaultPagerOptionJson,
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,
SubFormsJson = input.SubForms.Count > 0 ? JsonSerializer.Serialize(input.SubForms) : null,
WidgetsJson = input.Widgets.Count > 0 ? JsonSerializer.Serialize(input.Widgets) : null,
2026-07-28 18:36:37 +00:00
WorkflowJson = HasWorkflow(workflow) ? JsonSerializer.Serialize(workflow) : null,
TreeOptionJson = (input.Tree || input.DefaultLayout == "tree") &&
!string.IsNullOrEmpty(input.TreeOptionDto?.ParentIdExpr)
? JsonSerializer.Serialize(input.TreeOptionDto)
: null,
GanttOptionJson = (input.Gantt || input.DefaultLayout == "gantt") &&
!string.IsNullOrEmpty(input.GanttOptionDto?.ParentIdExpr)
? JsonSerializer.Serialize(input.GanttOptionDto)
: null,
SchedulerOptionJson = (input.Scheduler || input.DefaultLayout == "scheduler") &&
!string.IsNullOrEmpty(input.SchedulerOptionDto?.TextExpr)
? JsonSerializer.Serialize(input.SchedulerOptionDto)
: null,
2026-07-16 14:01:18 +00:00
TodoOptionJson = (input.Todo || input.DefaultLayout == "todo") &&
2026-07-28 18:36:37 +00:00
!string.IsNullOrEmpty(input.TodoOptionDto?.TitleExpr) &&
!string.IsNullOrEmpty(input.TodoOptionDto?.StatusExpr)
? JsonSerializer.Serialize(input.TodoOptionDto)
2026-07-16 14:01:18 +00:00
: null,
2026-05-02 17:08:06 +00:00
}, autoSave: true);
// ListFormFields
var fieldOrder = 0;
var editGroupOrder = 0;
2026-05-02 17:08:06 +00:00
foreach (var group in input.Groups)
{
editGroupOrder++;
var editOrder = 0;
2026-05-02 17:08:06 +00:00
foreach (var item in group.Items)
{
fieldOrder++;
if (item.IncludeInEditingForm && item.FieldName != input.KeyFieldName)
{
editOrder++;
}
2026-05-02 17:08:06 +00:00
await _repoListFormField.InsertAsync(new ListFormField
{
ListFormCode = input.ListFormCode,
FieldName = item.FieldName,
2026-05-02 17:08:06 +00:00
CaptionName = item.CaptionName,
Visible = item.FieldName != input.KeyFieldName,
2026-05-02 17:08:06 +00:00
IsActive = true,
Width = 0, //Fit columns için hepsine sıfır veriyorum.
2026-05-02 17:08:06 +00:00
AllowSearch = true,
ListOrderNo = fieldOrder,
EditGroupOrderNo = item.IncludeInEditingForm ? editGroupOrder : null,
2026-07-17 20:23:43 +00:00
EditOrderNo = item.IncludeInEditingForm && item.FieldName != input.KeyFieldName
? editOrder
: null,
EditorType2 = item.EditorType,
ColSpan = item.ColSpan,
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,
ValidationRuleJson = editingFormFieldNames.Any(f => f.FieldName == item.FieldName && f.Isrequired)
? WizardConsts.DefaultValidationRuleRequiredJson
: null
2026-05-02 17:08:06 +00:00
}, autoSave: true);
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
}
}
2026-07-28 18:36:37 +00:00
foreach (var criteria in workflow.Criteria)
{
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-07-17 20:23:43 +00:00
private async Task BackfillMenuParentShortNameAsync(ListFormWizardDto input)
{
if (string.IsNullOrWhiteSpace(input.MenuParentCode) ||
string.IsNullOrWhiteSpace(input.MenuParentShortName))
return;
var menuParent = await _repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
if (menuParent == null || !string.IsNullOrWhiteSpace(menuParent.ShortName))
return;
menuParent.ShortName = input.MenuParentShortName.Trim();
await _repoMenu.UpdateAsync(menuParent, autoSave: true);
_logger.LogInformation(
"Menu parent '{MenuParentCode}' ShortName backfilled as '{ShortName}'.",
input.MenuParentCode,
menuParent.ShortName);
}
2026-07-28 18:36:37 +00:00
private static bool HasWorkflow(WorkflowDto workflow)
{
return workflow != null && (
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
!string.IsNullOrWhiteSpace(workflow.ApprovalDateFieldName) ||
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
!string.IsNullOrWhiteSpace(workflow.ApprovalDescriptionFieldName) ||
2026-07-28 18:36:37 +00:00
workflow.Criteria.Count > 0
);
}
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
}