EditorScript düzenlemeleri
This commit is contained in:
parent
dea283e7cf
commit
1ede045bac
38 changed files with 837 additions and 447 deletions
49
.github/instructions/lowcode.instructions.md
vendored
49
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -546,7 +546,9 @@ eklenen dosya) bu satırlar ekranla birlikte seed edilir; migrate çalıştığ
|
||||||
|
|
||||||
1. `deploy_wizard` cevabına `SeedData: true` yaz, satırların hepsini aynı çağrıda `seedRows` ile
|
1. `deploy_wizard` cevabına `SeedData: true` yaz, satırların hepsini aynı çağrıda `seedRows` ile
|
||||||
gönder (alan adından değere nesne dizisi). Ekranın `SeedFilePath`'i `data/{ListFormCode}.json`
|
gönder (alan adından değere nesne dizisi). Ekranın `SeedFilePath`'i `data/{ListFormCode}.json`
|
||||||
olur, `SeedSyncInsert/Update/Delete` açılır (§9.15); satırlar deploy anında bu dosyaya yazılır.
|
olur; satırlar deploy anında bu dosyaya yazılır. `SeedSyncInsert/Update/Delete` (§9.15) demo veriyle
|
||||||
|
**açılmaz** — varsayılan `false`'tur; yalnızca istek "ekrandan girilen kayıtlar seed dosyasına
|
||||||
|
yansısın" diyorsa wizard cevabına `SeedSync: true` yazılır (wizard ekranında da aynı kural).
|
||||||
`Guid` anahtarı sunucu üretir; `TenantId` ve audit alanlarını migrate doldurur — yazma. Aynı
|
`Guid` anahtarı sunucu üretir; `TenantId` ve audit alanlarını migrate doldurur — yazma. Aynı
|
||||||
içerikli satır dosyada varsa (tekrar deploy) yeniden eklenmez.
|
içerikli satır dosyada varsa (tekrar deploy) yeniden eklenmez.
|
||||||
2. DB Migrate'i bekleme. Migrate wizard seeder ile ekranı kurar, ardından `ListFormSeedDataApplier`
|
2. DB Migrate'i bekleme. Migrate wizard seeder ile ekranı kurar, ardından `ListFormSeedDataApplier`
|
||||||
|
|
@ -1275,7 +1277,8 @@ atanmaz (admin dahil); ekranı görecek roller yetkiyi rol ekranından alır.
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `IsTenant` / `IsBranch` / `IsOrganizationUnit` | `[ListForm]` Otomatik kırılım filtresi. Tabloda `TenantId` varsa `IsTenant: true` **olmalı**. |
|
| `IsTenant` / `IsBranch` / `IsOrganizationUnit` | `[ListForm]` Otomatik kırılım filtresi. Tabloda `TenantId` varsa `IsTenant: true` **olmalı**. |
|
||||||
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | `[ListForm]` Toolbar ve satır aksiyonları (`EditingOptionDto`). |
|
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | `[ListForm]` Toolbar ve satır aksiyonları (`EditingOptionDto`). |
|
||||||
| `SeedData` | `[ListForm]` `true` ise `SeedFilePath = data/{ListFormCode}.json` ve `SeedSyncInsert/Update/Delete` açık üretilir; ekrandan girilen satır dosyaya yazılır, migrate geri yükler. Wizard ekranında sorulmaz; demo veri taşıyan MCP üretimi için (§0.10.1). Varsayılan `false`. |
|
| `SeedData` | `[ListForm]` `true` ise `SeedFilePath = data/{ListFormCode}.json` üretilir; migrate dosyadaki satırları yükler. Wizard ekranında sorulmaz; demo veri taşıyan MCP üretimi için (§0.10.1). Varsayılan `false`. |
|
||||||
|
| `SeedSync` | `[ListForm]` `SeedData` açıkken `SeedSyncInsert/Update/Delete`'i açar: ekrandan eklenen/değiştirilen/silinen kayıt dosyaya yansır. **Varsayılan `false`**; demo veri bunu açmaz, yalnızca istekte açıkça istenirse `true` yazılır. Okurken üç bayraktan biri açıksa `true`; tek tek ayar `ListForm` bölümünde yapılır. |
|
||||||
| `AllowDetail` | `[ListForm]` Satırda **Detay** düğmesi çıkar; `/admin/form/{ListFormCode}/{anahtar}` adresini yeni sekmede açar — aynı ekranın tek kayıtlık form görünümüdür. `KeyFieldName` boşsa çalışmaz. **`SubForms` doluysa cevaptan bağımsız `true` üretilir** (`ListFormWizardDto.BuildListForm`): wizard alt formları `Form` tipinde kurar ve sekmeler yalnızca Detay görünümünde açılır. Elle yazılan wizard dosyasında da alt formu olan ekranın `EditingOptionDto.AllowDetail` değeri `true` yazılır. **Alt formu olmayan listede `false` kalır** — MCP/AI üretiminde Detay düğmesi yalnızca `SubForms` taşıyan listeye açılır; kullanıcı açıkça istemedikçe tek tablolu liste ya da alt form ekranının kendisi (`PerformancePeriodSkill` gibi) Detay almaz. |
|
| `AllowDetail` | `[ListForm]` Satırda **Detay** düğmesi çıkar; `/admin/form/{ListFormCode}/{anahtar}` adresini yeni sekmede açar — aynı ekranın tek kayıtlık form görünümüdür. `KeyFieldName` boşsa çalışmaz. **`SubForms` doluysa cevaptan bağımsız `true` üretilir** (`ListFormWizardDto.BuildListForm`): wizard alt formları `Form` tipinde kurar ve sekmeler yalnızca Detay görünümünde açılır. Elle yazılan wizard dosyasında da alt formu olan ekranın `EditingOptionDto.AllowDetail` değeri `true` yazılır. **Alt formu olmayan listede `false` kalır** — MCP/AI üretiminde Detay düğmesi yalnızca `SubForms` taşıyan listeye açılır; kullanıcı açıkça istemedikçe tek tablolu liste ya da alt form ekranının kendisi (`PerformancePeriodSkill` gibi) Detay almaz. |
|
||||||
| `DefaultLayout` | `[ListForm]` `"grid"`, `"card"`, `"pivot"`, `"chart"`, `"tree"`, `"gantt"`, `"scheduler"`, `"todo"`. |
|
| `DefaultLayout` | `[ListForm]` `"grid"`, `"card"`, `"pivot"`, `"chart"`, `"tree"`, `"gantt"`, `"scheduler"`, `"todo"`. |
|
||||||
| `Grid` `Card` `Pivot` `Chart` `Tree` `Gantt` `Scheduler` `Todo` | `[ListForm]` Hangi görünümlerin açık olduğu (`LayoutDto`). **Liste ekranında `Grid`/`Card`/`Pivot`/`Chart` varsayılan `true`**, diğer dördü `false` (§9.8). Açtığın her görünümün option bloğunu da doldur. |
|
| `Grid` `Card` `Pivot` `Chart` `Tree` `Gantt` `Scheduler` `Todo` | `[ListForm]` Hangi görünümlerin açık olduğu (`LayoutDto`). **Liste ekranında `Grid`/`Card`/`Pivot`/`Chart` varsayılan `true`**, diğer dördü `false` (§9.8). Açtığın her görünümün option bloğunu da doldur. |
|
||||||
|
|
@ -1876,6 +1879,11 @@ bölümlerine düşer (§4.1). `SeededAt` ilk deploy anında sunucu tarafından
|
||||||
> tutarlı** olmalıdır: açılışta çalışan bir kural varsa (hesap tarifleri varsayılan `both`)
|
> tutarlı** olmalıdır: açılışta çalışan bir kural varsa (hesap tarifleri varsayılan `both`)
|
||||||
> `// @runOnOpen` satırı gelir ve metin literalleri çift tırnaklıdır (`JSON.stringify`). Elle yazmak yerine C# tarafında `EditorScript.Build(...)` kullan ya da
|
> `// @runOnOpen` satırı gelir ve metin literalleri çift tırnaklıdır (`JSON.stringify`). Elle yazmak yerine C# tarafında `EditorScript.Build(...)` kullan ya da
|
||||||
> Script Builder'da üret; uyuşmazsa dialog script'i "elle düzenlenmiş" sayar.
|
> Script Builder'da üret; uyuşmazsa dialog script'i "elle düzenlenmiş" sayar.
|
||||||
|
> **Wizard/MCP deploy'u script'i kurallardan yeniden üretir** (`EditorScript.Rebuild`): `// @builder`
|
||||||
|
> başlıklı script'in gövdesi atılıp kurallardan yazılır; `EditorScript` alanına yalnızca kural JSON'u
|
||||||
|
> (`{"v":1,"rules":[…]}`) da verilebilir. MCP script'i elle yazmaz, kuralları verir — elle yazılan
|
||||||
|
> gövde (yanlış yardımcı kullanımı) çalışmayan script üretiyordu. Başlıksız (tamamen elle yazılmış)
|
||||||
|
> script olduğu gibi kalır.
|
||||||
|
|
||||||
### 4.4.1 Deploy'un ve seeder'ın senin yerine ürettikleri — cevaba yazma
|
### 4.4.1 Deploy'un ve seeder'ın senin yerine ürettikleri — cevaba yazma
|
||||||
|
|
||||||
|
|
@ -2658,7 +2666,7 @@ bağlıdır.
|
||||||
| calc | `days` | start, end, target | `set('T', days('Bas', 'Bit'))` — bitiş günü **dahil** |
|
| calc | `days` | start, end, target | `set('T', days('Bas', 'Bit'))` — bitiş günü **dahil** |
|
||||||
| calc | `hours` | start, end, target | `set('T', hours('Bas', 'Bit'))` — bitiş küçükse ertesi güne taşar |
|
| calc | `hours` | start, end, target | `set('T', hours('Bas', 'Bit'))` — bitiş küçükse ertesi güne taşar |
|
||||||
| data | `copy` | source (seçili kayıttaki kolon/yol), target | `copy('unitPrice', 'UnitPrice')` |
|
| data | `copy` | source (seçili kayıttaki kolon/yol), target | `copy('unitPrice', 'UnitPrice')` |
|
||||||
| data | `setValue` | target, text | `set('T', tpl('{Musteri} - {value}'))` |
|
| data | `setValue` | target, text | `set('T', tpl('{Musteri} - {value}'))`; `text` `true`/`false` ise mantıksal sabit yazılır: `set('T', false)` — onay kutusuna metin `'false'` dolu sayılırdı. Runtime da mevcut değeri mantıksal olan alana gelen `'true'/'false'/'1'/'0'`'ı mantıksala çevirir |
|
||||||
| data | `clear` | sources[] | `clear('Il', 'Ilce')` |
|
| data | `clear` | sources[] | `clear('Il', 'Ilce')` |
|
||||||
| view | `readOnly` | target, invert? | `readOnly('Alan', <koşul>)` · invert → `readOnly('Alan', !(<koşul>))` · koşulsuz → `readOnly('Alan', true)` |
|
| view | `readOnly` | target, invert? | `readOnly('Alan', <koşul>)` · invert → `readOnly('Alan', !(<koşul>))` · koşulsuz → `readOnly('Alan', true)` |
|
||||||
| interaction | `notify` | message | `notify('Limit aşıldı')` — mesajda `{Alan}` şablonu geçerli |
|
| interaction | `notify` | message | `notify('Limit aşıldı')` — mesajda `{Alan}` şablonu geçerli |
|
||||||
|
|
@ -2726,14 +2734,14 @@ Script `AsyncFunction` ile derlenir, aşağıdaki adlar parametre olarak enjekte
|
||||||
| `data` | Script'in üzerinde çalıştığı canlı form verisi (kopya) |
|
| `data` | Script'in üzerinde çalıştığı canlı form verisi (kopya) |
|
||||||
| `isReady` | `true` ise script form açılışında çalışıyor |
|
| `isReady` | `true` ise script form açılışında çalışıyor |
|
||||||
| `get('Alan')` | Form verisinden okur. **Nokta yolu** (`Musteri.Unvan`) ve **büyük/küçük harf duyarsız** eşleşme destekler (SQL kolon adları tutarsız gelebiliyor) |
|
| `get('Alan')` | Form verisinden okur. **Nokta yolu** (`Musteri.Unvan`) ve **büyük/küçük harf duyarsız** eşleşme destekler (SQL kolon adları tutarsız gelebiliyor) |
|
||||||
| `num()` · `str()` · `bool()` | Tip dönüşümü. `bool` için `true`/`'true'`/`1`/`'1'` doğrudur |
|
| `num()` · `str()` · `bool()` | **Alan adı** alır, değeri değil: `str('ProductType')`. `str(get('X'))` ya da `str(value)` değeri alan adı sanıp boş döner — koşul hiç tutmaz. Değişen alanın değeri için `str(field)` ya da doğrudan `value` kullanılır. `bool` için `true`/`'true'`/`1`/`'1'` doğrudur |
|
||||||
| `dateOf('Alan')` | `Date` ya da `null` |
|
| `dateOf('Alan')` | `Date` ya da `null` |
|
||||||
| `pick(nesne, 'a.b')` | Herhangi bir nesneden yol okuma |
|
| `pick(nesne, 'a.b')` | Herhangi bir nesneden yol okuma |
|
||||||
| `set('Alan', deger)` · `set({A:1, B:2})` | Yazar. Değişmeyen değer yazılmaz; yazmalar biriktirilip **tek flush** ile forma ve düzenlenen grid hücresine uygulanır |
|
| `set('Alan', deger)` · `set({A:1, B:2})` | Yazar. Değişmeyen değer yazılmaz; yazmalar biriktirilip **tek flush** ile forma ve düzenlenen grid hücresine uygulanır |
|
||||||
| `clear('A','B')` | Verilen alanları `null` yapar |
|
| `clear('A','B')` | Verilen alanları `null` yapar |
|
||||||
| `copy('kolon','Alan')` | Lookup/GridBox'ta **seçili kayıttan** taşır; hedef verilmezse yolun son parçası hedef olur. Seçili kayıt, olay formdan gelse de (popup form, form sayfası) değişen alanın editöründen okunur; lookup sorgusu taşınacak kolonları döndürmelidir (ör. `SELECT Id AS [Key], …, DepartmentId, JobPositionId`) |
|
| `copy('kolon','Alan')` | Lookup/GridBox'ta **seçili kayıttan** taşır; hedef verilmezse yolun son parçası hedef olur. Seçili kayıt, olay formdan gelse de (popup form, form sayfası) değişen alanın editöründen okunur; lookup sorgusu taşınacak kolonları döndürmelidir (ör. `SELECT Id AS [Key], …, DepartmentId, JobPositionId`) |
|
||||||
| `selected('Yol')` · `snum()` · `sstr()` | Seçili kaydın tamamı / sayı / metin hâli |
|
| `selected('Yol')` · `snum()` · `sstr()` | Seçili kaydın tamamı / sayı / metin hâli |
|
||||||
| `readOnly('Alan', kosul)` | Formdaki **herhangi bir** editörü kilitler/açar (yalnızca script'in bağlı olduğu alanı değil). Grid popup'ında açılış çalışması (`isReady`) editörler her yeniden çizildiğinde tekrarlanır ve form verisi o anda güncel değildir; koşul başka bir alana bakıyorsa değer `editor.component.cellValue(satır, 'Alan')` ile okunur (örnek: `App.Hr.md` › İzin Yönetimi §5.1) |
|
| `readOnly('Alan', kosul)` | Formdaki **herhangi bir** editörü kilitler/açar (yalnızca script'in bağlı olduğu alanı değil). Grid popup'ında açılış çalışması (`isReady`) form her yeniden çizildiğinde (her `cellValue` yazımı) tekrarlanır; veri grid'in düzenlenen satırından güncel okunur (`getEditingRow`), `str('Alan')` yeterlidir |
|
||||||
| `round(x, 2)` · `sum('A','B')` | Matematik |
|
| `round(x, 2)` · `sum('A','B')` | Matematik |
|
||||||
| `days('Bas','Bit')` · `hours('Bas','Bit')` | Gün farkı (bitiş dahil) · saat farkı (negatifse +24) |
|
| `days('Bas','Bit')` · `hours('Bas','Bit')` | Gün farkı (bitiş dahil) · saat farkı (negatifse +24) |
|
||||||
| `tpl('{Alan} - {value}')` | Şablon: `{Alan}`, `{value}`, `{selected.Yol}` |
|
| `tpl('{Alan} - {value}')` | Şablon: `{Alan}`, `{value}`, `{selected.Yol}` |
|
||||||
|
|
@ -2750,6 +2758,37 @@ Eski (uzun) script'ler bozulmasın diye geriye dönük uyum vardır: script içi
|
||||||
(`formData, e, editor, runtimeSetEditorReadOnly, setFormData`) derlenir ve **yardımcılar
|
(`formData, e, editor, runtimeSetEditorReadOnly, setFormData`) derlenir ve **yardımcılar
|
||||||
tanımsız kalır**. Yardımcı adlarını yeniden tanımlama.
|
tanımsız kalır**. Yardımcı adlarını yeniden tanımlama.
|
||||||
|
|
||||||
|
**Zincir ve kalıcı readOnly (Grid/Tree popup).** Script'in `set` ile yazdığı alanın kendi script'i de
|
||||||
|
çalışır — kullanıcı o alanı değiştirmiş gibi (`runChainedScripts`; derinlik sınırı yoktur, zincir yazılan kadar ilerler; `ProductType` →
|
||||||
|
`TrackInventory = false` → `TrackingMethod` salt okunur). Grid'e `cellValue` ile yazılan değer editörün
|
||||||
|
değişiklik olayını tetiklemediği için zincir olmadan bağımlı kural ancak kullanıcı alanı elle değiştirince
|
||||||
|
çalışırdı. Yazma popup formunu yeniden çizer (`editForm.repaint`) ve açılış script'leri yeniden çalışır;
|
||||||
|
formun `formData`'sı açılıştaki nesne olduğundan veri grid'in düzenlenen satırından okunur
|
||||||
|
(`getEditingRow`, `list/shared/editingForm.ts` — yeni satırın anahtarı nesnedir, `row.key ===` eşleşmez).
|
||||||
|
Eski veriyle çalışan script kendi `readOnly` kararını geri alırdı. Popup formu
|
||||||
|
`getController('editing')._editForm`'dadır (`getActiveEditingForm`). Form sayfası `updateData` ile alan
|
||||||
|
olayını zaten tetikler.
|
||||||
|
|
||||||
|
Zincirde bir alanın aynı değerle ikinci kez tetiklenmesi döngüdür (A → B → A) ve durdurulur; değeri her turda
|
||||||
|
değişen döngüyü 1000 adımlık emniyet sınırı yakalar. İkisi de kullanıcıya hata olarak gösterilir.
|
||||||
|
|
||||||
|
**Hatalı script kullanıcıya gösterilir** — derleme ya da çalışma hatası konsola düşmekle kalmaz, ekranda
|
||||||
|
`App.EditorScript.Error` bildirimi çıkar (alan adı + hata; `reportEditorScriptError`,
|
||||||
|
`utils/editorScriptRuntime.ts`). Form her yeniden çizildiğinde aynı hata 5 saniyede bir kez gösterilir.
|
||||||
|
|
||||||
|
**Derlenemeyen script hiç çalışmaz** — bir satırdaki söz dizimi hatası (eksik parantez) diğer kuralları da
|
||||||
|
durdurur. Script Builder kaydetmeden önce metni derler ve hatada kaydetmez (`App.ScriptBuilder.SyntaxError`).
|
||||||
|
|
||||||
|
**Script Builder ile MCP aynı kodu üretir.** Kod `// @builder` başlığındaki kurallardan üretilir; MCP/wizard
|
||||||
|
deploy'u gövdeyi atıp kuralları `EditorScript.Rebuild` ile yeniden üretir (C# çıktısı `scriptRecipes.ts` ile
|
||||||
|
birebir). Onay kutusu/anahtar alanına değer yazan `setValue` kuralında dialog serbest metin yerine Evet/Hayır
|
||||||
|
sectirir (`getParamChoices`); deploy aynı alana gelen `0`/`1`/`evet`/`hayır` değerini `true`/`false`'a çevirir.
|
||||||
|
Kod editöründe kurallarla eşleşmeyecek şekilde elle değiştirilen script'te dialog uyarır
|
||||||
|
(`App.ScriptBuilder.StaleHeader`) ve kaydederken bayat başlığı siler; başlık kalsaydı deploy gövdeyi başlıktan
|
||||||
|
yeniden üretip elle yapılan düzeltmeyi geri alırdı.
|
||||||
|
`readOnly` tarifinde `invert: "true"` koşulu tersler: "koşul sağlanmıyorsa salt okunur". "X seçilince kilitle"
|
||||||
|
için `invert` boş bırakılır.
|
||||||
|
|
||||||
### 5.7 Yetenek sınırları — script ile yapılamayanlar
|
### 5.7 Yetenek sınırları — script ile yapılamayanlar
|
||||||
|
|
||||||
- **Alan gizleme yok.** Runtime'da `visible` yardımcısı yoktur; koşullu görünürlük yerine
|
- **Alan gizleme yok.** Runtime'da `visible` yardımcısı yoktur; koşullu görünürlük yerine
|
||||||
|
|
|
||||||
|
|
@ -1563,7 +1563,7 @@ liste süzgeci ve çağrı kapısı aynı kuralı kullanır.
|
||||||
| `save_sql_object` | View/procedure/function scriptini `sql/object/{Ad}.sql` ve `postgres/object/{Ad}.sql` dosyalarına yazar (`McpSqlObjectWriter`); veritabanında çalıştırılmaz, nesneyi DB Migrate kurar. Gövde diyalektler arasında güvenle çevrilemediği için iki script de istemciden gelir ve aynı nesneyi tanımlamalıdır (`SqlObjectInvalid`); ad scriptten okunur, başlık `CREATE OR ALTER` / `CREATE OR REPLACE`'e çevrilir. Tablo scriptinin üstüne yazmaz. `.Write` + `App.SqlQueryManager` ister. |
|
| `save_sql_object` | View/procedure/function scriptini `sql/object/{Ad}.sql` ve `postgres/object/{Ad}.sql` dosyalarına yazar (`McpSqlObjectWriter`); veritabanında çalıştırılmaz, nesneyi DB Migrate kurar. Gövde diyalektler arasında güvenle çevrilemediği için iki script de istemciden gelir ve aynı nesneyi tanımlamalıdır (`SqlObjectInvalid`); ad scriptten okunur, başlık `CREATE OR ALTER` / `CREATE OR REPLACE`'e çevrilir. Tablo scriptinin üstüne yazmaz. `.Write` + `App.SqlQueryManager` ister. |
|
||||||
| `generate_crud_endpoints` | SQL Query Manager CRUD diyaloğuyla aynı endpoint kümesini (`CrudEndpointSeedFileDto.Generate` — diyaloğun servisi de bunu kullanır) yalnızca `crud/{Entity}.json` dosyasına yazar; kayıtları DB Migrate ekler (`CrudEndpointSeedApplier`). `.Write` + `App.SqlQueryManager.CrudEndpoints` ister. |
|
| `generate_crud_endpoints` | SQL Query Manager CRUD diyaloğuyla aynı endpoint kümesini (`CrudEndpointSeedFileDto.Generate` — diyaloğun servisi de bunu kullanır) yalnızca `crud/{Entity}.json` dosyasına yazar; kayıtları DB Migrate ekler (`CrudEndpointSeedApplier`). `.Write` + `App.SqlQueryManager.CrudEndpoints` ister. |
|
||||||
| `list_wizards` / `get_wizard` | Wizard File Manager dosyaları; `get_wizard` seed dosyasını diskteki biçimiyle döndürür. |
|
| `list_wizards` / `get_wizard` | Wizard File Manager dosyaları; `get_wizard` seed dosyasını diskteki biçimiyle döndürür. |
|
||||||
| `deploy_wizard` | Wizard'ı Wizard ekranıyla aynı yoldan (`IListFormWizardAppService.Create`) deploy eder. Girdi seed dosyası biçimi (`{"Wizard": …, "ListForm": …, "Fields": […]}`) ya da düz cevaplardır; ikisi de `WizardSeedFileDto.FromDeployJson`'dan geçer: Wizard bölümünde yazılan cevap korunur, yazılmayan ekran alanı `ListForm`/`Fields`'tan tamamlanır, iki bölüm de ekrana katman olarak uygulanır (`WizardScreenOverlay`, üç yönlü: bölümdeki değer wizard'ın kendi ürettiğiyle aynıysa cevap kazanır, farklıysa editör ayarıdır ve korunur; kimlik alanları wizard'dan gelir, tanınmayan/hesaplanan anahtar reddedilir). Böylece MCP, ListForm editörünün yapabildiği her ayarı wizard dosyasına yazar ve `replaceFileName` ile güncelleme editör ayarlarını silmez. Yeni kaydın form varsayılanı kolonun tablodaki sabit `DEFAULT`'udur (katalog ya da seed scripti, `SqlSeedColumn.ConstantValue`; IsActive → true, UomId → Adet, para birimi → TRY tablonun `DEFAULT`'larından gelir); alana özel değer öğenin `DefaultValue`'su ile verilir (`lowcode.instructions.md` §4.3.5.1). Girdide sözleşmede olmayan anahtar adıyla reddedilir, sessizce yok sayılmaz. `StaticData` lookup seçeneklerinin düz metni `App.StaticLookup.*` dil anahtarına çevrilir ve EN + TR satırı yazılır (`LocalizeStaticLookupsAsync`, `lowcode.instructions.md` §4.3.4.1). Dosyaya kullanıcının etkin talebi `DevRequest` olarak yazılır (sunucu doldurur). Wizard ekranı gibi yalnızca seed dosyalarını yazar; ekran DB Migrate ile kurulur ve çıkıştaki `isPending` o zamana kadar `true` kalır (model migrate'i beklemez; `verify_wizard` bekleyen dosyada `install` kontrolünde durur). `replaceFileName` doluysa güncellemedir (dosyalar yeniden yazılır, önceki kayıtları migrate kaldırır). İstek demo veri taşıyorsa (prompt'taki satırlar, ekran görüntüsü) wizard `SeedData: true` ile deploy edilir — ekranın `SeedFilePath`'i `data/{ListFormCode}.json` olur ve `SeedSync*` bayrakları açılır — satırlar aynı çağrıda `seedRows` ile bu dosyaya yazılır (alan adları doğrulanır, `Guid` anahtarı sunucu üretir); migrate ekranı kurduktan sonra satırları ekler (`lowcode.instructions.md` §0.10.1). Widget sorgularındaki her dil anahtarı tanımlı olmalı ya da wizard'ın `Language.Texts` alanında EN + TR verilmeli, yoksa deploy adlarıyla reddeder (`ValidateWidgetLanguageKeysAsync`). `uploadUrl`'süz `dxImageUpload` alanı base64 sakladığı için kolonu sınırlı boyluysa (katalogdaki karakter sınırı) deploy reddedilir; tablo kolonuna bağlı `dxImageViewer` alanı da reddedilir — görsel kolonunun editörü her zaman `dxImageUpload`'dır (`ValidateImageColumnLengths`). `MenuOrder` yok sayılır: yeni ekran kardeşlerinin sonuna, güncelleme mevcut yerine yazılır — lookup kaynağı ekran önce deploy edildiği için onu kullanan ekranın üstünde kalır. |
|
| `deploy_wizard` | Wizard'ı Wizard ekranıyla aynı yoldan (`IListFormWizardAppService.Create`) deploy eder. Girdi seed dosyası biçimi (`{"Wizard": …, "ListForm": …, "Fields": […]}`) ya da düz cevaplardır; ikisi de `WizardSeedFileDto.FromDeployJson`'dan geçer: Wizard bölümünde yazılan cevap korunur, yazılmayan ekran alanı `ListForm`/`Fields`'tan tamamlanır, iki bölüm de ekrana katman olarak uygulanır (`WizardScreenOverlay`, üç yönlü: bölümdeki değer wizard'ın kendi ürettiğiyle aynıysa cevap kazanır, farklıysa editör ayarıdır ve korunur; kimlik alanları wizard'dan gelir, tanınmayan/hesaplanan anahtar reddedilir). Böylece MCP, ListForm editörünün yapabildiği her ayarı wizard dosyasına yazar ve `replaceFileName` ile güncelleme editör ayarlarını silmez. Yeni kaydın form varsayılanı kolonun tablodaki sabit `DEFAULT`'udur (katalog ya da seed scripti, `SqlSeedColumn.ConstantValue`; IsActive → true, UomId → Adet, para birimi → TRY tablonun `DEFAULT`'larından gelir); alana özel değer öğenin `DefaultValue`'su ile verilir (`lowcode.instructions.md` §4.3.5.1). Girdide sözleşmede olmayan anahtar adıyla reddedilir, sessizce yok sayılmaz. `StaticData` lookup seçeneklerinin düz metni `App.StaticLookup.*` dil anahtarına çevrilir ve EN + TR satırı yazılır (`LocalizeStaticLookupsAsync`, `lowcode.instructions.md` §4.3.4.1). Dosyaya kullanıcının etkin talebi `DevRequest` olarak yazılır (sunucu doldurur). Wizard ekranı gibi yalnızca seed dosyalarını yazar; ekran DB Migrate ile kurulur ve çıkıştaki `isPending` o zamana kadar `true` kalır (model migrate'i beklemez; `verify_wizard` bekleyen dosyada `install` kontrolünde durur). `replaceFileName` doluysa güncellemedir (dosyalar yeniden yazılır, önceki kayıtları migrate kaldırır). İstek demo veri taşıyorsa (prompt'taki satırlar, ekran görüntüsü) wizard `SeedData: true` ile deploy edilir — ekranın `SeedFilePath`'i `data/{ListFormCode}.json` olur (`SeedSync*` bayrakları demo veriyle açılmaz; yalnızca istekte açıkça istenirse wizard'ın `SeedSync: true` cevabıyla) — satırlar aynı çağrıda `seedRows` ile bu dosyaya yazılır (alan adları doğrulanır, `Guid` anahtarı sunucu üretir); migrate ekranı kurduktan sonra satırları ekler (`lowcode.instructions.md` §0.10.1). Widget sorgularındaki her dil anahtarı tanımlı olmalı ya da wizard'ın `Language.Texts` alanında EN + TR verilmeli, yoksa deploy adlarıyla reddeder (`ValidateWidgetLanguageKeysAsync`). `uploadUrl`'süz `dxImageUpload` alanı base64 sakladığı için kolonu sınırlı boyluysa (katalogdaki karakter sınırı) deploy reddedilir; tablo kolonuna bağlı `dxImageViewer` alanı da reddedilir — görsel kolonunun editörü her zaman `dxImageUpload`'dır (`ValidateImageColumnLengths`). `MenuOrder` yok sayılır: yeni ekran kardeşlerinin sonuna, güncelleme mevcut yerine yazılır — lookup kaynağı ekran önce deploy edildiği için onu kullanan ekranın üstünde kalır. |
|
||||||
| `delete_wizard` | Wizard Manager'daki silmeyle aynı: `InsertedRecords` + ekranın veri aynası, bağımlı wizard varsa reddedilir. `dropTables: true` ekranın tablosunu (yalnızca tasarımcı/`create_table` ile kurulmuş ve başka yerde kullanılmayan) CRUD endpoint'leri, `sql/object` ve `crud` dosyalarıyla düşürür; model bunu kullanıcı onayı olmadan göndermez (`WizardTableCleaner`, `lowcode.instructions.md` §4.5). |
|
| `delete_wizard` | Wizard Manager'daki silmeyle aynı: `InsertedRecords` + ekranın veri aynası, bağımlı wizard varsa reddedilir. `dropTables: true` ekranın tablosunu (yalnızca tasarımcı/`create_table` ile kurulmuş ve başka yerde kullanılmayan) CRUD endpoint'leri, `sql/object` ve `crud` dosyalarıyla düşürür; model bunu kullanıcı onayı olmadan göndermez (`WizardTableCleaner`, `lowcode.instructions.md` §4.5). |
|
||||||
| `verify_wizard` | Deploy edilmiş ekranı veri yazmadan doğrular: menü kaydı ve URL'si, menünün istediği yetki tanımı (`AbpPermissions`), List yolunda ekran sorgusu (`GetSelectAsync`, tek satır, çağıranın yetkisiyle), Custom yolunda bileşen (etkin, rota = menü URL'si) ve seed kapanışı (`ExportAnalysis` uyarıları; engellemez). İsteğe bağlıdır, migrate sonrasında kullanılır; `App.DevRequests` döngüsünün adımı değildir — döngü `Testing`'de biter, kullanıcı elle test edip `Staging`'e alır (`lowcode.instructions.md` §0.11). |
|
| `verify_wizard` | Deploy edilmiş ekranı veri yazmadan doğrular: menü kaydı ve URL'si, menünün istediği yetki tanımı (`AbpPermissions`), List yolunda ekran sorgusu (`GetSelectAsync`, tek satır, çağıranın yetkisiyle), Custom yolunda bileşen (etkin, rota = menü URL'si) ve seed kapanışı (`ExportAnalysis` uyarıları; engellemez). İsteğe bağlıdır, migrate sonrasında kullanılır; `App.DevRequests` döngüsünün adımı değildir — döngü `Testing`'de biter, kullanıcı elle test edip `Staging`'e alır (`lowcode.instructions.md` §0.11). |
|
||||||
| `start_dev_request` | Geliştirme talebini Id ya da tam başlıkla üstlenir: yalnızca `Ready to Start` durumundakini `In Progress` durumuna alır, diğerlerini reddeder; talebin metnini döndürür. Açıklamaya yapıştırılmış base64 görseller (`data:image/png|jpeg|gif|webp`) metinden ayıklanır (`McpDevRequestImages`), yerlerinde `[image-N]` kalır ve her biri ayrı MCP resim içeriği (`ImageContentBlock`) olarak döner — model görseli resim olarak görür, görsel hiçbir yere yüklenmez. Sonuç istemcinin 1 MB sınırına sığdırılır: büyük görseller küçültülüp JPEG'e çevrilir (`IMcpImageShrinker`), sığmayan gönderilmez ve metinde bildirilir. `.Write` + `App.DevRequests.Update` ister. |
|
| `start_dev_request` | Geliştirme talebini Id ya da tam başlıkla üstlenir: yalnızca `Ready to Start` durumundakini `In Progress` durumuna alır, diğerlerini reddeder; talebin metnini döndürür. Açıklamaya yapıştırılmış base64 görseller (`data:image/png|jpeg|gif|webp`) metinden ayıklanır (`McpDevRequestImages`), yerlerinde `[image-N]` kalır ve her biri ayrı MCP resim içeriği (`ImageContentBlock`) olarak döner — model görseli resim olarak görür, görsel hiçbir yere yüklenmez. Sonuç istemcinin 1 MB sınırına sığdırılır: büyük görseller küçültülüp JPEG'e çevrilir (`IMcpImageShrinker`), sığmayan gönderilmez ve metinde bildirilir. `.Write` + `App.DevRequests.Update` ister. |
|
||||||
|
|
|
||||||
|
|
@ -950,9 +950,9 @@ public class McpDevelopAppService(
|
||||||
return (string.IsNullOrWhiteSpace(json) ? null : WizardSeedFileDto.FromDeployJson(json))
|
return (string.IsNullOrWhiteSpace(json) ? null : WizardSeedFileDto.FromDeployJson(json))
|
||||||
?? throw new UserFriendlyException(L[McpErrorCodes.InvalidWizard]);
|
?? throw new UserFriendlyException(L[McpErrorCodes.InvalidWizard]);
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException(L[McpErrorCodes.InvalidWizardJson, ex.Message]);
|
throw new UserFriendlyException(L[McpErrorCodes.InvalidWizard]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ public sealed class McpDevelopTools(IMcpDevelopAppService developAppService, IMc
|
||||||
=> await developAppService.GetWizardAsync(fileName);
|
=> await developAppService.GetWizardAsync(fileName);
|
||||||
|
|
||||||
[McpServerTool(Name = McpToolNames.DeployWizard, Destructive = true, Idempotent = false)]
|
[McpServerTool(Name = McpToolNames.DeployWizard, Destructive = true, Idempotent = false)]
|
||||||
[Description("Wizard'i deploy eder: ListForm ekrani (ComponentKind 0) ya da bir custom component'i menuye baglayan kayit (ComponentKind 1), yetkileri, dil anahtarlari ve menusu seed dosyalarina yazilir. Deploy veritabanina yazmaz: ekran, menu ve yetkiler kullanici Wizard Manager'da DB Migrate calistirinca olusur; cikistaki isPending true iken ekran henuz yoktur; migrate'i bekleme, isini bitir ve kullaniciya yalnizca DB Migrate calistirmasini soyle. Eksik modul, ust menu, izin grubu, yetki ve dil satirlari da deploy'da dosyalara yazilir ve migrate onlari dogru sirayla ekler; bunlarin veritabaninda olmamasi beklenen durumdur, uyari olarak raporlama ve kullanicidan elle eklemesini isteme. Ust menu zinciri istenen derinlikte kurulur: MenuParentCode ekranin dogrudan ust menusu, Menu.MenuParentPath onun ustundeki seviyeler (kokten asagi, her biri {Code, Icon, En, Tr}); eksik her seviye bir ustunun altina kurulur, var olana baglanilir. Liste ekraninda Grid, Card, Pivot ve Chart gorunumleri varsayilan aciktir (DefaultLayout grid); kullanici bir gorunumu istemedigini soylemedikce bu bayraklari false yazma. Hedef tablo onceden var olmalidir; yoksa once create_table ile olustur. Istek ornek/demo veri tasiyorsa (prompt'taki tablo/liste ya da ekran goruntusunde gorunen satirlar) SeedData true yaz ve satirlarin hepsini ayni cagrida seedRows ile gonder: satirlar ekranin data/{ListFormCode}.json dosyasina yazilir, migrate ekrani kurduktan sonra ekler. Satirlar icin migrate'i bekleyip insert_row kullanma. replaceFileName verilirse o wizard'in dosyalari yeniden yazilir, onceki kayitlarin yerini migrate'te yenileri alir. Wizard cevaplarinin uretmedigi ekran ayarlari (FormFieldsDefaultValueDto/InsertFieldsDefaultValueDto gibi varsayilan degerler, ValidationRuleDto, Format, Alignment, Width, Visible, BandName, ozet/gruplama, ExtraFilter, Insert/Update/Delete komutlari, PageSize, CommandColumnDto …) ayni JSON'un ListForm ve Fields bolumlerine get_wizard ciktisindaki adlarla yazilir. Yeni kaydin form degeri (Aktif = true, Birim = Adet, Kur = TRY, Sure = 60) ayri yazilmaz: deploy kolonun tablodaki sabit DEFAULT degerini okur; tablodan farkli deger gerekiyorsa alan ogesinde DefaultValue yaz (Groups[].Items[].DefaultValue). StaticData lookup seceneklerinde (RadioGroup, SelectBox, TagBox) name dil anahtaridir (App.StaticLookup.{Key}); EN + TR metni bilinen secenegi Language.Texts'te ver, duz metin yazilirsa deploy onu anahtara cevirip TR'yi metinden yazar (get_dev_guide 4.3.4.1). Wizard girdisinde taninmayan anahtar adiyla reddedilir, baska adla tekrar deneme; bolumler kismi olabilir (yalnizca degisen anahtarlar), Fields ogeleri FieldName ile eslesir, *Json adlari (FormFieldsDefaultValueJson) *Dto karsiligina cevrilir. Guncellemede get_wizard ciktisini butunuyle degistirip gonder: tasarimci ayarlari korunur; Wizard bolumunde yazdigin cevap ayni ayarin ListForm'daki eski degerine ustun gelir. Kimlik alanlari (ListFormCode, Name, Title, Description, PermissionDto, FieldName) wizard'dan gelir. Fields'taki her alan Wizard Groups'ta da bulunmalidir; tanınmayan ya da hesaplanan bir anahtar reddedilir. Liste ekraninda DescTr ve DescEn ayrintili olmali (en az 3 satir, 150 karakter): ekran basligindaki bilgi simgesinde gosterilir; ne ise yaradigi, menude nerede ya da hangi ekranin sekmesi oldugu, ana alanlar, otomatik hesaplar ve iliskili ekranlar yazilir (get_dev_guide 4.4.2); kisa aciklama reddedilir. Deploy modulun dokumanini (docs/modules) acar ya da envanterini yeniler; elle yazilan bolumleri save_module_doc ile doldur. Ne yazacagini get_dev_guide 4. bolumunden ogren.")]
|
[Description("Wizard'i deploy eder: ListForm ekrani (ComponentKind 0) ya da bir custom component'i menuye baglayan kayit (ComponentKind 1), yetkileri, dil anahtarlari ve menusu seed dosyalarina yazilir. Deploy veritabanina yazmaz: ekran, menu ve yetkiler kullanici Wizard Manager'da DB Migrate calistirinca olusur; cikistaki isPending true iken ekran henuz yoktur; migrate'i bekleme, isini bitir ve kullaniciya yalnizca DB Migrate calistirmasini soyle. Eksik modul, ust menu, izin grubu, yetki ve dil satirlari da deploy'da dosyalara yazilir ve migrate onlari dogru sirayla ekler; bunlarin veritabaninda olmamasi beklenen durumdur, uyari olarak raporlama ve kullanicidan elle eklemesini isteme. Ust menu zinciri istenen derinlikte kurulur: MenuParentCode ekranin dogrudan ust menusu, Menu.MenuParentPath onun ustundeki seviyeler (kokten asagi, her biri {Code, Icon, En, Tr}); eksik her seviye bir ustunun altina kurulur, var olana baglanilir. Liste ekraninda Grid, Card, Pivot ve Chart gorunumleri varsayilan aciktir (DefaultLayout grid); kullanici bir gorunumu istemedigini soylemedikce bu bayraklari false yazma. Hedef tablo onceden var olmalidir; yoksa once create_table ile olustur. Istek ornek/demo veri tasiyorsa (prompt'taki tablo/liste ya da ekran goruntusunde gorunen satirlar) SeedData true yaz ve satirlarin hepsini ayni cagrida seedRows ile gonder: satirlar ekranin data/{ListFormCode}.json dosyasina yazilir, migrate ekrani kurduktan sonra ekler. SeedData ekrandan girilen kayitlari dosyaya yansitmaz (SeedSyncInsert/Update/Delete kapali); bunu yalnizca istek acikca isterse SeedSync true yaz. Satirlar icin migrate'i bekleyip insert_row kullanma. replaceFileName verilirse o wizard'in dosyalari yeniden yazilir, onceki kayitlarin yerini migrate'te yenileri alir. Wizard cevaplarinin uretmedigi ekran ayarlari (FormFieldsDefaultValueDto/InsertFieldsDefaultValueDto gibi varsayilan degerler, ValidationRuleDto, Format, Alignment, Width, Visible, BandName, ozet/gruplama, ExtraFilter, Insert/Update/Delete komutlari, PageSize, CommandColumnDto …) ayni JSON'un ListForm ve Fields bolumlerine get_wizard ciktisindaki adlarla yazilir. Yeni kaydin form degeri (Aktif = true, Birim = Adet, Kur = TRY, Sure = 60) ayri yazilmaz: deploy kolonun tablodaki sabit DEFAULT degerini okur; tablodan farkli deger gerekiyorsa alan ogesinde DefaultValue yaz (Groups[].Items[].DefaultValue). StaticData lookup seceneklerinde (RadioGroup, SelectBox, TagBox) name dil anahtaridir (App.StaticLookup.{Key}); EN + TR metni bilinen secenegi Language.Texts'te ver, duz metin yazilirsa deploy onu anahtara cevirip TR'yi metinden yazar (get_dev_guide 4.3.4.1). EditorScript'i elle yazma: Script Builder kurallarini ({v:1, rules:[...]}, get_dev_guide 5.3) ver, deploy kodu Script Builder dialogu ile birebir ayni uretir; govde yazsan da atilir. Onay kutusu alanina setValue text 'true'/'false' (0/1 cevrilir). set ile yazilan alanin kurallari da sirayla calisir (derinlik siniri yok); A->B->A dongusu kuran kural yazma, calisma aninda hata olarak gosterilir. Wizard girdisinde taninmayan anahtar adiyla reddedilir, baska adla tekrar deneme; bolumler kismi olabilir (yalnizca degisen anahtarlar), Fields ogeleri FieldName ile eslesir, *Json adlari (FormFieldsDefaultValueJson) *Dto karsiligina cevrilir. Guncellemede get_wizard ciktisini butunuyle degistirip gonder: tasarimci ayarlari korunur; Wizard bolumunde yazdigin cevap ayni ayarin ListForm'daki eski degerine ustun gelir. Kimlik alanlari (ListFormCode, Name, Title, Description, PermissionDto, FieldName) wizard'dan gelir. Fields'taki her alan Wizard Groups'ta da bulunmalidir; tanınmayan ya da hesaplanan bir anahtar reddedilir. Liste ekraninda DescTr ve DescEn ayrintili olmali (en az 3 satir, 150 karakter): ekran basligindaki bilgi simgesinde gosterilir; ne ise yaradigi, menude nerede ya da hangi ekranin sekmesi oldugu, ana alanlar, otomatik hesaplar ve iliskili ekranlar yazilir (get_dev_guide 4.4.2); kisa aciklama reddedilir. Deploy modulun dokumanini (docs/modules) acar ya da envanterini yeniler; elle yazilan bolumleri save_module_doc ile doldur. Ne yazacagini get_dev_guide 4. bolumunden ogren.")]
|
||||||
public async Task<WizardFileInfoDto?> DeployWizardAsync(
|
public async Task<WizardFileInfoDto?> DeployWizardAsync(
|
||||||
[Description("Wizard JSON'u: seed dosyasi bicimi ({\"Wizard\": {...}, \"ListForm\": {...}, \"Fields\": [...]}, get_wizard cikisi gibi) ya da duz wizard cevaplari. ListForm/Fields istege baglidir ve kismi olabilir. Enum alanlari sayi olarak yazilir.")]
|
[Description("Wizard JSON'u: seed dosyasi bicimi ({\"Wizard\": {...}, \"ListForm\": {...}, \"Fields\": [...]}, get_wizard cikisi gibi) ya da duz wizard cevaplari. ListForm/Fields istege baglidir ve kismi olabilir. Enum alanlari sayi olarak yazilir.")]
|
||||||
string wizard,
|
string wizard,
|
||||||
|
|
|
||||||
|
|
@ -100,12 +100,19 @@ public class ListFormWizardDto
|
||||||
public bool ConfirmDelete { get; set; }
|
public bool ConfirmDelete { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ekranin verisi seed klasorundeki <c>data/{ListFormCode}.json</c> dosyasina aynalanir
|
/// Ekranin demo verisi seed klasorundeki <c>data/{ListFormCode}.json</c> dosyasindan migrate ile
|
||||||
/// (<c>SeedFilePath</c> + ekle/guncelle/sil senkronizasyonu). Istekle gelen ornek/demo satirlar
|
/// yuklenir (<c>SeedFilePath</c>). Ekrandan yapilan kayitlarin dosyaya yansimasi ayri karardir
|
||||||
/// ekrandan girildiginde dosyaya yazilir ve migrate ile geri yuklenir.
|
/// (<see cref="SeedSync"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SeedData { get; set; }
|
public bool SeedData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ekrandan yapilan ekle/guncelle/sil islemleri veri dosyasina yansisin mi (<c>SeedSyncInsert/Update/Delete</c>).
|
||||||
|
/// Demo veri (<see cref="SeedData"/>) bunu acmaz: varsayilan kapali, yalnizca istekte acikca
|
||||||
|
/// istenirse true yazilir; aksi halde kullanicinin girdigi her kayit seed dosyasina karisirdi.
|
||||||
|
/// </summary>
|
||||||
|
public bool SeedSync { get; set; }
|
||||||
|
|
||||||
// Liste ekranı varsayılan olarak dört analiz görünümüyle gelir (UI wizard ve LayoutDto ile aynı);
|
// Liste ekranı varsayılan olarak dört analiz görünümüyle gelir (UI wizard ve LayoutDto ile aynı);
|
||||||
// MCP/prompt ile gelen cevapta bayrak yazılmazsa görünüm sessizce kapanmasın diye.
|
// MCP/prompt ile gelen cevapta bayrak yazılmazsa görünüm sessizce kapanmasın diye.
|
||||||
public string DefaultLayout { get; set; } = "grid";
|
public string DefaultLayout { get; set; } = "grid";
|
||||||
|
|
@ -279,9 +286,9 @@ public class ListFormWizardDto
|
||||||
// Query'de SelectCommand tablo adi degildir; silme komutu elle yazilir.
|
// Query'de SelectCommand tablo adi degildir; silme komutu elle yazilir.
|
||||||
DeleteCommand = isDeletedField && SelectCommandType != SelectCommandTypeEnum.Query ? WizardConsts.DefaultDeleteCommand(SelectCommand) : null,
|
DeleteCommand = isDeletedField && SelectCommandType != SelectCommandTypeEnum.Query ? WizardConsts.DefaultDeleteCommand(SelectCommand) : null,
|
||||||
SeedFilePath = SeedData ? WizardConsts.SeedDataFilePath(listFormCode) : null,
|
SeedFilePath = SeedData ? WizardConsts.SeedDataFilePath(listFormCode) : null,
|
||||||
SeedSyncInsert = SeedData,
|
SeedSyncInsert = SeedData && SeedSync,
|
||||||
SeedSyncUpdate = SeedData,
|
SeedSyncUpdate = SeedData && SeedSync,
|
||||||
SeedSyncDelete = SeedData,
|
SeedSyncDelete = SeedData && SeedSync,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Varsayilanlar WizardConsts uzerinden JSON olarak kurulur; DTO'nun nesne karsiliklari
|
// Varsayilanlar WizardConsts uzerinden JSON olarak kurulur; DTO'nun nesne karsiliklari
|
||||||
|
|
@ -450,6 +457,7 @@ public class ListFormWizardDto
|
||||||
IsBranch = listForm.IsBranch;
|
IsBranch = listForm.IsBranch;
|
||||||
IsOrganizationUnit = listForm.IsOrganizationUnit;
|
IsOrganizationUnit = listForm.IsOrganizationUnit;
|
||||||
SeedData = !string.IsNullOrWhiteSpace(listForm.SeedFilePath);
|
SeedData = !string.IsNullOrWhiteSpace(listForm.SeedFilePath);
|
||||||
|
SeedSync = listForm.SeedSyncInsert || listForm.SeedSyncUpdate || listForm.SeedSyncDelete;
|
||||||
|
|
||||||
var layout = listForm.LayoutDto;
|
var layout = listForm.LayoutDto;
|
||||||
DefaultLayout = layout.DefaultLayout;
|
DefaultLayout = layout.DefaultLayout;
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ public class WizardSeedFileDto
|
||||||
[
|
[
|
||||||
nameof(ListFormWizardDto.IsTenant), nameof(ListFormWizardDto.IsBranch), nameof(ListFormWizardDto.IsOrganizationUnit),
|
nameof(ListFormWizardDto.IsTenant), nameof(ListFormWizardDto.IsBranch), nameof(ListFormWizardDto.IsOrganizationUnit),
|
||||||
nameof(ListFormWizardDto.AllowAdding), nameof(ListFormWizardDto.AllowUpdating), nameof(ListFormWizardDto.AllowDeleting),
|
nameof(ListFormWizardDto.AllowAdding), nameof(ListFormWizardDto.AllowUpdating), nameof(ListFormWizardDto.AllowDeleting),
|
||||||
nameof(ListFormWizardDto.AllowDetail), nameof(ListFormWizardDto.ConfirmDelete), nameof(ListFormWizardDto.SeedData),
|
nameof(ListFormWizardDto.AllowDetail), nameof(ListFormWizardDto.ConfirmDelete), nameof(ListFormWizardDto.SeedData), nameof(ListFormWizardDto.SeedSync),
|
||||||
nameof(ListFormWizardDto.DefaultLayout), nameof(ListFormWizardDto.Grid), nameof(ListFormWizardDto.Card),
|
nameof(ListFormWizardDto.DefaultLayout), nameof(ListFormWizardDto.Grid), nameof(ListFormWizardDto.Card),
|
||||||
nameof(ListFormWizardDto.Pivot), nameof(ListFormWizardDto.Tree), nameof(ListFormWizardDto.Chart),
|
nameof(ListFormWizardDto.Pivot), nameof(ListFormWizardDto.Tree), nameof(ListFormWizardDto.Chart),
|
||||||
nameof(ListFormWizardDto.Gantt), nameof(ListFormWizardDto.Scheduler), nameof(ListFormWizardDto.Todo),
|
nameof(ListFormWizardDto.Gantt), nameof(ListFormWizardDto.Scheduler), nameof(ListFormWizardDto.Todo),
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,15 @@ public class ListFormWizardAppService(
|
||||||
ValidateImageColumnLengths(input, tableColumns);
|
ValidateImageColumnLengths(input, tableColumns);
|
||||||
await LocalizeStaticLookupsAsync(input, fieldDtos, rows, owned, inserted);
|
await LocalizeStaticLookupsAsync(input, fieldDtos, rows, owned, inserted);
|
||||||
|
|
||||||
|
// Script Builder kurali tasiyan script'in kodu kurallardan uretilir; MCP'nin elle yazdigi
|
||||||
|
// govdeye guvenilmez (lowcode §4.4, §5.6).
|
||||||
|
var booleanFields = fieldDtos
|
||||||
|
.Where(f => f.SourceDbType == System.Data.DbType.Boolean || f.EditorType2 is "dxCheckBox" or "dxSwitch")
|
||||||
|
.Select(f => f.FieldName)
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var field in fieldDtos)
|
||||||
|
field.EditorScript = Sozsoft.Platform.Editors.EditorScript.Rebuild(field.EditorScript, booleanFields.Contains);
|
||||||
|
|
||||||
foreach (var item in input.Groups.SelectMany(g => g.Items))
|
foreach (var item in input.Groups.SelectMany(g => g.Items))
|
||||||
await WriteLanguageRowsAsync(rows, item.CaptionName, item.EnglishCaption, item.TurkishCaption, owned, inserted);
|
await WriteLanguageRowsAsync(rows, item.CaptionName, item.EnglishCaption, item.TurkishCaption, owned, inserted);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,56 @@ public static class EditorScript
|
||||||
return string.Join("\n", lines);
|
return string.Join("\n", lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Script Builder kurallarini tasiyan script'in kodunu kurallardan yeniden uretir. Girdi
|
||||||
|
/// <c>// @builder {...}</c> basligiyla baslayan bir script ya da yalnizca kural JSON'u
|
||||||
|
/// (<c>{"v":1,"rules":[...]}</c>) olabilir; basligin altindaki elle yazilmis govde atilir —
|
||||||
|
/// yanlis yardimci kullanimi (<c>str(get('X'))</c>) calismayan script uretirdi. Kural tasimayan
|
||||||
|
/// script oldugu gibi doner. <paramref name="isBooleanField"/> verilirse mantiksal alana deger yazan
|
||||||
|
/// kuralin degeri dialogdaki Evet/Hayir seciminin karsiligina (<c>true</c>/<c>false</c>) cevrilir.
|
||||||
|
/// </summary>
|
||||||
|
public static string Rebuild(string script, System.Func<string, bool> isBooleanField = null)
|
||||||
|
{
|
||||||
|
var text = script?.Trim();
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
string json;
|
||||||
|
if (text.StartsWith(BuilderHeader, System.StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var lineEnd = text.IndexOf('\n');
|
||||||
|
json = (lineEnd < 0 ? text : text[..lineEnd])[BuilderHeader.Length..];
|
||||||
|
}
|
||||||
|
else if (text.StartsWith('{'))
|
||||||
|
{
|
||||||
|
json = text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var document = System.Text.Json.JsonDocument.Parse(json);
|
||||||
|
if (!document.RootElement.TryGetProperty("rules", out var rules) || rules.ValueKind != System.Text.Json.JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
var built = Build(rules.EnumerateArray()
|
||||||
|
.Select(EditorScriptRule.FromJson)
|
||||||
|
.Select(rule => isBooleanField == null ? rule : rule.WithBooleanValue(isBooleanField)));
|
||||||
|
return built.Length == 0 ? script : built;
|
||||||
|
}
|
||||||
|
catch (System.Text.Json.JsonException)
|
||||||
|
{
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------- *
|
/* ---------------------------------------------------------------- *
|
||||||
* Ortak yardimcilar (scriptRecipes.ts portu)
|
* Ortak yardimcilar (scriptRecipes.ts portu)
|
||||||
* ---------------------------------------------------------------- */
|
* ---------------------------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ public sealed class EditorScriptCondition
|
||||||
|
|
||||||
internal string Value { get; }
|
internal string Value { get; }
|
||||||
|
|
||||||
|
/// <summary>Kural JSON'undaki kosulu geri okur (<see cref="EditorScriptRule.FromJson"/>).</summary>
|
||||||
|
internal static EditorScriptCondition Of(string @operator, string source, string value)
|
||||||
|
=> new EditorScriptCondition(@operator, source, value);
|
||||||
|
|
||||||
/// <summary>Kosulsuz - kural her zaman calisir.</summary>
|
/// <summary>Kosulsuz - kural her zaman calisir.</summary>
|
||||||
public static EditorScriptCondition Always()
|
public static EditorScriptCondition Always()
|
||||||
=> new EditorScriptCondition("always", string.Empty, string.Empty);
|
=> new EditorScriptCondition("always", string.Empty, string.Empty);
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,76 @@ public sealed class EditorScriptRule
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ToJson"/> ciktisini (dialogun <c>@builder</c> basligindaki kural) geri okur; bilinmeyen
|
||||||
|
/// tarif <see cref="IsComplete"/>'ten gecmez ve uretimde atlanir.
|
||||||
|
/// </summary>
|
||||||
|
internal static EditorScriptRule FromJson(System.Text.Json.JsonElement json)
|
||||||
|
{
|
||||||
|
static string Text(System.Text.Json.JsonElement element, string name) =>
|
||||||
|
element.ValueKind == System.Text.Json.JsonValueKind.Object && element.TryGetProperty(name, out var value)
|
||||||
|
? value.ValueKind == System.Text.Json.JsonValueKind.String ? value.GetString() : value.ToString()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
static EditorScriptCondition Condition(System.Text.Json.JsonElement element) =>
|
||||||
|
EditorScriptCondition.Of(Text(element, "operator") ?? "always", Text(element, "source"), Text(element, "value"));
|
||||||
|
|
||||||
|
var rule = new EditorScriptRule(Text(json, "recipe") ?? string.Empty).WithId(Text(json, "id"));
|
||||||
|
|
||||||
|
if (json.TryGetProperty("params", out var parameters) && parameters.ValueKind == System.Text.Json.JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
foreach (var parameter in parameters.EnumerateObject())
|
||||||
|
rule.Set(parameter.Name, Text(parameters, parameter.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.TryGetProperty("fields", out var fields) && fields.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||||
|
rule.SetFields(fields.EnumerateArray().Select(field => field.ToString()));
|
||||||
|
|
||||||
|
var conditions = new List<EditorScriptCondition>();
|
||||||
|
if (json.TryGetProperty("condition", out var condition))
|
||||||
|
conditions.Add(Condition(condition));
|
||||||
|
if (json.TryGetProperty("conditions", out var more) && more.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||||
|
conditions.AddRange(more.EnumerateArray().Select(Condition));
|
||||||
|
|
||||||
|
// Kosulsuz kural "always" ile yazilir; ToJson onu kendisi ekler.
|
||||||
|
rule.AddConditions([.. conditions.Where(c => c.Operator != "always")], Text(json, "join") == "or");
|
||||||
|
|
||||||
|
return rule.WithTrigger(Text(json, "trigger") switch
|
||||||
|
{
|
||||||
|
"change" => EditorScriptTrigger.Change,
|
||||||
|
"open" => EditorScriptTrigger.Open,
|
||||||
|
"both" => EditorScriptTrigger.Both,
|
||||||
|
_ => EditorScriptTrigger.Default
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mantiksal alana yazan <c>setValue</c> kuralinin degerini <c>true</c>/<c>false</c>'a cevirir; dialog bu
|
||||||
|
/// alanlarda yalnizca Evet/Hayir sectirir. <c>tpl("0")</c> metni onay kutusunu dolu sayardi.
|
||||||
|
/// </summary>
|
||||||
|
internal EditorScriptRule WithBooleanValue(System.Func<string, bool> isBooleanField)
|
||||||
|
{
|
||||||
|
if (_recipe != "setValue" || !isBooleanField(Param("target")))
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var flag = Param("text").Trim().ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"true" or "1" or "yes" or "evet" or "on" => "true",
|
||||||
|
"false" or "0" or "no" or "hayir" or "hayır" or "off" => "false",
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
var index = _params.FindIndex(pair => pair.Key == "text");
|
||||||
|
if (flag != null && index >= 0)
|
||||||
|
{
|
||||||
|
_params[index] = new KeyValuePair<string, string>("text", flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Tek kurallik script metni. Birden fazla kural icin <see cref="EditorScript.Build(EditorScriptRule[])"/> kullanin.</summary>
|
/// <summary>Tek kurallik script metni. Birden fazla kural icin <see cref="EditorScript.Build(EditorScriptRule[])"/> kullanin.</summary>
|
||||||
public string ToScript() => EditorScript.Build(this);
|
public string ToScript() => EditorScript.Build(this);
|
||||||
|
|
||||||
|
|
@ -352,7 +422,7 @@ public sealed class EditorScriptRule
|
||||||
return "copy(" + q(Param("source")) + ", " + q(Param("target")) + ")";
|
return "copy(" + q(Param("source")) + ", " + q(Param("target")) + ")";
|
||||||
|
|
||||||
case "setValue":
|
case "setValue":
|
||||||
return "set(" + q(Param("target")) + ", tpl(" + q(Param("text")) + "))";
|
return "set(" + q(Param("target")) + ", " + ValueLiteral(Param("text")) + ")";
|
||||||
|
|
||||||
case "clear":
|
case "clear":
|
||||||
return "clear(" + string.Join(", ", _fields.Select(q)) + ")";
|
return "clear(" + string.Join(", ", _fields.Select(q)) + ")";
|
||||||
|
|
@ -396,6 +466,18 @@ public sealed class EditorScriptRule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>true</c>/<c>false</c> mantiksal sabit olarak yazilir; metin olarak (<c>tpl('false')</c>) yazilsa
|
||||||
|
/// onay kutusu dolu sayardi. Diger degerler sablondur. scriptRecipes.ts <c>valueLiteral</c> ile ayni.
|
||||||
|
/// </summary>
|
||||||
|
private static string ValueLiteral(string text)
|
||||||
|
{
|
||||||
|
var trimmed = (text ?? string.Empty).Trim();
|
||||||
|
return trimmed.Equals("true", System.StringComparison.OrdinalIgnoreCase) || trimmed.Equals("false", System.StringComparison.OrdinalIgnoreCase)
|
||||||
|
? trimmed.ToLowerInvariant()
|
||||||
|
: "tpl(" + EditorScript.Quote(text) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
private static string TriggerName(EditorScriptTrigger trigger)
|
private static string TriggerName(EditorScriptTrigger trigger)
|
||||||
{
|
{
|
||||||
switch (trigger)
|
switch (trigger)
|
||||||
|
|
|
||||||
10
claude.md
10
claude.md
|
|
@ -232,6 +232,13 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
`name` düz metin değil `App.StaticLookup.*` dil anahtarıdır; EN + TR satırı dil dosyasına yazılır.
|
`name` düz metin değil `App.StaticLookup.*` dil anahtarıdır; EN + TR satırı dil dosyasına yazılır.
|
||||||
Deploy düz metni kendisi anahtara çevirir (aynı TR metinli anahtar varsa onu kullanır) —
|
Deploy düz metni kendisi anahtara çevirir (aynı TR metinli anahtar varsa onu kullanır) —
|
||||||
`ListFormWizardAppService.LocalizeStaticLookupsAsync`, `lowcode.instructions.md` §4.3.4.1.
|
`ListFormWizardAppService.LocalizeStaticLookupsAsync`, `lowcode.instructions.md` §4.3.4.1.
|
||||||
|
20. **EditorScript** → MCP/wizard script'i elle yazmaz, Script Builder kurallarını verir; deploy kodu
|
||||||
|
kurallardan üretir (`EditorScript.Rebuild`). `str/num/bool` alan **adı** alır (`str('X')`, değeri
|
||||||
|
değil). `setValue`'da `true`/`false` mantıksal yazılır (onay kutusu alanında `0`/`1` deploy'da çevrilir).
|
||||||
|
MCP ve Script Builder aynı kuralları aynı koda çevirir; `set` zinciri derinlik sınırsızdır, döngü ve
|
||||||
|
script hatası kullanıcıya bildirilir. Ayrıntı: `lowcode.instructions.md` §4.4, §5.3, §5.6.
|
||||||
|
21. **Demo veri ≠ seed senkronu** → `SeedData: true` yalnızca `SeedFilePath` açar;
|
||||||
|
`SeedSyncInsert/Update/Delete` istekte açıkça istenmedikçe `false`'tur (wizard cevabı `SeedSync`).
|
||||||
|
|
||||||
### Cross-cutting: touching one thing means touching these too
|
### Cross-cutting: touching one thing means touching these too
|
||||||
|
|
||||||
|
|
@ -335,7 +342,8 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
adım ekleme. Ayrıntı: `lowcode.instructions.md` §4.0.
|
adım ekleme. Ayrıntı: `lowcode.instructions.md` §4.0.
|
||||||
- **MCP ile demo veri** → geliştirme isteği örnek satır taşıyorsa (prompt'taki tablo/liste, ekran
|
- **MCP ile demo veri** → geliştirme isteği örnek satır taşıyorsa (prompt'taki tablo/liste, ekran
|
||||||
görüntüsü) ekran `deploy_wizard`'a `SeedData: true` ile gider (`SeedFilePath = data/{ListFormCode}.json`
|
görüntüsü) ekran `deploy_wizard`'a `SeedData: true` ile gider (`SeedFilePath = data/{ListFormCode}.json`
|
||||||
+ `SeedSync*` açık, `ListFormWizardDto.BuildListForm`) ve satırlar aynı çağrıda `seedRows` ile
|
; `SeedSync*` bayrakları **kapalı** kalır — yalnızca istek ekrandan girilen kayıtların dosyaya
|
||||||
|
yansımasını açıkça isterse `SeedSync: true`, wizard ekranında da aynı; `ListFormWizardDto.BuildListForm`) ve satırlar aynı çağrıda `seedRows` ile
|
||||||
`configs/seeds/data/{ListFormCode}.json` dosyasına yazılır (`McpDevelopAppService`; alan adları
|
`configs/seeds/data/{ListFormCode}.json` dosyasına yazılır (`McpDevelopAppService`; alan adları
|
||||||
ekrana göre doğrulanır, `Guid` anahtarı sunucu üretir). Migrate ekranı kurar, ardından satırları
|
ekrana göre doğrulanır, `Guid` anahtarı sunucu üretir). Migrate ekranı kurar, ardından satırları
|
||||||
ekler; MCP migrate'i beklemez. Talep döngüsü `Testing`'de biter, `Staging`'e kullanıcı alır. Dosya elle yazılmaz, görselden okunamayan değer uydurulmaz.
|
ekler; MCP migrate'i beklemez. Talep döngüsü `Testing`'de biter, `Staging`'e kullanıcı alır. Dosya elle yazılmaz, görselden okunamayan değer uydurulmaz.
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -53,6 +53,25 @@
|
||||||
"Cost": 0,
|
"Cost": 0,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Id": "4de8b328-a0bf-3b36-96d9-3a2429f022bf"
|
"Id": "4de8b328-a0bf-3b36-96d9-3a2429f022bf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "eb0ce687-3014-4f8b-9482-469f3e6d3993",
|
||||||
|
"Name": "Deneme",
|
||||||
|
"IsFavorite": false,
|
||||||
|
"CanBeSold": true,
|
||||||
|
"CanBeExpensed": false,
|
||||||
|
"CanBePurchased": false,
|
||||||
|
"ProductType": "Service",
|
||||||
|
"CategoryId": "eb9eed73-a7e1-5b5b-c31b-3a2429dfe211",
|
||||||
|
"TrackInventory": false,
|
||||||
|
"TrackingMethod": "Quantity",
|
||||||
|
"SalesPrice": 0.0000,
|
||||||
|
"Cost": 0.0000,
|
||||||
|
"InternalReference": null,
|
||||||
|
"UomId": "Adet",
|
||||||
|
"PurchaseUomId": "Adet",
|
||||||
|
"IsActive": true,
|
||||||
|
"Image": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59742,6 +59742,48 @@
|
||||||
"Key": "App.ScriptBuilder.AddOption",
|
"Key": "App.ScriptBuilder.AddOption",
|
||||||
"Value": "+ ekle"
|
"Value": "+ ekle"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Id": "92147417-0362-5de6-9fdd-8c45da7e3857",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ScriptBuilder.SyntaxError",
|
||||||
|
"Value": "The script has a syntax error and was not saved: {message}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "cd1e1fe4-af2d-5042-b258-ae16236da342",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ScriptBuilder.SyntaxError",
|
||||||
|
"Value": "Script'te söz dizimi hatası var, kaydedilmedi: {message}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "e0f30407-7383-5a15-ad1d-7beb2c8a264c",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.EditorScript.Error",
|
||||||
|
"Value": "The editor script of field '{field}' is faulty and did not run: {message}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "cf351e16-422f-565e-ae84-ff72de0f8102",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.EditorScript.Error",
|
||||||
|
"Value": "'{field}' alanının editör script'i hatalı, çalışmadı: {message}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "cf45e07a-0a2e-50bc-92d2-af9850cd22d7",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ScriptBuilder.StaleHeader",
|
||||||
|
"Value": "The code no longer matches the wizard rules. On save the rules header is removed and the code is kept as hand-written; use Regenerate to return to the rules."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "7c7d420a-df72-5d21-b2e4-5f9f95bfe873",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ScriptBuilder.StaleHeader",
|
||||||
|
"Value": "Kod sihirbaz kurallarıyla artık eşleşmiyor. Kaydedince kural başlığı kaldırılır ve kod elle yazılmış olarak kalır; kurallara dönmek için Yeniden üret'i kullanın."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Id": "5c33f8a3-775e-50ba-9e9b-97e0a4032e7a",
|
"Id": "5c33f8a3-775e-50ba-9e9b-97e0a4032e7a",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,6 @@ Dış entegrasyon yok. Ölçü Birimleri, Kontaklar, Para Birimleri, Ülkeler ve
|
||||||
| `InternalReference` | İç Referans | dxTextBox | | ✓ | |
|
| `InternalReference` | İç Referans | dxTextBox | | ✓ | |
|
||||||
| `ProductName` | Adı | dxTextBox | | ✓ | |
|
| `ProductName` | Adı | dxTextBox | | ✓ | |
|
||||||
| `VariantName` | Varyant Değerleri | dxTextBox | | ✓ | |
|
| `VariantName` | Varyant Değerleri | dxTextBox | | ✓ | |
|
||||||
| `VariantKey` | Varyant Anahtarı | dxTextBox | | | |
|
|
||||||
| `SalesPrice` | Satış Fiyatı | dxNumberBox | | ✓ | |
|
| `SalesPrice` | Satış Fiyatı | dxNumberBox | | ✓ | |
|
||||||
| `Cost` | Maliyet | dxNumberBox | | ✓ | |
|
| `Cost` | Maliyet | dxNumberBox | | ✓ | |
|
||||||
| `UomId` | Birim | dxSelectBox | | ✓ | sorgu: Adm_T_Uom |
|
| `UomId` | Birim | dxSelectBox | | ✓ | sorgu: Adm_T_Uom |
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Inventory.Attribute.json",
|
"SeedFilePath": "data/App.Inventory.Attribute.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Inv_T_Attribute\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Inv_T_Attribute\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Prc.DeliveryMethod.json",
|
"SeedFilePath": "data/App.Prc.DeliveryMethod.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Prc_T_DeliveryMethod\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Prc_T_DeliveryMethod\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -771,7 +771,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -909,7 +909,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1047,7 +1047,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Prc.Incoterm.json",
|
"SeedFilePath": "data/App.Prc.Incoterm.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Prc_T_Incoterm\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Prc_T_Incoterm\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -777,7 +777,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -915,7 +915,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1053,7 +1053,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1191,7 +1191,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 3,
|
"EditOrderNo": 3,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Prc.PaymentMethod.json",
|
"SeedFilePath": "data/App.Prc.PaymentMethod.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Prc_T_PaymentMethod\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Prc_T_PaymentMethod\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -777,7 +777,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -915,7 +915,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1041,7 +1041,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1179,7 +1179,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 3,
|
"EditOrderNo": 3,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Prc.PaymentTerm.json",
|
"SeedFilePath": "data/App.Prc.PaymentTerm.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Prc_T_PaymentTerm\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Prc_T_PaymentTerm\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -783,7 +783,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -921,7 +921,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1047,7 +1047,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1185,7 +1185,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 3,
|
"EditOrderNo": 3,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1311,7 +1311,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 4,
|
"EditOrderNo": 4,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -171,9 +171,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Inventory.Product.json",
|
"SeedFilePath": "data/App.Inventory.Product.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Inv_T_Product\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Inv_T_Product\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -1846,7 +1846,7 @@
|
||||||
"GridBoxOptions": null,
|
"GridBoxOptions": null,
|
||||||
"TagBoxOptions": null,
|
"TagBoxOptions": null,
|
||||||
"ImageUploadOptions": null,
|
"ImageUploadOptions": null,
|
||||||
"EditorScript": "// @runOnOpen\nreadOnly(\"TrackInventory\", str(get(\"ProductType\")) === \"Service\")\nif (!isReady && str(value) === \"Service\") { set(\"TrackInventory\", false); clear(\"TrackingMethod\"); readOnly(\"TrackingMethod\", true) }",
|
"EditorScript": "// @runOnOpen\nif (str(\"ProductType\") === \"Service\") set(\"TrackInventory\", false)\nreadOnly(\"TrackInventory\", str(\"ProductType\") === \"Service\")",
|
||||||
"LastModificationTime": null,
|
"LastModificationTime": null,
|
||||||
"LastModifierId": null,
|
"LastModifierId": null,
|
||||||
"CreationTime": "0001-01-01T00:00:00",
|
"CreationTime": "0001-01-01T00:00:00",
|
||||||
|
|
|
||||||
|
|
@ -763,7 +763,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -889,7 +889,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -1027,7 +1027,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1121,11 +1121,11 @@
|
||||||
"DataSourceType": 2,
|
"DataSourceType": 2,
|
||||||
"ValueExpr": "Key",
|
"ValueExpr": "Key",
|
||||||
"DisplayExpr": "Name",
|
"DisplayExpr": "Name",
|
||||||
"CascadeRelationField": null,
|
"CascadeRelationField": "",
|
||||||
"CascadeFilterOperator": "=",
|
"CascadeFilterOperator": "=",
|
||||||
"CascadeParentFields": "AttributeId",
|
"CascadeParentFields": "AttributeId",
|
||||||
"CascadeEmptyFields": null,
|
"CascadeEmptyFields": "",
|
||||||
"LookupQuery": "SELECT CAST(Id AS nvarchar(36)) AS [Key], Name AS [Name] FROM Inv_T_AttributeValue WHERE IsDeleted = 'false' AND IsActive = 1 AND (AttributeId = @param0 OR @param0 IS NULL) ORDER BY SortOrder, Name"
|
"LookupQuery": "SELECT CAST(Id AS nvarchar(36)) AS [Key], Name AS [Name], ColorCode AS [Color] FROM Inv_T_AttributeValue WHERE IsDeleted = 'false' AND IsActive = 1 AND (AttributeId = @param0 OR @param0 IS NULL) ORDER BY SortOrder, Name"
|
||||||
},
|
},
|
||||||
"ValidationRuleDto": [
|
"ValidationRuleDto": [
|
||||||
{
|
{
|
||||||
|
|
@ -1165,7 +1165,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Inventory.ProductCategory.json",
|
"SeedFilePath": "data/App.Inventory.ProductCategory.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Inv_T_ProductCategory\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Inv_T_ProductCategory\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -777,7 +777,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -915,7 +915,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1041,7 +1041,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1179,7 +1179,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 3,
|
"EditOrderNo": 3,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -68,12 +68,6 @@
|
||||||
"En": "Variant Values",
|
"En": "Variant Values",
|
||||||
"Tr": "Varyant Değerleri"
|
"Tr": "Varyant Değerleri"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"FieldName": "VariantKey",
|
|
||||||
"CaptionName": "App.Listform.ListformField.VariantKey",
|
|
||||||
"En": "Variant Key",
|
|
||||||
"Tr": "Varyant Anahtarı"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"FieldName": "SalesPrice",
|
"FieldName": "SalesPrice",
|
||||||
"CaptionName": "App.Listform.ListformField.SalesPrice",
|
"CaptionName": "App.Listform.ListformField.SalesPrice",
|
||||||
|
|
@ -789,7 +783,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -915,7 +909,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -1041,7 +1035,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1167,7 +1161,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1293,7 +1287,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 3,
|
"EditOrderNo": 3,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1326,17 +1320,17 @@
|
||||||
"JoinFieldName2": null,
|
"JoinFieldName2": null,
|
||||||
"BaseTableJoinFieldName2_OrValue": null
|
"BaseTableJoinFieldName2_OrValue": null
|
||||||
},
|
},
|
||||||
"FieldName": "VariantKey",
|
"FieldName": "SalesPrice",
|
||||||
"CaptionName": "App.Listform.ListformField.VariantKey",
|
"CaptionName": "App.Listform.ListformField.SalesPrice",
|
||||||
"PlaceHolder": null,
|
"PlaceHolder": null,
|
||||||
"ReadOnly": false,
|
"ReadOnly": false,
|
||||||
"Visible": true,
|
"Visible": true,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 6,
|
"ListOrderNo": 6,
|
||||||
"SourceDbType": 16,
|
"SourceDbType": 7,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "string",
|
"DataType": "number",
|
||||||
"SortIndex": 0,
|
"SortIndex": 0,
|
||||||
"SortDirection": null,
|
"SortDirection": null,
|
||||||
"AllowSearch": true,
|
"AllowSearch": true,
|
||||||
|
|
@ -1419,13 +1413,13 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": 4,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": 1,
|
||||||
"EditorType2": "dxTextBox",
|
"EditorType2": "dxNumberBox",
|
||||||
"ColSpan": 1,
|
"ColSpan": 1,
|
||||||
"EditorOptions": null,
|
"EditorOptions": "{\"format\":{\"type\":\"fixedPoint\",\"precision\":2}}",
|
||||||
"GridBoxOptions": null,
|
"GridBoxOptions": null,
|
||||||
"TagBoxOptions": null,
|
"TagBoxOptions": null,
|
||||||
"ImageUploadOptions": null,
|
"ImageUploadOptions": null,
|
||||||
|
|
@ -1452,8 +1446,8 @@
|
||||||
"JoinFieldName2": null,
|
"JoinFieldName2": null,
|
||||||
"BaseTableJoinFieldName2_OrValue": null
|
"BaseTableJoinFieldName2_OrValue": null
|
||||||
},
|
},
|
||||||
"FieldName": "SalesPrice",
|
"FieldName": "Cost",
|
||||||
"CaptionName": "App.Listform.ListformField.SalesPrice",
|
"CaptionName": "App.Listform.ListformField.Cost",
|
||||||
"PlaceHolder": null,
|
"PlaceHolder": null,
|
||||||
"ReadOnly": false,
|
"ReadOnly": false,
|
||||||
"Visible": true,
|
"Visible": true,
|
||||||
|
|
@ -1545,133 +1539,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
|
||||||
"EditOrderNo": 4,
|
|
||||||
"EditGroupOrderNo": 1,
|
|
||||||
"EditorType2": "dxNumberBox",
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"format\":{\"type\":\"fixedPoint\",\"precision\":2}}",
|
|
||||||
"GridBoxOptions": null,
|
|
||||||
"TagBoxOptions": null,
|
|
||||||
"ImageUploadOptions": null,
|
|
||||||
"EditorScript": null,
|
|
||||||
"LastModificationTime": null,
|
|
||||||
"LastModifierId": null,
|
|
||||||
"CreationTime": "0001-01-01T00:00:00",
|
|
||||||
"CreatorId": null,
|
|
||||||
"Id": "00000000-0000-0000-0000-000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"EditType": null,
|
|
||||||
"ListFormCode": "App.Inventory.ProductVariant",
|
|
||||||
"RoleId": null,
|
|
||||||
"UserId": null,
|
|
||||||
"ColumnJoinTableDto": {
|
|
||||||
"JoinType": null,
|
|
||||||
"TableName": null,
|
|
||||||
"JoinFieldName": null,
|
|
||||||
"BaseTableJoinFieldName": null,
|
|
||||||
"SelectFieldName": null,
|
|
||||||
"FieldNameAlias": null,
|
|
||||||
"JoinCondition2": "AND",
|
|
||||||
"JoinFieldName2": null,
|
|
||||||
"BaseTableJoinFieldName2_OrValue": null
|
|
||||||
},
|
|
||||||
"FieldName": "Cost",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Cost",
|
|
||||||
"PlaceHolder": null,
|
|
||||||
"ReadOnly": false,
|
|
||||||
"Visible": true,
|
|
||||||
"IsActive": true,
|
|
||||||
"Width": 0,
|
|
||||||
"ListOrderNo": 8,
|
|
||||||
"SourceDbType": 7,
|
|
||||||
"CultureName": "en",
|
|
||||||
"DataType": "number",
|
|
||||||
"SortIndex": 0,
|
|
||||||
"SortDirection": null,
|
|
||||||
"AllowSearch": true,
|
|
||||||
"AllowEditing": true,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"ColumnCssClass": null,
|
|
||||||
"ColumnCssValue": null,
|
|
||||||
"BandName": null,
|
|
||||||
"ColumnFilterDto": {
|
|
||||||
"AllowFiltering": true,
|
|
||||||
"FilterOperations": null,
|
|
||||||
"SelectedFilterOperation": null,
|
|
||||||
"FilterValue": null
|
|
||||||
},
|
|
||||||
"ColumnHeaderDto": {
|
|
||||||
"AllowHeaderFiltering": true,
|
|
||||||
"AllowSearch": false,
|
|
||||||
"DataSource": null,
|
|
||||||
"DataSourceString": ""
|
|
||||||
},
|
|
||||||
"ColumnGroupingDto": {
|
|
||||||
"AllowGrouping": true,
|
|
||||||
"GroupIndex": null,
|
|
||||||
"AutoExpandGroup": false
|
|
||||||
},
|
|
||||||
"ColumnCustomizationDto": {
|
|
||||||
"Fixed": false,
|
|
||||||
"FixedPosition": "left",
|
|
||||||
"AllowReordering": true
|
|
||||||
},
|
|
||||||
"ColumnTotalSummaryDto": {
|
|
||||||
"SummaryType": null,
|
|
||||||
"ValueFormat": null,
|
|
||||||
"ShowInGroupFooter": false,
|
|
||||||
"AlignByColumn": false,
|
|
||||||
"DisplayFormat": null,
|
|
||||||
"ShowInColumn": null
|
|
||||||
},
|
|
||||||
"ColumnGroupSummaryDto": {
|
|
||||||
"SummaryType": null,
|
|
||||||
"ValueFormat": null,
|
|
||||||
"ShowInGroupFooter": false,
|
|
||||||
"AlignByColumn": false,
|
|
||||||
"DisplayFormat": null,
|
|
||||||
"ShowInColumn": null
|
|
||||||
},
|
|
||||||
"LookupDto": {
|
|
||||||
"DataSourceType": 0,
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name",
|
|
||||||
"CascadeRelationField": null,
|
|
||||||
"CascadeFilterOperator": "=",
|
|
||||||
"CascadeParentFields": null,
|
|
||||||
"CascadeEmptyFields": null,
|
|
||||||
"LookupQuery": null
|
|
||||||
},
|
|
||||||
"ValidationRuleDto": [],
|
|
||||||
"ColumnStylingDto": [],
|
|
||||||
"PermissionDto": {
|
|
||||||
"Deny": false,
|
|
||||||
"C": "App.Inventory.ProductVariant.Create",
|
|
||||||
"R": "App.Inventory.ProductVariant",
|
|
||||||
"U": "App.Inventory.ProductVariant.Update",
|
|
||||||
"E": true,
|
|
||||||
"I": true
|
|
||||||
},
|
|
||||||
"CanRead": false,
|
|
||||||
"CanCreate": false,
|
|
||||||
"CanUpdate": false,
|
|
||||||
"CanExport": false,
|
|
||||||
"DefaultValue": null,
|
|
||||||
"DefaultValueType": 0,
|
|
||||||
"PivotSettingsDto": {
|
|
||||||
"IsPivot": true,
|
|
||||||
"Expanded": false,
|
|
||||||
"SortOrder": "asc",
|
|
||||||
"Area": "filter",
|
|
||||||
"Format": null,
|
|
||||||
"SummaryType": null,
|
|
||||||
"GroupInterval": "",
|
|
||||||
"WordWrapEnabled": false
|
|
||||||
},
|
|
||||||
"Alignment": null,
|
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 5,
|
"EditOrderNo": 5,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1711,7 +1579,7 @@
|
||||||
"Visible": true,
|
"Visible": true,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 9,
|
"ListOrderNo": 8,
|
||||||
"SourceDbType": 16,
|
"SourceDbType": 16,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "string",
|
"DataType": "string",
|
||||||
|
|
@ -1797,7 +1665,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 6,
|
"EditOrderNo": 6,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1837,7 +1705,7 @@
|
||||||
"Visible": true,
|
"Visible": true,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 10,
|
"ListOrderNo": 9,
|
||||||
"SourceDbType": 9,
|
"SourceDbType": 9,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "string",
|
"DataType": "string",
|
||||||
|
|
@ -1923,7 +1791,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 7,
|
"EditOrderNo": 7,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1963,6 +1831,132 @@
|
||||||
"Visible": true,
|
"Visible": true,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
|
"ListOrderNo": 10,
|
||||||
|
"SourceDbType": 3,
|
||||||
|
"CultureName": "en",
|
||||||
|
"DataType": "boolean",
|
||||||
|
"SortIndex": 0,
|
||||||
|
"SortDirection": null,
|
||||||
|
"AllowSearch": true,
|
||||||
|
"AllowEditing": true,
|
||||||
|
"AllowAdding": true,
|
||||||
|
"ColumnCssClass": null,
|
||||||
|
"ColumnCssValue": null,
|
||||||
|
"BandName": null,
|
||||||
|
"ColumnFilterDto": {
|
||||||
|
"AllowFiltering": true,
|
||||||
|
"FilterOperations": null,
|
||||||
|
"SelectedFilterOperation": null,
|
||||||
|
"FilterValue": null
|
||||||
|
},
|
||||||
|
"ColumnHeaderDto": {
|
||||||
|
"AllowHeaderFiltering": true,
|
||||||
|
"AllowSearch": false,
|
||||||
|
"DataSource": null,
|
||||||
|
"DataSourceString": ""
|
||||||
|
},
|
||||||
|
"ColumnGroupingDto": {
|
||||||
|
"AllowGrouping": true,
|
||||||
|
"GroupIndex": null,
|
||||||
|
"AutoExpandGroup": false
|
||||||
|
},
|
||||||
|
"ColumnCustomizationDto": {
|
||||||
|
"Fixed": false,
|
||||||
|
"FixedPosition": "left",
|
||||||
|
"AllowReordering": true
|
||||||
|
},
|
||||||
|
"ColumnTotalSummaryDto": {
|
||||||
|
"SummaryType": null,
|
||||||
|
"ValueFormat": null,
|
||||||
|
"ShowInGroupFooter": false,
|
||||||
|
"AlignByColumn": false,
|
||||||
|
"DisplayFormat": null,
|
||||||
|
"ShowInColumn": null
|
||||||
|
},
|
||||||
|
"ColumnGroupSummaryDto": {
|
||||||
|
"SummaryType": null,
|
||||||
|
"ValueFormat": null,
|
||||||
|
"ShowInGroupFooter": false,
|
||||||
|
"AlignByColumn": false,
|
||||||
|
"DisplayFormat": null,
|
||||||
|
"ShowInColumn": null
|
||||||
|
},
|
||||||
|
"LookupDto": {
|
||||||
|
"DataSourceType": 0,
|
||||||
|
"ValueExpr": "Key",
|
||||||
|
"DisplayExpr": "Name",
|
||||||
|
"CascadeRelationField": null,
|
||||||
|
"CascadeFilterOperator": "=",
|
||||||
|
"CascadeParentFields": null,
|
||||||
|
"CascadeEmptyFields": null,
|
||||||
|
"LookupQuery": null
|
||||||
|
},
|
||||||
|
"ValidationRuleDto": [],
|
||||||
|
"ColumnStylingDto": [],
|
||||||
|
"PermissionDto": {
|
||||||
|
"Deny": false,
|
||||||
|
"C": "App.Inventory.ProductVariant.Create",
|
||||||
|
"R": "App.Inventory.ProductVariant",
|
||||||
|
"U": "App.Inventory.ProductVariant.Update",
|
||||||
|
"E": true,
|
||||||
|
"I": true
|
||||||
|
},
|
||||||
|
"CanRead": false,
|
||||||
|
"CanCreate": false,
|
||||||
|
"CanUpdate": false,
|
||||||
|
"CanExport": false,
|
||||||
|
"DefaultValue": null,
|
||||||
|
"DefaultValueType": 0,
|
||||||
|
"PivotSettingsDto": {
|
||||||
|
"IsPivot": true,
|
||||||
|
"Expanded": false,
|
||||||
|
"SortOrder": "asc",
|
||||||
|
"Area": "filter",
|
||||||
|
"Format": null,
|
||||||
|
"SummaryType": null,
|
||||||
|
"GroupInterval": "",
|
||||||
|
"WordWrapEnabled": false
|
||||||
|
},
|
||||||
|
"Alignment": "left",
|
||||||
|
"Format": null,
|
||||||
|
"EditOrderNo": 8,
|
||||||
|
"EditGroupOrderNo": 1,
|
||||||
|
"EditorType2": "dxCheckBox",
|
||||||
|
"ColSpan": 1,
|
||||||
|
"EditorOptions": null,
|
||||||
|
"GridBoxOptions": null,
|
||||||
|
"TagBoxOptions": null,
|
||||||
|
"ImageUploadOptions": null,
|
||||||
|
"EditorScript": null,
|
||||||
|
"LastModificationTime": null,
|
||||||
|
"LastModifierId": null,
|
||||||
|
"CreationTime": "0001-01-01T00:00:00",
|
||||||
|
"CreatorId": null,
|
||||||
|
"Id": "00000000-0000-0000-0000-000000000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EditType": null,
|
||||||
|
"ListFormCode": "App.Inventory.ProductVariant",
|
||||||
|
"RoleId": null,
|
||||||
|
"UserId": null,
|
||||||
|
"ColumnJoinTableDto": {
|
||||||
|
"JoinType": null,
|
||||||
|
"TableName": null,
|
||||||
|
"JoinFieldName": null,
|
||||||
|
"BaseTableJoinFieldName": null,
|
||||||
|
"SelectFieldName": null,
|
||||||
|
"FieldNameAlias": null,
|
||||||
|
"JoinCondition2": "AND",
|
||||||
|
"JoinFieldName2": null,
|
||||||
|
"BaseTableJoinFieldName2_OrValue": null
|
||||||
|
},
|
||||||
|
"FieldName": "IsDeleted",
|
||||||
|
"CaptionName": "App.Listform.ListformField.IsDeleted",
|
||||||
|
"PlaceHolder": null,
|
||||||
|
"ReadOnly": false,
|
||||||
|
"Visible": true,
|
||||||
|
"IsActive": true,
|
||||||
|
"Width": 0,
|
||||||
"ListOrderNo": 11,
|
"ListOrderNo": 11,
|
||||||
"SourceDbType": 3,
|
"SourceDbType": 3,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -2049,133 +2043,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
|
||||||
"EditOrderNo": 8,
|
|
||||||
"EditGroupOrderNo": 1,
|
|
||||||
"EditorType2": "dxCheckBox",
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": null,
|
|
||||||
"GridBoxOptions": null,
|
|
||||||
"TagBoxOptions": null,
|
|
||||||
"ImageUploadOptions": null,
|
|
||||||
"EditorScript": null,
|
|
||||||
"LastModificationTime": null,
|
|
||||||
"LastModifierId": null,
|
|
||||||
"CreationTime": "0001-01-01T00:00:00",
|
|
||||||
"CreatorId": null,
|
|
||||||
"Id": "00000000-0000-0000-0000-000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"EditType": null,
|
|
||||||
"ListFormCode": "App.Inventory.ProductVariant",
|
|
||||||
"RoleId": null,
|
|
||||||
"UserId": null,
|
|
||||||
"ColumnJoinTableDto": {
|
|
||||||
"JoinType": null,
|
|
||||||
"TableName": null,
|
|
||||||
"JoinFieldName": null,
|
|
||||||
"BaseTableJoinFieldName": null,
|
|
||||||
"SelectFieldName": null,
|
|
||||||
"FieldNameAlias": null,
|
|
||||||
"JoinCondition2": "AND",
|
|
||||||
"JoinFieldName2": null,
|
|
||||||
"BaseTableJoinFieldName2_OrValue": null
|
|
||||||
},
|
|
||||||
"FieldName": "IsDeleted",
|
|
||||||
"CaptionName": "App.Listform.ListformField.IsDeleted",
|
|
||||||
"PlaceHolder": null,
|
|
||||||
"ReadOnly": false,
|
|
||||||
"Visible": true,
|
|
||||||
"IsActive": true,
|
|
||||||
"Width": 0,
|
|
||||||
"ListOrderNo": 12,
|
|
||||||
"SourceDbType": 3,
|
|
||||||
"CultureName": "en",
|
|
||||||
"DataType": "boolean",
|
|
||||||
"SortIndex": 0,
|
|
||||||
"SortDirection": null,
|
|
||||||
"AllowSearch": true,
|
|
||||||
"AllowEditing": true,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"ColumnCssClass": null,
|
|
||||||
"ColumnCssValue": null,
|
|
||||||
"BandName": null,
|
|
||||||
"ColumnFilterDto": {
|
|
||||||
"AllowFiltering": true,
|
|
||||||
"FilterOperations": null,
|
|
||||||
"SelectedFilterOperation": null,
|
|
||||||
"FilterValue": null
|
|
||||||
},
|
|
||||||
"ColumnHeaderDto": {
|
|
||||||
"AllowHeaderFiltering": true,
|
|
||||||
"AllowSearch": false,
|
|
||||||
"DataSource": null,
|
|
||||||
"DataSourceString": ""
|
|
||||||
},
|
|
||||||
"ColumnGroupingDto": {
|
|
||||||
"AllowGrouping": true,
|
|
||||||
"GroupIndex": null,
|
|
||||||
"AutoExpandGroup": false
|
|
||||||
},
|
|
||||||
"ColumnCustomizationDto": {
|
|
||||||
"Fixed": false,
|
|
||||||
"FixedPosition": "left",
|
|
||||||
"AllowReordering": true
|
|
||||||
},
|
|
||||||
"ColumnTotalSummaryDto": {
|
|
||||||
"SummaryType": null,
|
|
||||||
"ValueFormat": null,
|
|
||||||
"ShowInGroupFooter": false,
|
|
||||||
"AlignByColumn": false,
|
|
||||||
"DisplayFormat": null,
|
|
||||||
"ShowInColumn": null
|
|
||||||
},
|
|
||||||
"ColumnGroupSummaryDto": {
|
|
||||||
"SummaryType": null,
|
|
||||||
"ValueFormat": null,
|
|
||||||
"ShowInGroupFooter": false,
|
|
||||||
"AlignByColumn": false,
|
|
||||||
"DisplayFormat": null,
|
|
||||||
"ShowInColumn": null
|
|
||||||
},
|
|
||||||
"LookupDto": {
|
|
||||||
"DataSourceType": 0,
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name",
|
|
||||||
"CascadeRelationField": null,
|
|
||||||
"CascadeFilterOperator": "=",
|
|
||||||
"CascadeParentFields": null,
|
|
||||||
"CascadeEmptyFields": null,
|
|
||||||
"LookupQuery": null
|
|
||||||
},
|
|
||||||
"ValidationRuleDto": [],
|
|
||||||
"ColumnStylingDto": [],
|
|
||||||
"PermissionDto": {
|
|
||||||
"Deny": false,
|
|
||||||
"C": "App.Inventory.ProductVariant.Create",
|
|
||||||
"R": "App.Inventory.ProductVariant",
|
|
||||||
"U": "App.Inventory.ProductVariant.Update",
|
|
||||||
"E": true,
|
|
||||||
"I": true
|
|
||||||
},
|
|
||||||
"CanRead": false,
|
|
||||||
"CanCreate": false,
|
|
||||||
"CanUpdate": false,
|
|
||||||
"CanExport": false,
|
|
||||||
"DefaultValue": null,
|
|
||||||
"DefaultValueType": 0,
|
|
||||||
"PivotSettingsDto": {
|
|
||||||
"IsPivot": true,
|
|
||||||
"Expanded": false,
|
|
||||||
"SortOrder": "asc",
|
|
||||||
"Area": "filter",
|
|
||||||
"Format": null,
|
|
||||||
"SummaryType": null,
|
|
||||||
"GroupInterval": "",
|
|
||||||
"WordWrapEnabled": false
|
|
||||||
},
|
|
||||||
"Alignment": null,
|
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
|
||||||
|
|
@ -94,9 +94,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Inventory.WorkCenter.json",
|
"SeedFilePath": "data/App.Inventory.WorkCenter.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Inv_T_WorkCenter\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Inv_T_WorkCenter\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,9 @@
|
||||||
"SelectCommandType": 1,
|
"SelectCommandType": 1,
|
||||||
"SelectFieldsDefaultValueDto": [],
|
"SelectFieldsDefaultValueDto": [],
|
||||||
"SeedFilePath": "data/App.Inventory.WorkCenterTag.json",
|
"SeedFilePath": "data/App.Inventory.WorkCenterTag.json",
|
||||||
"SeedSyncInsert": true,
|
"SeedSyncInsert": false,
|
||||||
"SeedSyncUpdate": true,
|
"SeedSyncUpdate": false,
|
||||||
"SeedSyncDelete": true,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Inv_T_WorkCenterTag\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Inv_T_WorkCenterTag\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": null,
|
"DeleteBeforeCommand": null,
|
||||||
"DeleteAfterCommand": null,
|
"DeleteAfterCommand": null,
|
||||||
|
|
@ -777,7 +777,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
|
|
@ -915,7 +915,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 1,
|
"EditOrderNo": 1,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1041,7 +1041,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 2,
|
"EditOrderNo": 2,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
@ -1179,7 +1179,7 @@
|
||||||
"GroupInterval": "",
|
"GroupInterval": "",
|
||||||
"WordWrapEnabled": false
|
"WordWrapEnabled": false
|
||||||
},
|
},
|
||||||
"Alignment": null,
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 3,
|
"EditOrderNo": 3,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,12 @@ function ScriptBuilderDialog({
|
||||||
const cancelText = cancelLabel ?? translate('::App.Platform.Cancel')
|
const cancelText = cancelLabel ?? translate('::App.Platform.Cancel')
|
||||||
const [rules, setRules] = useState<ScriptRule[]>([])
|
const [rules, setRules] = useState<ScriptRule[]>([])
|
||||||
const [code, setCode] = useState('')
|
const [code, setCode] = useState('')
|
||||||
|
const [syntaxError, setSyntaxError] = useState('')
|
||||||
// Sihirbaz bir butona basılarak açılır; varsayılan her zaman kod editörüdür.
|
// Sihirbaz bir butona basılarak açılır; varsayılan her zaman kod editörüdür.
|
||||||
const [view, setView] = useState<'code' | 'wizard'>('code')
|
const [view, setView] = useState<'code' | 'wizard'>('code')
|
||||||
|
|
||||||
|
// Önceki kaydetme denemesinin hatası, metin değişince ya da diyalog yeniden açılınca geçersizdir.
|
||||||
|
useEffect(() => setSyntaxError(''), [code, view, isOpen])
|
||||||
const [openRules, setOpenRules] = useState<Record<string, boolean>>({})
|
const [openRules, setOpenRules] = useState<Record<string, boolean>>({})
|
||||||
const [paletteOpen, setPaletteOpen] = useState(true)
|
const [paletteOpen, setPaletteOpen] = useState(true)
|
||||||
const [helpOpen, setHelpOpen] = useState(true)
|
const [helpOpen, setHelpOpen] = useState(true)
|
||||||
|
|
@ -102,6 +106,13 @@ function ScriptBuilderDialog({
|
||||||
const generatedScript = useMemo(() => dialect.buildScript(rules), [dialect, rules])
|
const generatedScript = useMemo(() => dialect.buildScript(rules), [dialect, rules])
|
||||||
const completeRuleCount = rules.filter((rule) => dialect.isRuleComplete(rule)).length
|
const completeRuleCount = rules.filter((rule) => dialect.isRuleComplete(rule)).length
|
||||||
const codeDiverged = code.trim() !== '' && code.trim() !== generatedScript.trim()
|
const codeDiverged = code.trim() !== '' && code.trim() !== generatedScript.trim()
|
||||||
|
// Kural başlığı taşıyan ama elle değiştirilmiş kod: başlık artık gövdeyi anlatmaz. Kalırsa
|
||||||
|
// dialog ve sunucu (`EditorScript.Rebuild`) gövdeyi başlıktan yeniden üretip elle yapılan
|
||||||
|
// düzeltmeyi geri alır.
|
||||||
|
const staleHeader = useMemo(() => {
|
||||||
|
const parsed = dialect.parseScript(code)
|
||||||
|
return parsed.hasHeader && !parsed.inSync
|
||||||
|
}, [dialect, code])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) return
|
if (!isOpen) return
|
||||||
|
|
@ -284,9 +295,7 @@ function ScriptBuilderDialog({
|
||||||
menuPortalTarget={document.body}
|
menuPortalTarget={document.body}
|
||||||
options={options.map((option) => ({ value: option, label: option }))}
|
options={options.map((option) => ({ value: option, label: option }))}
|
||||||
value={
|
value={
|
||||||
options.includes(selectedValue)
|
options.includes(selectedValue) ? { value: selectedValue, label: selectedValue } : null
|
||||||
? { value: selectedValue, label: selectedValue }
|
|
||||||
: null
|
|
||||||
}
|
}
|
||||||
onChange={(option) => onChange(option?.value ?? '')}
|
onChange={(option) => onChange(option?.value ?? '')}
|
||||||
/>
|
/>
|
||||||
|
|
@ -341,6 +350,10 @@ function ScriptBuilderDialog({
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentValue = rule.params[param.key] ?? ''
|
const currentValue = rule.params[param.key] ?? ''
|
||||||
|
const ruleChoices =
|
||||||
|
param.type === 'text' ? dialect.getParamChoices?.(rule, param.key) : undefined
|
||||||
|
const choices = ruleChoices ?? param.choices
|
||||||
|
const inputType = ruleChoices ? 'select' : param.type
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label key={param.key} className="min-w-0">
|
<label key={param.key} className="min-w-0">
|
||||||
|
|
@ -359,24 +372,22 @@ function ScriptBuilderDialog({
|
||||||
(next) => updateParam(rule.id, param.key, next),
|
(next) => updateParam(rule.id, param.key, next),
|
||||||
param.placeholder || translate('::App.ScriptBuilder.Choose'),
|
param.placeholder || translate('::App.ScriptBuilder.Choose'),
|
||||||
)}
|
)}
|
||||||
{param.type === 'select' && (
|
{inputType === 'select' && (
|
||||||
<Select
|
<Select
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
menuPortalTarget={document.body}
|
menuPortalTarget={document.body}
|
||||||
options={(param.choices ?? []).map((choice) => ({
|
options={(choices ?? []).map((choice) => ({
|
||||||
value: choice.value,
|
value: choice.value,
|
||||||
label: choice.label,
|
label: choice.label,
|
||||||
}))}
|
}))}
|
||||||
value={(param.choices ?? [])
|
value={(choices ?? [])
|
||||||
.filter((choice) => choice.value === currentValue)
|
.filter((choice) => choice.value === currentValue)
|
||||||
.map((choice) => ({ value: choice.value, label: choice.label }))}
|
.map((choice) => ({ value: choice.value, label: choice.label }))}
|
||||||
onChange={(option) =>
|
onChange={(option) => option && updateParam(rule.id, param.key, option.value)}
|
||||||
option && updateParam(rule.id, param.key, option.value)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(param.type === 'text' || param.type === 'number') && (
|
{(inputType === 'text' || inputType === 'number') && (
|
||||||
<Input
|
<Input
|
||||||
unstyle
|
unstyle
|
||||||
className={controlClass}
|
className={controlClass}
|
||||||
|
|
@ -635,9 +646,7 @@ function ScriptBuilderDialog({
|
||||||
value={triggerOptions.filter(
|
value={triggerOptions.filter(
|
||||||
(option) => option.value === (trigger ?? dialect.triggers?.[0]?.value),
|
(option) => option.value === (trigger ?? dialect.triggers?.[0]?.value),
|
||||||
)}
|
)}
|
||||||
onChange={(option) =>
|
onChange={(option) => option && updateRule(rule.id, { trigger: option.value })}
|
||||||
option && updateRule(rule.id, { trigger: option.value })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
|
|
@ -836,6 +845,13 @@ function ScriptBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{view === 'code' && staleHeader && (
|
||||||
|
<div className="flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-700 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-200">
|
||||||
|
<FaExclamationTriangle className="shrink-0" />
|
||||||
|
<span className="flex-1">{translate('::App.ScriptBuilder.StaleHeader')}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{view === 'code' ? (
|
{view === 'code' ? (
|
||||||
<div className="flex min-h-0 flex-1 gap-3">
|
<div className="flex min-h-0 flex-1 gap-3">
|
||||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col gap-2">
|
<div className="flex min-h-0 min-w-0 flex-1 flex-col gap-2">
|
||||||
|
|
@ -966,6 +982,11 @@ function ScriptBuilderDialog({
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
|
|
||||||
<Dialog.Footer className="mt-1 flex justify-end gap-2 border-t border-gray-200 pt-3 dark:border-gray-700">
|
<Dialog.Footer className="mt-1 flex justify-end gap-2 border-t border-gray-200 pt-3 dark:border-gray-700">
|
||||||
|
{syntaxError && (
|
||||||
|
<p className="mr-auto self-center text-xs text-red-600">
|
||||||
|
{translate('::App.ScriptBuilder.SyntaxError', { message: syntaxError })}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<Button size="sm" type="button" variant="plain" icon={<FaTimes />} onClick={onClose}>
|
<Button size="sm" type="button" variant="plain" icon={<FaTimes />} onClick={onClose}>
|
||||||
{cancelText}
|
{cancelText}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -975,7 +996,18 @@ function ScriptBuilderDialog({
|
||||||
variant="solid"
|
variant="solid"
|
||||||
icon={<FaCheck />}
|
icon={<FaCheck />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onApply(view === 'wizard' ? generatedScript : code)
|
const script =
|
||||||
|
view === 'wizard'
|
||||||
|
? generatedScript
|
||||||
|
: staleHeader
|
||||||
|
? code.trim().split('\n').slice(1).join('\n').trim()
|
||||||
|
: code
|
||||||
|
// Runtime script'i derleyemezse tamamı (diğer kurallar dahil) sessizce çalışmaz;
|
||||||
|
// bozuk metin kaydedilmeden önce yakalanır.
|
||||||
|
const error = findSyntaxError(script)
|
||||||
|
setSyntaxError(error)
|
||||||
|
if (error) return
|
||||||
|
onApply(script)
|
||||||
onClose()
|
onClose()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -986,4 +1018,18 @@ function ScriptBuilderDialog({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AsyncFunction = Object.getPrototypeOf(async function noop() {}).constructor as new (
|
||||||
|
...args: string[]
|
||||||
|
) => unknown
|
||||||
|
|
||||||
|
/** Script runtime'ın derleyebileceği bir gövde mi; değilse derleyicinin mesajı. */
|
||||||
|
const findSyntaxError = (script: string) => {
|
||||||
|
try {
|
||||||
|
new AsyncFunction(script)
|
||||||
|
return ''
|
||||||
|
} catch (error) {
|
||||||
|
return error instanceof SyntaxError ? error.message : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default ScriptBuilderDialog
|
export default ScriptBuilderDialog
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,11 @@ export interface ScriptDialect {
|
||||||
getTrigger?: (rule: ScriptRule) => string
|
getTrigger?: (rule: ScriptRule) => string
|
||||||
/** Kural kartında gösterilecek uyarı; örneğin eksik bağlam. */
|
/** Kural kartında gösterilecek uyarı; örneğin eksik bağlam. */
|
||||||
getRuleWarning?: (rule: ScriptRule) => string
|
getRuleWarning?: (rule: ScriptRule) => string
|
||||||
|
/**
|
||||||
|
* Serbest metin parametresinin kurala göre sabit seçenekleri (mantıksal alana yazılan değer için
|
||||||
|
* true/false). Seçenek dönerse dialog metin kutusu yerine seçici gösterir.
|
||||||
|
*/
|
||||||
|
getParamChoices?: (rule: ScriptRule, paramKey: string) => ScriptRecipeParam['choices']
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Kuralın bütün koşulları; ilki her zaman `condition`. */
|
/** Kuralın bütün koşulları; ilki her zaman `condition`. */
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ export interface ListFormWizardDto {
|
||||||
* Wizard adımlarında sorulmaz (MCP ile demo veri taşıyan ekranlar açar), düzenlemede korunur.
|
* Wizard adımlarında sorulmaz (MCP ile demo veri taşıyan ekranlar açar), düzenlemede korunur.
|
||||||
*/
|
*/
|
||||||
seedData: boolean
|
seedData: boolean
|
||||||
|
/** Ekrandan yapılan kayıtlar veri dosyasına yansısın mı; varsayılan kapalı, düzenlemede korunur. */
|
||||||
|
seedSync: boolean
|
||||||
|
|
||||||
defaultLayout: ListViewLayoutType
|
defaultLayout: ListViewLayoutType
|
||||||
grid: boolean
|
grid: boolean
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@
|
||||||
* yeniden derlenir.
|
* yeniden derlenir.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createElement } from 'react'
|
||||||
|
import Notification from '@/components/ui/Notification'
|
||||||
|
import toast from '@/components/ui/toast'
|
||||||
import apiService from '@/services/api.service'
|
import apiService from '@/services/api.service'
|
||||||
import { getLocalization } from '@/services/localization.service'
|
import { getLocalization } from '@/services/localization.service'
|
||||||
import { store } from '@/store'
|
import { store } from '@/store'
|
||||||
|
|
@ -69,9 +72,36 @@ const HELPER_PARAMS = [
|
||||||
* ------------------------------------------------------------------ */
|
* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
/** Script runtime React ağacının dışındadır; dil metni store'dan okunur. */
|
/** Script runtime React ağacının dışındadır; dil metni store'dan okunur. */
|
||||||
const translateRuntime = (key: string) => {
|
const translateRuntime = (key: string, params?: Record<string, string | number>) => {
|
||||||
const { texts, config } = store.getState().abpConfig
|
const { texts, config } = store.getState().abpConfig
|
||||||
return getLocalization(texts ?? {}, config?.localization?.defaultResourceName, key)
|
return getLocalization(texts ?? {}, config?.localization?.defaultResourceName, key, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Form her yeniden çizildiğinde aynı script tekrar çalışır; aynı hata kısa sürede bir kez gösterilir. */
|
||||||
|
const REPEATED_ERROR_WINDOW_MS = 5000
|
||||||
|
const lastReportedErrors = new Map<string, number>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script hatasını kullanıcıya gösterir. Konsola düşen hata kullanıcıya görünmez; bozuk bir kural
|
||||||
|
* sessizce çalışmadığında ekran "bozuk" görünür ama nedeni bilinmez.
|
||||||
|
*/
|
||||||
|
export const reportEditorScriptError = (field: string, error: unknown) => {
|
||||||
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
|
console.error('Editor script error:', field, error)
|
||||||
|
|
||||||
|
const key = `${field}|${message}`
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - (lastReportedErrors.get(key) ?? 0) < REPEATED_ERROR_WINDOW_MS) return
|
||||||
|
lastReportedErrors.set(key, now)
|
||||||
|
|
||||||
|
toast.push(
|
||||||
|
createElement(
|
||||||
|
Notification,
|
||||||
|
{ type: 'danger', duration: 6000 },
|
||||||
|
translateRuntime('::App.EditorScript.Error', { field, message }),
|
||||||
|
),
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Alan adını büyük/küçük harf duyarsız arar; SQL kolonları tutarsız gelebiliyor. */
|
/** Alan adını büyük/küçük harf duyarsız arar; SQL kolonları tutarsız gelebiliyor. */
|
||||||
|
|
@ -98,8 +128,7 @@ const toDate = (input: any): Date | null => {
|
||||||
return Number.isNaN(parsed.getTime()) ? null : parsed
|
return Number.isNaN(parsed.getTime()) ? null : parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
const startOfDayUtc = (date: Date) =>
|
const startOfDayUtc = (date: Date) => Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
|
||||||
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ *
|
/* ------------------------------------------------------------------ *
|
||||||
* Yardımcı API üretimi
|
* Yardımcı API üretimi
|
||||||
|
|
@ -156,9 +185,15 @@ const buildHelpers = ({
|
||||||
queueMicrotask(flush)
|
queueMicrotask(flush)
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeOne = (name: string, next: any) => {
|
const writeOne = (name: string, input: any) => {
|
||||||
if (!name) return
|
if (!name) return
|
||||||
const key = resolveKey(data, name) ?? name
|
const key = resolveKey(data, name) ?? name
|
||||||
|
// Mantıksal alana şablondan gelen 'false'/'0' metni dolu sayılırdı; mevcut değer
|
||||||
|
// mantıksalsa yazılan değer de mantıksala çevrilir.
|
||||||
|
const next =
|
||||||
|
typeof data[key] === 'boolean' && /^(true|false|1|0)$/i.test(String(input))
|
||||||
|
? /^(true|1)$/i.test(String(input))
|
||||||
|
: input
|
||||||
// Değer değişmediyse yazmıyoruz. Form açılışında (contentReady) çalışan
|
// Değer değişmediyse yazmıyoruz. Form açılışında (contentReady) çalışan
|
||||||
// script'ler setFormData'yı tetikleyip yeniden contentReady doğurabildiği
|
// script'ler setFormData'yı tetikleyip yeniden contentReady doğurabildiği
|
||||||
// için bu koruma olmadan sonsuz döngü oluşur.
|
// için bu koruma olmadan sonsuz döngü oluşur.
|
||||||
|
|
@ -191,8 +226,7 @@ const buildHelpers = ({
|
||||||
|
|
||||||
const clear = (...names: string[]) => set(Object.fromEntries(names.map((name) => [name, null])))
|
const clear = (...names: string[]) => set(Object.fromEntries(names.map((name) => [name, null])))
|
||||||
|
|
||||||
const readOnly = (name: string, flag: boolean = true) =>
|
const readOnly = (name: string, flag: boolean = true) => runtimeSetEditorReadOnly?.(name, !!flag)
|
||||||
runtimeSetEditorReadOnly?.(name, !!flag)
|
|
||||||
|
|
||||||
/** Lookup/GridBox ile seçilen kaydın bir kolonunu form alanına taşır. */
|
/** Lookup/GridBox ile seçilen kaydın bir kolonunu form alanına taşır. */
|
||||||
const copy = (sourcePath: string, targetName?: string) => {
|
const copy = (sourcePath: string, targetName?: string) => {
|
||||||
|
|
@ -230,13 +264,14 @@ const buildHelpers = ({
|
||||||
const tpl = (text: string) =>
|
const tpl = (text: string) =>
|
||||||
String(text ?? '')
|
String(text ?? '')
|
||||||
.replace(/\{value\}/g, () => String(e?.value ?? ''))
|
.replace(/\{value\}/g, () => String(e?.value ?? ''))
|
||||||
.replace(/\{selected\.([^}]+)\}/g, (_, key: string) => String(pickPath(selectedItem, key) ?? ''))
|
.replace(/\{selected\.([^}]+)\}/g, (_, key: string) =>
|
||||||
|
String(pickPath(selectedItem, key) ?? ''),
|
||||||
|
)
|
||||||
.replace(/\{([^}]+)\}/g, (_, key: string) => String(get(key) ?? ''))
|
.replace(/\{([^}]+)\}/g, (_, key: string) => String(get(key) ?? ''))
|
||||||
|
|
||||||
/** Geçerli alanı değişiklik öncesindeki değerine döndürür. */
|
/** Geçerli alanı değişiklik öncesindeki değerine döndürür. */
|
||||||
const revert = () => {
|
const revert = () => {
|
||||||
const previous =
|
const previous = e?.previousValue ?? pickPath(formData, field) ?? null
|
||||||
e?.previousValue ?? pickPath(formData, field) ?? null
|
|
||||||
writeOne(field, previous)
|
writeOne(field, previous)
|
||||||
const activeEditor = e?.component?.getEditor?.(field)
|
const activeEditor = e?.component?.getEditor?.(field)
|
||||||
if (activeEditor?.option) activeEditor.option('value', previous)
|
if (activeEditor?.option) activeEditor.option('value', previous)
|
||||||
|
|
@ -405,7 +440,15 @@ const compileScript = (script: string): CompiledScript => {
|
||||||
|
|
||||||
export const executeEditorScript = (script: string, context: EditorScriptRuntimeContext) => {
|
export const executeEditorScript = (script: string, context: EditorScriptRuntimeContext) => {
|
||||||
const { formData, e, editor, runtimeSetEditorReadOnly, setFormData } = context
|
const { formData, e, editor, runtimeSetEditorReadOnly, setFormData } = context
|
||||||
const compiled = compileScript(script)
|
const scriptField = String(editor?.dataField ?? e?.dataField ?? '')
|
||||||
|
|
||||||
|
let compiled: CompiledScript
|
||||||
|
try {
|
||||||
|
compiled = compileScript(script)
|
||||||
|
} catch (error) {
|
||||||
|
reportEditorScriptError(scriptField, error)
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
const { args, flush } = buildHelpers(context)
|
const { args, flush } = buildHelpers(context)
|
||||||
|
|
||||||
const baseArgs = [formData, e, editor, runtimeSetEditorReadOnly, setFormData]
|
const baseArgs = [formData, e, editor, runtimeSetEditorReadOnly, setFormData]
|
||||||
|
|
@ -415,8 +458,6 @@ export const executeEditorScript = (script: string, context: EditorScriptRuntime
|
||||||
flush()
|
flush()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
.catch((error: unknown) => {
|
.catch((error: unknown) => reportEditorScriptError(scriptField, error))
|
||||||
console.error('Editor script error:', error)
|
|
||||||
})
|
|
||||||
.finally(flush)
|
.finally(flush)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import { bool, object, string } from 'yup'
|
import { bool, object, string } from 'yup'
|
||||||
import { columnEditorTypeListOptions, sortDirectionOptions } from '../options'
|
import { columnEditorTypeListOptions, sortDirectionOptions } from '../options'
|
||||||
import { FormFieldEditProps } from './FormFields'
|
import { FormFieldEditProps } from './FormFields'
|
||||||
import { EditingFormDto, SortDirectionEnum } from '@/proxy/form/models'
|
import { DbTypeEnum, EditingFormDto, SortDirectionEnum } from '@/proxy/form/models'
|
||||||
import { FaCode } from 'react-icons/fa'
|
import { FaCode } from 'react-icons/fa'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import EditorScriptBuilderDialog from '../json-row-operations/EditorScriptBuilderDialog'
|
import EditorScriptBuilderDialog from '../json-row-operations/EditorScriptBuilderDialog'
|
||||||
|
|
@ -52,6 +52,16 @@ function FormFieldTabOptions({
|
||||||
value: field.fieldName,
|
value: field.fieldName,
|
||||||
label: field.captionName || field.fieldName,
|
label: field.captionName || field.fieldName,
|
||||||
}))
|
}))
|
||||||
|
const booleanFields = fields
|
||||||
|
.filter(
|
||||||
|
(field) =>
|
||||||
|
field.fieldName &&
|
||||||
|
(field.sourceDbType === DbTypeEnum.Boolean ||
|
||||||
|
field.dataType === 'boolean' ||
|
||||||
|
field.editorType2 === 'dxCheckBox' ||
|
||||||
|
field.editorType2 === 'dxSwitch'),
|
||||||
|
)
|
||||||
|
.map((field) => field.fieldName!)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik
|
<Formik
|
||||||
|
|
@ -189,6 +199,7 @@ function FormFieldTabOptions({
|
||||||
value={values.editorScript}
|
value={values.editorScript}
|
||||||
currentField={values.fieldName}
|
currentField={values.fieldName}
|
||||||
fields={editorScriptFields}
|
fields={editorScriptFields}
|
||||||
|
booleanFields={booleanFields}
|
||||||
onClose={() => setIsEditorScriptDialogOpen(false)}
|
onClose={() => setIsEditorScriptDialogOpen(false)}
|
||||||
onApply={(val) => setFieldValue('editorScript', val)}
|
onApply={(val) => setFieldValue('editorScript', val)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ type EditorScriptBuilderDialogProps = {
|
||||||
value?: string
|
value?: string
|
||||||
currentField?: string
|
currentField?: string
|
||||||
fields: SelectBoxOption[]
|
fields: SelectBoxOption[]
|
||||||
|
booleanFields?: string[]
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onApply: (value: string) => void
|
onApply: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
@ -22,6 +23,7 @@ function EditorScriptBuilderDialog({
|
||||||
value,
|
value,
|
||||||
currentField,
|
currentField,
|
||||||
fields,
|
fields,
|
||||||
|
booleanFields,
|
||||||
onClose,
|
onClose,
|
||||||
onApply,
|
onApply,
|
||||||
}: EditorScriptBuilderDialogProps) {
|
}: EditorScriptBuilderDialogProps) {
|
||||||
|
|
@ -30,9 +32,16 @@ function EditorScriptBuilderDialog({
|
||||||
// Çağıran her render'da yeni bir dizi verebilir; lehçe alan adlarına göre
|
// Çağıran her render'da yeni bir dizi verebilir; lehçe alan adlarına göre
|
||||||
// sabitlenmezse editör yazarken sıfırlanırdı.
|
// sabitlenmezse editör yazarken sıfırlanırdı.
|
||||||
const fieldKey = fields.map((field) => String(field.value || field.label || '')).join('|')
|
const fieldKey = fields.map((field) => String(field.value || field.label || '')).join('|')
|
||||||
|
const booleanKey = (booleanFields ?? []).join('|')
|
||||||
const dialect = useMemo(
|
const dialect = useMemo(
|
||||||
() => createFormScriptDialect({ currentField, fields: fieldKey.split('|'), translate }),
|
() =>
|
||||||
[currentField, fieldKey, translate],
|
createFormScriptDialect({
|
||||||
|
currentField,
|
||||||
|
fields: fieldKey.split('|'),
|
||||||
|
booleanFields: booleanKey.split('|'),
|
||||||
|
translate,
|
||||||
|
}),
|
||||||
|
[currentField, fieldKey, booleanKey, translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,8 @@ const snippets: ScriptSnippet[] = [
|
||||||
export type FormScriptDialectContext = {
|
export type FormScriptDialectContext = {
|
||||||
/** Form kolonları. */
|
/** Form kolonları. */
|
||||||
fields: string[]
|
fields: string[]
|
||||||
|
/** Mantıksal (onay kutusu / anahtar) alanlar; değer yazan kurallarda true/false seçtirilir. */
|
||||||
|
booleanFields?: string[]
|
||||||
/** Script'in bağlı olduğu alan; başlıkta gösterilir. */
|
/** Script'in bağlı olduğu alan; başlıkta gösterilir. */
|
||||||
currentField?: string
|
currentField?: string
|
||||||
/** `useLocalization` hook'undan gelen çeviri fonksiyonu. */
|
/** `useLocalization` hook'undan gelen çeviri fonksiyonu. */
|
||||||
|
|
@ -150,6 +152,11 @@ export const createFormScriptDialect = (context: FormScriptDialectContext): Scri
|
||||||
)
|
)
|
||||||
|
|
||||||
const asFormRule = (rule: ScriptRule) => rule as unknown as FormScriptRule
|
const asFormRule = (rule: ScriptRule) => rule as unknown as FormScriptRule
|
||||||
|
const booleanFields = new Set((context.booleanFields ?? []).map((field) => field.toLowerCase()))
|
||||||
|
const booleanChoices = [
|
||||||
|
{ value: 'true', label: t('::App.Platform.Yes') },
|
||||||
|
{ value: 'false', label: t('::App.Platform.No') },
|
||||||
|
]
|
||||||
|
|
||||||
// Tarif listesi hem "Kural ekle" seçicisinde hem de kural kartında kullanılıyor.
|
// Tarif listesi hem "Kural ekle" seçicisinde hem de kural kartında kullanılıyor.
|
||||||
// Tek bir çevrilmiş liste üretip ikisini de buradan besliyoruz; aksi halde kart
|
// Tek bir çevrilmiş liste üretip ikisini de buradan besliyoruz; aksi halde kart
|
||||||
|
|
@ -241,11 +248,16 @@ export const createFormScriptDialect = (context: FormScriptDialectContext): Scri
|
||||||
// Kosulsuz kuralda "Her zaman ->" oneki bilgi tasimiyor, yalnizca gurultu.
|
// Kosulsuz kuralda "Her zaman ->" oneki bilgi tasimiyor, yalnizca gurultu.
|
||||||
if (!conditions.length) return action
|
if (!conditions.length) return action
|
||||||
const joiner =
|
const joiner =
|
||||||
rule.join === 'or'
|
rule.join === 'or' ? ` ${t('::App.Platform.Or')} ` : ` ${t('::App.Platform.And')} `
|
||||||
? ` ${t('::App.Platform.Or')} `
|
|
||||||
: ` ${t('::App.Platform.And')} `
|
|
||||||
return `${conditions.join(joiner)} → ${action}`
|
return `${conditions.join(joiner)} → ${action}`
|
||||||
},
|
},
|
||||||
getTrigger: (rule) => effectiveTrigger(asFormRule(rule)),
|
getTrigger: (rule) => effectiveTrigger(asFormRule(rule)),
|
||||||
|
// Onay kutusuna metin ('0', 'Hayır') yazmak dolu sayılırdı; değer mantıksal sabit olarak seçilir.
|
||||||
|
getParamChoices: (rule, paramKey) =>
|
||||||
|
rule.recipe === 'setValue' &&
|
||||||
|
paramKey === 'text' &&
|
||||||
|
booleanFields.has(String(rule.params.target ?? '').toLowerCase())
|
||||||
|
? booleanChoices
|
||||||
|
: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,15 @@ export type TranslateFn = (key: string, params?: Record<string, string | number>
|
||||||
|
|
||||||
const q = (input: string) => JSON.stringify(input ?? '')
|
const q = (input: string) => JSON.stringify(input ?? '')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `true`/`false` mantıksal sabit olarak yazılır; metin (`tpl('false')`) onay kutusunu dolu sayardı.
|
||||||
|
* Diğer değerler şablondur. `EditorScriptRule.ValueLiteral` (C#) ile aynı.
|
||||||
|
*/
|
||||||
|
const valueLiteral = (text: string) => {
|
||||||
|
const trimmed = String(text ?? '').trim()
|
||||||
|
return /^(true|false)$/i.test(trimmed) ? trimmed.toLowerCase() : `tpl(${q(text)})`
|
||||||
|
}
|
||||||
|
|
||||||
const label = (input: string, fallback: string) => input || fallback
|
const label = (input: string, fallback: string) => input || fallback
|
||||||
|
|
||||||
const isNumericLiteral = (input: string) => /^-?\d+(\.\d+)?$/.test(String(input).trim())
|
const isNumericLiteral = (input: string) => /^-?\d+(\.\d+)?$/.test(String(input).trim())
|
||||||
|
|
@ -508,7 +517,7 @@ export const recipes: Recipe[] = [
|
||||||
help: 'App.ScriptBuilderText.Help',
|
help: 'App.ScriptBuilderText.Help',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
build: ({ params }) => `set(${q(params.target)}, tpl(${q(params.text)}))`,
|
build: ({ params }) => `set(${q(params.target)}, ${valueLiteral(params.text)})`,
|
||||||
describe: ({ params }, t) =>
|
describe: ({ params }, t) =>
|
||||||
t('::App.ScriptBuilderDescribe.SetValue', {
|
t('::App.ScriptBuilderDescribe.SetValue', {
|
||||||
target: label(params.target, t('::App.Listform.ListformField.Target')),
|
target: label(params.target, t('::App.Listform.ListformField.Target')),
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ const initialValues: ListFormWizardDto = {
|
||||||
confirmDelete: true,
|
confirmDelete: true,
|
||||||
allowDetail: false,
|
allowDetail: false,
|
||||||
seedData: false,
|
seedData: false,
|
||||||
|
seedSync: false,
|
||||||
defaultLayout: 'grid',
|
defaultLayout: 'grid',
|
||||||
grid: true,
|
grid: true,
|
||||||
card: true,
|
card: true,
|
||||||
|
|
@ -715,6 +716,7 @@ const Wizard = () => {
|
||||||
confirmDelete: w.confirmDelete ?? true,
|
confirmDelete: w.confirmDelete ?? true,
|
||||||
allowDetail: w.allowDetail ?? false,
|
allowDetail: w.allowDetail ?? false,
|
||||||
seedData: w.seedData ?? false,
|
seedData: w.seedData ?? false,
|
||||||
|
seedSync: w.seedSync ?? false,
|
||||||
defaultLayout: w.defaultLayout ?? 'grid',
|
defaultLayout: w.defaultLayout ?? 'grid',
|
||||||
grid: w.grid ?? initialValues.grid,
|
grid: w.grid ?? initialValues.grid,
|
||||||
card: w.card ?? initialValues.card,
|
card: w.card ?? initialValues.card,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Grid, Tree ve form sayfası aynı kuralları paylaşır.
|
* Grid, Tree ve form sayfası aynı kuralları paylaşır.
|
||||||
*/
|
*/
|
||||||
import type { ColumnFormatDto, GridDto } from '@/proxy/form/models'
|
import type { ColumnFormatDto, GridDto } from '@/proxy/form/models'
|
||||||
import { getActiveEditingForm } from './editingForm'
|
import { getActiveEditingForm, getEditingRow } from './editingForm'
|
||||||
|
|
||||||
type CascadeInfo = {
|
type CascadeInfo = {
|
||||||
parentFields: string[]
|
parentFields: string[]
|
||||||
|
|
@ -99,16 +99,6 @@ export const hasCascadeBehavior = (maps: CascadeMaps, fieldName: string) =>
|
||||||
maps.parentToChildrenMap.has(fieldName) ||
|
maps.parentToChildrenMap.has(fieldName) ||
|
||||||
maps.clearFieldsMap.has(fieldName)
|
maps.clearFieldsMap.has(fieldName)
|
||||||
|
|
||||||
const getEditingRow = (grid: any) => {
|
|
||||||
const rowKey = grid.option('editing.editRowKey')
|
|
||||||
return {
|
|
||||||
rowIndex: grid.getRowIndexByKey(rowKey) as number,
|
|
||||||
rowData: grid.getVisibleRows().find((row: any) => row.key === rowKey)?.data as
|
|
||||||
| Record<string, any>
|
|
||||||
| undefined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parent değiştikten sonra etkilenen çocuk editörlerin kilidini yeniler. */
|
/** Parent değiştikten sonra etkilenen çocuk editörlerin kilidini yeniler. */
|
||||||
const updateChildDisabledStates = (
|
const updateChildDisabledStates = (
|
||||||
grid: any,
|
grid: any,
|
||||||
|
|
|
||||||
|
|
@ -222,11 +222,25 @@ export const setFormEditorReadOnly = (form: any, field: string, readOnly: boolea
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** DataGrid/TreeList popup düzenleme formu; düzenleme denetleyicisinde yaşar. */
|
||||||
export const getActiveEditingForm = (component: any) => {
|
export const getActiveEditingForm = (component: any) => {
|
||||||
const editForm = component?.getView?.('editingView')?._editForm
|
const editForm = component?.getController?.('editing')?._editForm
|
||||||
return editForm?.getEditor || editForm?.itemOption ? editForm : undefined
|
return editForm?.getEditor || editForm?.itemOption ? editForm : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Düzenlenen satırın güncel verisi (`cellValue` ile yazılanlar dahil). Yeni satırın anahtarı
|
||||||
|
* nesnedir; `row.key === rowKey` eşleşmez, satır grid'in değerle eşleyen indeksinden okunur.
|
||||||
|
*/
|
||||||
|
export const getEditingRow = (grid: any) => {
|
||||||
|
const rowKey = grid?.option?.('editing.editRowKey')
|
||||||
|
const rowIndex: number =
|
||||||
|
rowKey !== undefined && rowKey !== null ? (grid?.getRowIndexByKey?.(rowKey) ?? -1) : -1
|
||||||
|
const rowData: Record<string, any> | undefined =
|
||||||
|
rowIndex >= 0 ? grid?.getVisibleRows?.()?.[rowIndex]?.data : undefined
|
||||||
|
return { rowKey, rowIndex, rowData }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form ilk açıldığında da çalışması gereken script'leri ayıklar.
|
* Form ilk açıldığında da çalışması gereken script'leri ayıklar.
|
||||||
*
|
*
|
||||||
|
|
@ -537,7 +551,11 @@ export const createFormItemMapper = ({
|
||||||
defaultEditorOptions.placeholder = translate('::' + colFormat.placeHolder)
|
defaultEditorOptions.placeholder = translate('::' + colFormat.placeHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof colFormat.defaultValue === 'string' && colFormat.defaultValue === '@AUTONUMBER' && mode === 'new') {
|
if (
|
||||||
|
typeof colFormat.defaultValue === 'string' &&
|
||||||
|
colFormat.defaultValue === '@AUTONUMBER' &&
|
||||||
|
mode === 'new'
|
||||||
|
) {
|
||||||
defaultEditorOptions.value = autoNumber()
|
defaultEditorOptions.value = autoNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* birebir tekrar ediyordu; buradan tek noktadan sağlanır.
|
* birebir tekrar ediyordu; buradan tek noktadan sağlanır.
|
||||||
*/
|
*/
|
||||||
import type { MutableRefObject } from 'react'
|
import type { MutableRefObject } from 'react'
|
||||||
import { executeEditorScript } from '@/utils/editorScriptRuntime'
|
import { executeEditorScript, reportEditorScriptError } from '@/utils/editorScriptRuntime'
|
||||||
import { attachFieldValueBroadcast } from '@/utils/phoneFormat'
|
import { attachFieldValueBroadcast } from '@/utils/phoneFormat'
|
||||||
import type { GridDto } from '@/proxy/form/models'
|
import type { GridDto } from '@/proxy/form/models'
|
||||||
import type { RowMode } from '../../form/types'
|
import type { RowMode } from '../../form/types'
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
} from './cascade'
|
} from './cascade'
|
||||||
import {
|
import {
|
||||||
getActiveEditingForm,
|
getActiveEditingForm,
|
||||||
|
getEditingRow,
|
||||||
getValueByField,
|
getValueByField,
|
||||||
resolveEditorButtons,
|
resolveEditorButtons,
|
||||||
setFormEditorReadOnly,
|
setFormEditorReadOnly,
|
||||||
|
|
@ -34,14 +35,6 @@ type EditingFormHandlerParams = {
|
||||||
editingFormInstanceRef: MutableRefObject<any>
|
editingFormInstanceRef: MutableRefObject<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const getEditingRowIndex = (grid: any) => {
|
|
||||||
const rowKey = grid?.option?.('editing.editRowKey')
|
|
||||||
return {
|
|
||||||
rowKey,
|
|
||||||
rowIndex: rowKey !== undefined ? (grid?.getRowIndexByKey?.(rowKey) ?? -1) : -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grid hücrelerine script tarafından yazılan değerleri uygular.
|
* Grid hücrelerine script tarafından yazılan değerleri uygular.
|
||||||
* CardView gibi `cellValue` API'si olmayan bileşenlerde sessizce atlanır;
|
* CardView gibi `cellValue` API'si olmayan bileşenlerde sessizce atlanır;
|
||||||
|
|
@ -52,6 +45,99 @@ const createGridCellWriter = (grid: any, rowIndex: number) => (newData: Record<s
|
||||||
Object.keys(newData).forEach((field) => grid.cellValue(rowIndex, field, newData[field]))
|
Object.keys(newData).forEach((field) => grid.cellValue(rowIndex, field, newData[field]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zincirde bir alanın aynı değerle ikinci kez tetiklenmesi döngüdür (A → B → A). Derinlik sınırı
|
||||||
|
* yoktur; değeri her turda değişen döngüyü (sayaç gibi) bu emniyet sınırı yakalar.
|
||||||
|
*/
|
||||||
|
const MAX_CHAIN_STEPS = 1000
|
||||||
|
|
||||||
|
const chainStep = (field: string, value: unknown) => {
|
||||||
|
try {
|
||||||
|
return `${field.toLowerCase()}=${JSON.stringify(value ?? null)}`
|
||||||
|
} catch {
|
||||||
|
return `${field.toLowerCase()}=${String(value)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChainedScriptParams = {
|
||||||
|
gridDto: GridDto | undefined
|
||||||
|
grid: any
|
||||||
|
form: any
|
||||||
|
before: Record<string, any>
|
||||||
|
after: Record<string, any>
|
||||||
|
sourceField?: string
|
||||||
|
/** Zincirin bu ana kadar çalıştırdığı alan=değer adımları; ilk çağrıda verilmez. */
|
||||||
|
visited?: Set<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script'in `set` ile yazdığı alanların kendi script'lerini çalıştırır; onların yazdıkları da aynı
|
||||||
|
* şekilde devam eder. Grid'e `cellValue` ile yazılan değer editörün değişiklik olayını tetiklemez;
|
||||||
|
* zincir olmadan "TrackInventory false olunca TrackingMethod salt okunur" gibi bağımlı kurallar
|
||||||
|
* kullanıcı alanı elle değiştirene kadar çalışmazdı. Form sayfası (`updateData`) olayı zaten
|
||||||
|
* tetiklediği için yalnızca Grid/Tree'de gerekir.
|
||||||
|
*/
|
||||||
|
const runChainedScripts = ({
|
||||||
|
gridDto,
|
||||||
|
grid,
|
||||||
|
form,
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
sourceField,
|
||||||
|
visited = new Set(sourceField ? [chainStep(sourceField, after[sourceField])] : []),
|
||||||
|
}: ChainedScriptParams) => {
|
||||||
|
if (!gridDto) return
|
||||||
|
|
||||||
|
const source = String(sourceField ?? '').toLowerCase()
|
||||||
|
const fields = getEditingFormFields(gridDto)
|
||||||
|
const changed = Object.keys(after).filter(
|
||||||
|
(key) => key.toLowerCase() !== source && !Object.is(before?.[key], after[key]),
|
||||||
|
)
|
||||||
|
|
||||||
|
changed.forEach((key) => {
|
||||||
|
const format = fields.find(
|
||||||
|
(field) => String(field.fieldName ?? '').toLowerCase() === key.toLowerCase(),
|
||||||
|
)
|
||||||
|
if (!format?.editorScript || !format.fieldName) return
|
||||||
|
|
||||||
|
const step = chainStep(format.fieldName, after[key])
|
||||||
|
if (visited.has(step) || visited.size >= MAX_CHAIN_STEPS) {
|
||||||
|
reportEditorScriptError(
|
||||||
|
format.fieldName,
|
||||||
|
new Error(`${[...visited].map((item) => item.split('=')[0]).join(' → ')} → ${key}`),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
visited.add(step)
|
||||||
|
|
||||||
|
const writeToGrid = createGridCellWriter(grid, getEditingRow(grid).rowIndex)
|
||||||
|
executeEditorScript(format.editorScript, {
|
||||||
|
formData: { ...after },
|
||||||
|
e: {
|
||||||
|
component: form,
|
||||||
|
dataField: format.fieldName,
|
||||||
|
value: after[key],
|
||||||
|
scriptEvent: 'valueChanged',
|
||||||
|
},
|
||||||
|
editor: { dataField: format.fieldName, component: grid },
|
||||||
|
runtimeSetEditorReadOnly: (field: string, flag: boolean) =>
|
||||||
|
setFormEditorReadOnly(form, field, flag),
|
||||||
|
setFormData: (next: Record<string, any>) => {
|
||||||
|
writeToGrid(next)
|
||||||
|
runChainedScripts({
|
||||||
|
gridDto,
|
||||||
|
grid,
|
||||||
|
form,
|
||||||
|
before: after,
|
||||||
|
after: next,
|
||||||
|
sourceField: format.fieldName,
|
||||||
|
visited,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const createEditingFormHandlers = ({
|
export const createEditingFormHandlers = ({
|
||||||
getGridDto,
|
getGridDto,
|
||||||
getGridInstance,
|
getGridInstance,
|
||||||
|
|
@ -89,9 +175,13 @@ export const createEditingFormHandlers = ({
|
||||||
return values
|
return values
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
|
// Popup formu her `cellValue` yazımında yeniden çizilir ve bu olay yeniden gelir; formun
|
||||||
|
// `formData`'sı açılıştaki nesnedir, güncel değerler grid'in düzenlenen satırındadır. Eski
|
||||||
|
// değerle çalışan script kendi verdiği readOnly kararını geri alırdı.
|
||||||
const formData = {
|
const formData = {
|
||||||
...editingFormDataRef.current,
|
...editingFormDataRef.current,
|
||||||
...(form?.option?.('formData') || {}),
|
...(form?.option?.('formData') || {}),
|
||||||
|
...(getEditingRow(grid).rowData || {}),
|
||||||
...editorValues,
|
...editorValues,
|
||||||
}
|
}
|
||||||
editingFormDataRef.current = { ...formData }
|
editingFormDataRef.current = { ...formData }
|
||||||
|
|
@ -99,7 +189,7 @@ export const createEditingFormHandlers = ({
|
||||||
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
|
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
|
||||||
setFormEditorReadOnly(form, field, readOnly)
|
setFormEditorReadOnly(form, field, readOnly)
|
||||||
|
|
||||||
const { rowIndex } = getEditingRowIndex(grid)
|
const { rowIndex } = getEditingRow(grid)
|
||||||
const writeToGrid = createGridCellWriter(grid, rowIndex)
|
const writeToGrid = createGridCellWriter(grid, rowIndex)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -152,7 +242,7 @@ export const createEditingFormHandlers = ({
|
||||||
setFormData: applyFormData,
|
setFormData: applyFormData,
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Script exec error on contentReady', field.fieldName, err)
|
reportEditorScriptError(field.fieldName!, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -177,7 +267,7 @@ export const createEditingFormHandlers = ({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const grid = getGridInstance()
|
const grid = getGridInstance()
|
||||||
const { rowIndex } = getEditingRowIndex(grid)
|
const { rowIndex } = getEditingRow(grid)
|
||||||
const formData = {
|
const formData = {
|
||||||
...(e.component?.option?.('formData') || {}),
|
...(e.component?.option?.('formData') || {}),
|
||||||
[e.dataField]: e.value,
|
[e.dataField]: e.value,
|
||||||
|
|
@ -196,10 +286,18 @@ export const createEditingFormHandlers = ({
|
||||||
e.component?.option?.('formData', newData)
|
e.component?.option?.('formData', newData)
|
||||||
editingFormDataRef.current = { ...newData }
|
editingFormDataRef.current = { ...newData }
|
||||||
writeToGrid(newData)
|
writeToGrid(newData)
|
||||||
|
runChainedScripts({
|
||||||
|
gridDto,
|
||||||
|
grid,
|
||||||
|
form: e.component,
|
||||||
|
before: formData,
|
||||||
|
after: newData,
|
||||||
|
sourceField: e.dataField,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Script exec error', formField.fieldName, err)
|
reportEditorScriptError(formField.fieldName!, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,6 +306,7 @@ export const createEditingFormHandlers = ({
|
||||||
|
|
||||||
type EditorValueChangedScriptParams = {
|
type EditorValueChangedScriptParams = {
|
||||||
editor: any
|
editor: any
|
||||||
|
gridDto: GridDto
|
||||||
editorScript: string
|
editorScript: string
|
||||||
fieldName?: string
|
fieldName?: string
|
||||||
editingFormInstanceRef: MutableRefObject<any>
|
editingFormInstanceRef: MutableRefObject<any>
|
||||||
|
|
@ -218,6 +317,7 @@ type EditorValueChangedScriptParams = {
|
||||||
*/
|
*/
|
||||||
const attachEditorValueChangedScript = ({
|
const attachEditorValueChangedScript = ({
|
||||||
editor,
|
editor,
|
||||||
|
gridDto,
|
||||||
editorScript,
|
editorScript,
|
||||||
fieldName,
|
fieldName,
|
||||||
editingFormInstanceRef,
|
editingFormInstanceRef,
|
||||||
|
|
@ -230,26 +330,31 @@ const attachEditorValueChangedScript = ({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const grid = editor.component
|
const grid = editor.component
|
||||||
const { rowKey, rowIndex } = getEditingRowIndex(grid)
|
const { rowIndex, rowData } = getEditingRow(grid)
|
||||||
const formData = {
|
const formData = { ...(rowData || {}), [editorDataField]: e.value }
|
||||||
...(grid.getVisibleRows().find((row: any) => row.key === rowKey)?.data || {}),
|
|
||||||
[editorDataField]: e.value,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const form = editingFormInstanceRef.current ?? getActiveEditingForm(grid)
|
||||||
|
const writeToGrid = createGridCellWriter(grid, rowIndex)
|
||||||
executeEditorScript(editorScript, {
|
executeEditorScript(editorScript, {
|
||||||
formData,
|
formData,
|
||||||
e: { ...e, scriptEvent: 'valueChanged' },
|
e: { ...e, scriptEvent: 'valueChanged' },
|
||||||
editor,
|
editor,
|
||||||
runtimeSetEditorReadOnly: (field: string, readOnly: boolean) =>
|
runtimeSetEditorReadOnly: (field: string, readOnly: boolean) =>
|
||||||
setFormEditorReadOnly(
|
setFormEditorReadOnly(form, field, readOnly),
|
||||||
editingFormInstanceRef.current ?? getActiveEditingForm(grid),
|
setFormData: (newData: Record<string, any>) => {
|
||||||
field,
|
writeToGrid(newData)
|
||||||
readOnly,
|
runChainedScripts({
|
||||||
),
|
gridDto,
|
||||||
setFormData: createGridCellWriter(grid, rowIndex),
|
grid,
|
||||||
|
form,
|
||||||
|
before: formData,
|
||||||
|
after: newData,
|
||||||
|
sourceField: editorDataField,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Script exec error', fieldName, err)
|
reportEditorScriptError(fieldName ?? '', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,9 +368,8 @@ const attachEditorButtonContext = (editor: any, mode: RowMode) => {
|
||||||
|
|
||||||
editor.editorOptions.buttons = withButtonContext(editor.editorOptions.buttons, () => {
|
editor.editorOptions.buttons = withButtonContext(editor.editorOptions.buttons, () => {
|
||||||
const grid = editor.component
|
const grid = editor.component
|
||||||
const { rowKey, rowIndex } = getEditingRowIndex(grid)
|
const { rowKey, rowIndex, rowData } = getEditingRow(grid)
|
||||||
const formData = grid.getVisibleRows().find((row: any) => row.key === rowKey)?.data || {}
|
return { formData: rowData || {}, fieldName: editor.dataField, mode, rowKey, rowIndex }
|
||||||
return { formData, fieldName: editor.dataField, mode, rowKey, rowIndex }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,6 +437,7 @@ export const prepareEditingEditor = ({
|
||||||
if (columnFormat?.editOrderNo != null && columnFormat.editorScript) {
|
if (columnFormat?.editOrderNo != null && columnFormat.editorScript) {
|
||||||
attachEditorValueChangedScript({
|
attachEditorValueChangedScript({
|
||||||
editor,
|
editor,
|
||||||
|
gridDto,
|
||||||
editorScript: columnFormat.editorScript,
|
editorScript: columnFormat.editorScript,
|
||||||
fieldName: columnFormat.fieldName,
|
fieldName: columnFormat.fieldName,
|
||||||
editingFormInstanceRef,
|
editingFormInstanceRef,
|
||||||
|
|
@ -395,19 +500,31 @@ export const createCustomEditorCellAdapter =
|
||||||
|
|
||||||
if (columnFormat?.editOrderNo == null || !columnFormat.editorScript) return
|
if (columnFormat?.editOrderNo == null || !columnFormat.editorScript) return
|
||||||
|
|
||||||
const { rowIndex } = getEditingRowIndex(grid)
|
const { rowIndex } = getEditingRow(grid)
|
||||||
const activeForm = editingFormInstanceRef.current ?? getActiveEditingForm(grid)
|
const activeForm = editingFormInstanceRef.current ?? getActiveEditingForm(grid)
|
||||||
|
const formData = { ...rowData, [fieldName]: value }
|
||||||
|
const writeToGrid = createGridCellWriter(grid, rowIndex)
|
||||||
try {
|
try {
|
||||||
executeEditorScript(columnFormat.editorScript, {
|
executeEditorScript(columnFormat.editorScript, {
|
||||||
formData: { ...rowData, [fieldName]: value },
|
formData,
|
||||||
e: { component: activeForm, dataField: fieldName, value, scriptEvent: 'valueChanged' },
|
e: { component: activeForm, dataField: fieldName, value, scriptEvent: 'valueChanged' },
|
||||||
editor: { dataField: fieldName, component: grid },
|
editor: { dataField: fieldName, component: grid },
|
||||||
runtimeSetEditorReadOnly: (field: string, flag: boolean) =>
|
runtimeSetEditorReadOnly: (field: string, flag: boolean) =>
|
||||||
setFormEditorReadOnly(activeForm, field, flag),
|
setFormEditorReadOnly(activeForm, field, flag),
|
||||||
setFormData: createGridCellWriter(grid, rowIndex),
|
setFormData: (newData: Record<string, any>) => {
|
||||||
|
writeToGrid(newData)
|
||||||
|
runChainedScripts({
|
||||||
|
gridDto,
|
||||||
|
grid,
|
||||||
|
form: activeForm,
|
||||||
|
before: formData,
|
||||||
|
after: newData,
|
||||||
|
sourceField: fieldName,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Script exec error', fieldName, err)
|
reportEditorScriptError(fieldName ?? '', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue