diff --git a/.github/instructions/lowcode.instructions.md b/.github/instructions/lowcode.instructions.md index 7249800c..5ce14e47 100644 --- a/.github/instructions/lowcode.instructions.md +++ b/.github/instructions/lowcode.instructions.md @@ -1986,6 +1986,11 @@ işlemde siler. Onay diyaloğu sırayı ve tabloları gösterir: - **Tablolar en sonda** düşürülür: bütün ekranlar gittikten sonra kullanım yeniden ölçülür; aynı tabloyu paylaşan alt formlar (`Hr_T_HealthCard` gibi) tabloyu bir kez listeler. Bağlantılar veri kaynağı kaydı silinmeden önce çözülür. Tablo seçmek yine `App.SqlQueryManager` ister. +- **Talebin Custom Endpoint'leri de gider:** tablo talebe aitse (`create_table`/tasarımcı scripti var, + talep dışında ekran kullanmıyor) onu sorgulayan Custom Endpoint'ler tabloyu tutmaz; veritabanından + ve `data/App.DeveloperKit.CustomEndpoints.json`'dan (migrate edilmemiş satır dahil) silinir — + tablo seçilmese de (`WizardDeleteTableDto.CustomEndpoints`, `WizardTableCleaner.DeleteCustomEndpointsAsync`). + Diyalog bunları tablonun altında listeler. Tek wizard silmede endpoint yine tabloyu "kullanımda" tutar. - Rapor tek wizard silmeyle aynı sözleşmedir (`WizardDeleteResultDto`, bütün wizard'ların kayıtları tek listede). diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpQueryAppService.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpQueryAppService.cs index 6b7c7ad2..6f0f43d9 100644 --- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpQueryAppService.cs +++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpQueryAppService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data.Common; using System.Linq; using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Sozsoft.Mcp.Dtos; @@ -30,7 +31,7 @@ namespace Sozsoft.Mcp; /// icindedir. /// [Authorize(PlatformConsts.AppCodes.DeveloperKits.McpServers.Invoke)] -public class McpQueryAppService( +public partial class McpQueryAppService( IMcpServerConfigProvider serverConfigProvider, IMcpSqlGuard sqlGuard, IMcpQueryExecutor queryExecutor, @@ -115,10 +116,67 @@ public class McpQueryAppService( } catch (DbException ex) { - throw new UserFriendlyException(L[McpErrorCodes.SelectFailed, ex.Message]); + var columns = IsUnknownColumnError(ex) ? await DescribeTableColumnsAsync(context, sql) : null; + throw new UserFriendlyException(columns is null + ? L[McpErrorCodes.SelectFailed, ex.Message] + : L[McpErrorCodes.SelectFailedColumns, ex.Message, columns]); } } + private const int MaxDescribedTables = 5; + + /// SQL Server 207 ("Invalid column name"), PostgreSQL 42703 (undefined_column). + private static bool IsUnknownColumnError(DbException ex) => + ex.SqlState == "42703" || ex.Message.Contains("Invalid column name", StringComparison.OrdinalIgnoreCase); + + /// + /// Sorgunun FROM/JOIN ile andigi tablolarin gercek kolonlari (Tablo(A, B, C); …). Model + /// kolonu tahmin ettiyse dogru listeyi ayni cevapta gorur ve ayrica sema araci cagirmadan duzeltir. + /// Kolonlar okunamazsa null doner; asil hata yine iletilir. + /// + private async Task DescribeTableColumnsAsync(McpDataSourceContext context, string sql) + { + var tables = TableReferenceRegex().Matches(sql) + .Select(match => match.Groups["table"].Value) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Take(MaxDescribedTables) + .ToList(); + if (tables.Count == 0) + return null; + + var parameters = tables.Select((table, index) => (Name: $"t{index}", Table: table)) + .ToDictionary(p => p.Name, p => (object)p.Table); + + try + { + var rows = await queryExecutor.ExecuteAsync( + context, + $"SELECT TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN ({string.Join(", ", parameters.Keys.Select(name => "@" + name))}) ORDER BY TABLE_NAME, ORDINAL_POSITION", + parameters, + int.MaxValue); + + var described = rows.Rows + .Select(row => (Table: ReadIgnoreCase(row, "TABLE_NAME"), Column: ReadIgnoreCase(row, "COLUMN_NAME"))) + .Where(item => item.Table is not null && item.Column is not null) + .GroupBy(item => item.Table!, StringComparer.OrdinalIgnoreCase) + .Select(group => $"{group.Key}({string.Join(", ", group.Select(item => item.Column))})") + .ToList(); + + return described.Count == 0 ? null : string.Join("; ", described); + } + catch (DbException) + { + return null; + } + } + + private static string? ReadIgnoreCase(Dictionary row, string key) => + row.FirstOrDefault(pair => string.Equals(pair.Key, key, StringComparison.OrdinalIgnoreCase)).Value?.ToString(); + + /// FROM/JOIN'den sonraki ad; sema ve kose parantezi/tirnak atilir ([dbo].[Tablo] → Tablo). + [GeneratedRegex(@"\b(?:FROM|JOIN)\s+(?:[\[""]?\w+[\]""]?\.)?[\[""]?(?\w+)[\]""]?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] + private static partial Regex TableReferenceRegex(); + public async Task> GetSavedQueriesAsync(string? search = null) { await EnsureToolAllowedAsync(McpToolNames.ListQueries); diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpErrorCodes.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpErrorCodes.cs index c7bc315e..e61b2095 100644 --- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpErrorCodes.cs +++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpErrorCodes.cs @@ -48,6 +48,7 @@ public static class McpErrorCodes public const string VerifySelectPassed = Prefix + "VerifySelectPassed"; public const string VerifySelectFailed = Prefix + "VerifySelectFailed"; public const string SelectFailed = Prefix + "SelectFailed"; + public const string SelectFailedColumns = Prefix + "SelectFailedColumns"; public const string VerifyComponentInactive = Prefix + "VerifyComponentInactive"; public const string VerifyComponentRouteMismatch = Prefix + "VerifyComponentRouteMismatch"; public const string VerifySeedClosureIncomplete = Prefix + "VerifySeedClosureIncomplete"; diff --git a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/WizardSeedFileDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/WizardSeedFileDto.cs index 49ca1205..eec297ef 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/WizardSeedFileDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/WizardSeedFileDto.cs @@ -513,6 +513,12 @@ public class WizardDeleteTableDto /// Tabloyla birlikte silinecek seed dosyalari, seed klasorune gore goreli. public List Files { get; set; } = []; + + /// + /// Talep silmesinde talepten kalkan Custom Endpoint'ler (ad): tablo talebe aittir (scripti var, + /// talep disinda ekran kullanmiyor) ve endpoint onu sorgular. Tablo secilmese de silinir. + /// + public List CustomEndpoints { get; set; } = []; } /// Silmeden once onay diyaloguna gelen analiz. diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs index f8f90f06..a61ef4c1 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs @@ -315,6 +315,8 @@ public class ListFormWizardAppService( : tableColumns.ContainsKey("IsDeleted"); isCreated = tableColumns.ContainsKey("CreatorId"); ValidateImageColumnLengths(input, tableColumns); + foreach (var item in input.Groups.SelectMany(g => g.Items)) + item.CaptionName = await ResolveLanguageKeyCasingAsync(rows, item.CaptionName); listFormDto = input.BuildListForm(isDeleted, isCreated); fieldDtos = input.BuildFields(); @@ -429,6 +431,24 @@ public class ListFormWizardAppService( inserted.LanguageKeys.Add(key); } + /// + /// Anahtar yalnizca harf buyuklugu farkiyla zaten varsa onun yazimini dondurur. Anahtar + /// (ResourceName, Key) uzerinde harf duyarsiz tekildir, metin sozlugu ise harf duyarlidir: + /// farkli yazimla birakilan alan basligi ikinci bir anahtar acamaz ve ekranda anahtar olarak kalir. + /// + private async Task ResolveLanguageKeyCasingAsync(WizardSeedRowStore rows, string? key) + { + if (string.IsNullOrWhiteSpace(key)) + return key; + + var fileKey = WizardSeedRowStore.Text(rows.Find(WizardSeedRowStore.LanguageTextFile, nameof(LanguageText.Key), key), nameof(LanguageText.Key)); + if (fileKey != null) + return fileKey; + + var res = PlatformConsts.AppName; + return (await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == res && a.Key == key))?.Key ?? key; + } + private void UpsertLanguageRows(WizardSeedRowStore rows, string res, string key, string? textEn, string? textTr) { foreach (var (culture, value) in new[] { (cultureNameDefault, textEn), (LanguageCodes.Tr, textTr) }) @@ -1194,6 +1214,10 @@ public class ListFormWizardAppService( } } + // Endpoint'ler tablodan once gider; tablo secilmese de talebin parcasi olarak silinir. + await _tableCleaner.DeleteCustomEndpointsAsync( + [.. tables.SelectMany(t => t.CustomEndpoints).Distinct(StringComparer.OrdinalIgnoreCase)], result.Items); + if (tables.Count > 0) await _tableCleaner.DropAsync(tables, dropTables, connections, result.Items); diff --git a/api/src/Sozsoft.Platform.Application/ListForms/WizardTableCleaner.cs b/api/src/Sozsoft.Platform.Application/ListForms/WizardTableCleaner.cs index 1af44803..39d37738 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/WizardTableCleaner.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/WizardTableCleaner.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; @@ -35,6 +36,7 @@ public class WizardTableCleaner( IRepository repoCustomEndpoint, IRepository repoCustomComponent, IRepository repoCrudEndpoint, + IListFormSeedDataSynchronizer seedDataSynchronizer, IDynamicDataManager dynamicDataManager, IConfiguration configuration, ILogger logger) : ITransientDependency @@ -79,6 +81,18 @@ public class WizardTableCleaner( .Where(u => u.RecordType != WizardRecordTypeEnum.ListForm || batchListForms?.Contains(u.Name, StringComparer.OrdinalIgnoreCase) != true) .ToList(); + + // Talep silmesinde talebin tablosunu sorgulayan Custom Endpoint'ler talebin parcasidir; tabloyu + // tutmaz, taleple birlikte silinir. Migrate edilmemis endpoint yalnizca seed dosyasindadir. + if (batchListForms != null && HasCreateTableScript(tableName) && + usedBy.All(u => u.RecordType == WizardRecordTypeEnum.CustomEndpoint)) + { + table.CustomEndpoints = [.. usedBy.Select(u => u.Name) + .Concat(await FindSeedEndpointsAsync(table)) + .Distinct(StringComparer.OrdinalIgnoreCase)]; + usedBy.Clear(); + } + if (usedBy.Count > 0) { table.Reason = WizardDeleteReasonEnum.InUse; @@ -167,8 +181,53 @@ public class WizardTableCleaner( return result; } + /// + /// Talep silmesinde talebin tablolarini sorgulayan Custom Endpoint'leri veritabanindan ve + /// data/App.DeveloperKit.CustomEndpoints.json dosyasindan siler. + /// + public async Task DeleteCustomEndpointsAsync(IReadOnlyCollection names, List report) + { + if (names.Count == 0) + return; + + var endpoints = await repoCustomEndpoint.GetListAsync(a => names.Contains(a.Name)); + if (endpoints.Count > 0) + await repoCustomEndpoint.DeleteManyAsync(endpoints, autoSave: true); + + await seedDataSynchronizer.RemoveRowsByFieldAsync(CustomEndpointsListFormCode, nameof(CustomEndpoint.Name), names); + + report.AddRange(names.Select(name => new WizardDeleteLogItemDto + { + RecordType = WizardRecordTypeEnum.CustomEndpoint, + Name = name, + Status = WizardDeleteStatusEnum.Deleted, + })); + } + // ── Analiz ───────────────────────────────────────────────────────────────────── + private const string CustomEndpointsListFormCode = PlatformConsts.AppCodes.DeveloperKits.CustomEndpoints; + + /// Henuz migrate edilmemis, yalnizca seed dosyasinda duran ve tabloyu sorgulayan endpoint'ler. + private async Task> FindSeedEndpointsAsync(WizardDeleteTableDto table) + { + var path = ScopeFile(SeedPathResolver.GetDataFilePath(CustomEndpointsListFormCode)); + if (!File.Exists(path)) + return []; + + var file = JsonSerializer.Deserialize(await File.ReadAllTextAsync(path), ListFormSeedDataFileDto.JsonOptions); + var pattern = TablePattern(table.TableName); + + string? Text(Dictionary row, string field) => + row.FirstOrDefault(kv => string.Equals(kv.Key, field, StringComparison.OrdinalIgnoreCase)).Value?.ToString(); + + return [.. (file?.Rows ?? []) + .Where(row => string.Equals(Text(row, nameof(CustomEndpoint.DataSourceCode)), table.DataSourceCode, StringComparison.OrdinalIgnoreCase) && + Text(row, nameof(CustomEndpoint.Sql)) is { } sql && pattern.IsMatch(sql)) + .Select(row => Text(row, nameof(CustomEndpoint.Name))) + .OfType()]; + } + private async Task ReadTableStateAsync(WizardDeleteTableDto table) { try diff --git a/configs/seeds/data/App.DevRequests/5d11851e-5f00-4a0b-b5a3-b8211e56c8b5.json b/configs/seeds/data/App.DevRequests/5d11851e-5f00-4a0b-b5a3-b8211e56c8b5.json index 1cb49726..64438c8c 100644 --- a/configs/seeds/data/App.DevRequests/5d11851e-5f00-4a0b-b5a3-b8211e56c8b5.json +++ b/configs/seeds/data/App.DevRequests/5d11851e-5f00-4a0b-b5a3-b8211e56c8b5.json @@ -9,15 +9,15 @@ "TenantId": null, "UserName": "system@sozsoft.com", "RequestDate": null, - "Status": "Testing", + "Status": "Backlog", "Title": "Inventory -> Tanımlamalar", - "Description": "

Ürünler : Ürün listesi ve detayı aşağıdaki örneklendirilmiştir. Ürünler Satış, Masraflar, Satınalma olarak 3 seçeneği var. Satış seçerse sadece Satış siparişlerinde görünür. Satınalma seçerse Satınalınabilir anlamına gelir. Ürün Türü Mal veya Hizmet olabilir. Envanter Takibi yapılıp yapılmayacağı seçilebilir. Eğer takip edilecekse Seri No, Lot Numarası veya Miktara göre seklinden seçilecek.


Nitelikler ve Varyantlar seçeneğinde yazılabilecek değerler Nitelik olarak tanımlanmalı. İçerisindeki değerleri de tanımlanabilir. Nitelik seçilip değerleri birden fazla şekilde yapılabilir. Eğer nitelikli bir ürün tanımlanırsa o varyantlı ürün listeler de seçilecektir.


Satınalma kısmında o ürünü hangi tedarikçiden hangi bedele aldığımızı belirtiyoruz. Ürün ve reçetede kullanılan Birim sütunun Adm_T_Uom tablosu ile ilişkili olmalı. Adm_T_Uom tablosundaki Id bilgisi nvarchar(64)







Reçeteler : Ürünlerin hammadde ve yarı mamülerin ve tüketim miktarlarının belirlendiği kısımdır. Ayrıca tanımlanan operasyonların tanımlandığı listedir. Hangi bileşenlerin hangi operasyon ile tüketildiği belirlenecektir.

Operasyonlar : Inventory -> Tanımlamalar menüsünde yer alacaktır.

İş Merkezi tanımlamaları : Inventory -> Tanımlamalar menüsünde yer alacaktır.


Eğer nitelikli bir ürün tanımlanırsa o zaman Varyantlı liste oluşur ve tüm sistem varyantlı seçeneker çalışır. Eğer nitelik tanımlamazsa ana ürün seçilecektir.

Ürün reçetesine genel bakış raporu bu ERP ssitemlerdeki BOM ile aynı. Reçetelerin malzemelerinde recursive şekilde iç içe kullanılan diğer malzemeler ve miktarların hesaplanması gerekiyor.




", + "Description": "

Ürünler : Ürün listesi ve detayı aşağıdaki örneklendirilmiştir. Ürünler Satış, Masraflar, Satınalma olarak 3 seçeneği var. Satış seçerse sadece Satış siparişlerinde görünür. Satınalma seçerse Satınalınabilir anlamına gelir. Ürün Türü Mal veya Hizmet olabilir. Envanter Takibi yapılıp yapılmayacağı seçilebilir. Eğer takip edilecekse Seri No, Lot Numarası veya Miktara göre seklinden seçilecek.


Nitelikler ve Varyantlar seçeneğinde yazılabilecek değerler Nitelik olarak tanımlanmalı. İçerisindeki değerleri de tanımlanabilir. Nitelik seçilip değerleri birden fazla şekilde yapılabilir. Eğer nitelikli bir ürün tanımlanırsa o varyantlı ürün listeler de seçilecektir.


Satınalma kısmında o ürünü hangi tedarikçiden hangi bedele aldığımızı belirtiyoruz. Ürün ve reçetede kullanılan Birim sütunun Adm_T_Uom tablosu ile ilişkili olmalı. Adm_T_Uom tablosundaki Id bilgisi nvarchar(64)



Ürün listesinde ürün adı, resmi, Satış, Masraflar ve Satınalma sütunlarından oluşmalı. Diğer bilgiler Ürün listesindeki Detay ksımında Tablar şeklinde resimdeki gibi görüntülenmeli. App.Definitions.Contact gibi olabilir.



Nitelikler kısmı Nitelik kısmında seçilmeli ve Değerler birden fazla seçebilecek bir sütundan oluşmalı.




Reçeteler : Ürünlerin hammadde ve yarı mamülerin ve tüketim miktarlarının belirlendiği kısımdır. Ayrıca tanımlanan operasyonların tanımlandığı listedir. Hangi bileşenlerin hangi operasyon ile tüketildiği belirlenecektir. Ürün bazında da reçete yazılabilir. Her bir ürünün Varyantı bazında da reçete yazılabilri. Bom türü eklenmeyecek


İş Merkezi tanımlamaları : Inventory -> Tanımlamalar menüsünde yer alacaktır.İş Merkezi lsitesinde iş merkezi adı, etiketler, çalışma saatleri (Weekly working time limit (h) ile setting kısmından alınabilir.)İş Merkezi lsitesinde iş merkezi adı, etiketler, çalışma saatleri (Weekly working time limit (h) ile setting kısmından alınabilir.) İş merkezi lsitesinin detayında Genel Bİlgi, Ürün kapasitesi bilgilerini tab olarak göstermeli


Operasyonlar : Inventory -> Tanımlamalar menüsünde yer alacaktır.Operasyon Listesinde Operasyon Adı, İş Merkezi Adı, Varsayılan Süre sütunlarından oluşmalı. Detay butonu ile Açıklamalar listesinde yer almalı



Eğer nitelikli bir ürün tanımlanırsa o zaman Varyantlı liste oluşur ve tüm sistem varyantlı seçeneker çalışır. Eğer nitelik tanımlamazsa ana ürün seçilecektir.

Ürün reçetesine genel bakış raporu bu ERP ssitemlerdeki BOM ile aynı. Reçetelerin malzemelerinde recursive şekilde iç içe kullanılan diğer malzemeler ve miktarların hesaplanması gerekiyor. Bu ksıım Tree Layout olarak sunulmalı.




", "DueDate": "2026-10-06T21:02:00", "Tags": "IN", "Assignees": "system@sozsoft.com", "Priority": null, "Completed": false, - "SortOrder": 0, + "SortOrder": 2, "Timesheets": null, "SubTasks": null, "Analysis": "" diff --git a/configs/seeds/data/App.Languages.LanguageText.json b/configs/seeds/data/App.Languages.LanguageText.json index 363e5aad..70efea84 100644 --- a/configs/seeds/data/App.Languages.LanguageText.json +++ b/configs/seeds/data/App.Languages.LanguageText.json @@ -39933,17 +39933,17 @@ "Value": "Ağırlık" }, { - "Id": "bbcfd955-c535-5c01-b9bb-c7321dd61d89", + "Id": "5961b6ba-f1d4-510a-a9cd-ac723dbad6a8", "CultureName": "en", "ResourceName": "Platform", - "Key": "App.Listform.ListformField.WorkcenterId", - "Value": "Workcenter" + "Key": "App.Listform.ListformField.WorkCenterId", + "Value": "Work Center" }, { - "Id": "994c5396-1eea-5a2f-a4dc-11d1c3b1bb8b", + "Id": "db209c22-79f7-53aa-b125-06be98097e3e", "CultureName": "tr", "ResourceName": "Platform", - "Key": "App.Listform.ListformField.WorkcenterId", + "Key": "App.Listform.ListformField.WorkCenterId", "Value": "İş Merkezi" }, { @@ -40702,6 +40702,20 @@ "Key": "App.ListForm.WizardDeleteTableNoPermission", "Value": "Tablo silmek için SQL Query Manager yetkisi gerekir." }, + { + "Id": "2f3d5aa3-35d7-5d2b-bc3d-ba0363eb838b", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.ListForm.WizardDeleteCustomEndpoints", + "Value": "Custom Endpoints querying this table are deleted with the request:" + }, + { + "Id": "5c30228d-7a64-509d-92cb-5218d0cacfc5", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.ListForm.WizardDeleteCustomEndpoints", + "Value": "Bu tabloyu sorgulayan Custom Endpoint'ler taleple birlikte silinir:" + }, { "Id": "50b48483-4900-53d7-8264-e1a31c899097", "CultureName": "en", @@ -69290,6 +69304,20 @@ "Key": "App.Mcp.Error.SelectFailed", "Value": "Sorgu çalışmadı: {0} Bir kolonun var olduğunu varsayma (IsDeleted, TenantId, IsActive her tabloda yoktur); tablonun kolonlarını search_schema ya da INFORMATION_SCHEMA.COLUMNS üzerinde bir SELECT ile kontrol edip sorguyu düzelt." }, + { + "Id": "f78c1319-0925-5e25-af53-eed5f6039eb1", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Mcp.Error.SelectFailedColumns", + "Value": "The query failed: {0} Actual columns of the tables in the query: {1}. Rewrite the query using only these columns; do not guess column names." + }, + { + "Id": "834dbec7-04c1-57c7-9044-a9bc1e348828", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Mcp.Error.SelectFailedColumns", + "Value": "Sorgu çalışmadı: {0} Sorgudaki tabloların gerçek kolonları: {1}. Sorguyu yalnızca bu kolonlarla yeniden yaz; kolon adı tahmin etme." + }, { "Id": "aac27480-88f4-5230-9820-f9c7472ef178", "CultureName": "en", @@ -86929,6 +86957,76 @@ "ResourceName": "Platform", "Key": "App.Listform.ListformField.Longitude", "Value": "Boylam" + }, + { + "Id": "f62001da-76fc-59e7-a302-39ca902d471e", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.InternalReference", + "Value": "Internal Reference" + }, + { + "Id": "c7fa6c98-b2fd-563c-a83f-87639440f864", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.InternalReference", + "Value": "İç Referans" + }, + { + "Id": "e5c139da-74ef-51d9-923e-c9d3de7e109b", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.Cost", + "Value": "Cost" + }, + { + "Id": "74014c98-a5f8-53de-b16e-d77c86f987b4", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.Cost", + "Value": "Maliyet" + }, + { + "Id": "de319c46-4357-5025-86c4-1700e71cd1c5", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.SetupTimeMin", + "Value": "Setup Time (min)" + }, + { + "Id": "440df770-7b20-5180-8aff-bd8ef821657d", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.SetupTimeMin", + "Value": "Kurulum Süresi (dk)" + }, + { + "Id": "e8104b4f-8b74-56f7-a96d-3126fca31472", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.CleanupTimeMin", + "Value": "Cleanup Time (min)" + }, + { + "Id": "cf803733-a414-5abd-a659-70e86431f51b", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.CleanupTimeMin", + "Value": "Temizlik Süresi (dk)" + }, + { + "Id": "8c9c9180-00ec-5ca0-9d34-a96c6b6a68df", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.ExpenseAccount", + "Value": "Expense Account" + }, + { + "Id": "415a25cd-c5cf-561b-a827-55259b8108e6", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Listform.ListformField.ExpenseAccount", + "Value": "Gider Hesabı" } ] } diff --git a/ui/src/proxy/admin/wizard/models.ts b/ui/src/proxy/admin/wizard/models.ts index 667fba48..fe42089e 100644 --- a/ui/src/proxy/admin/wizard/models.ts +++ b/ui/src/proxy/admin/wizard/models.ts @@ -234,6 +234,8 @@ export interface WizardDeleteTableDto { usedBy: WizardRecordRefDto[] /** Tabloyla birlikte silinecek seed dosyaları (kapsam klasörüne göre). */ files: string[] + /** Talep silmesinde tabloyu sorgulayan, taleple birlikte silinecek Custom Endpoint'ler. */ + customEndpoints?: string[] } export interface WizardDeleteAnalysisDto { diff --git a/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx b/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx index 588f6f94..2afea85c 100644 --- a/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardFileManager.tsx @@ -596,6 +596,16 @@ const WizardFileManager = () => { {translate('::App.ListForm.WizardDeleteTableNoPermission')} )} + {!!table.customEndpoints?.length && ( +
+ {translate('::App.ListForm.WizardDeleteCustomEndpoints')} + {table.customEndpoints.map((name) => ( +
+ {name} +
+ ))} +
+ )} ) }