Satınalma Talepleri düzenlemeleri
This commit is contained in:
parent
6f472b15f0
commit
2d48626219
68 changed files with 3476 additions and 5273 deletions
|
|
@ -272,6 +272,7 @@ metnini biçimlendirmek için — sözlükte tipi yoktur, olduğu gibi bileşene
|
||||||
| `AllowAllDeleting` | `false` | Toplu silme düğmesi |
|
| `AllowAllDeleting` | `false` | Toplu silme düğmesi |
|
||||||
| `AllowDuplicate` | `false` | Kaydı kopyalayarak yeni kayıt |
|
| `AllowDuplicate` | `false` | Kaydı kopyalayarak yeni kayıt |
|
||||||
| `AllowDetail` | `false` | Satırda Detay düğmesi |
|
| `AllowDetail` | `false` | Satırda Detay düğmesi |
|
||||||
|
| `OpenDetailAfterInsert` | `false` | Popup'tan eklenen kayıt kaydedilince aynı sekmede kaydın form sayfasına (`/admin/form/{ListFormCode}/{Id}`) geçilir. Grid, Tree, Card ve Scheduler'da çalışır; anahtar insert yanıtından alınır, dönmezse geçilmez. Wizard cevabında da aynı adla vardır (`ListFormWizardDto.OpenDetailAfterInsert`); ne zaman açılacağı `lowcode` §4.2 Davranış tablosunda |
|
||||||
| `ConfirmDelete` | `true` | Silme onayı |
|
| `ConfirmDelete` | `true` | Silme onayı |
|
||||||
| `UseIcons` | `false` | Aksiyonlar ikon olarak |
|
| `UseIcons` | `false` | Aksiyonlar ikon olarak |
|
||||||
| `NewRowPosition` | `"viewportTop"` | `first` \| `last` \| `pageTop` \| `pageBottom` \| `viewportTop` \| `viewportBottom` |
|
| `NewRowPosition` | `"viewportTop"` | `first` \| `last` \| `pageTop` \| `pageBottom` \| `viewportTop` \| `viewportBottom` |
|
||||||
|
|
|
||||||
66
.github/instructions/lowcode.instructions.md
vendored
66
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -816,7 +816,8 @@ Kısa ve karar odaklı yaz; talebi tekrar anlatma.
|
||||||
kaydı açar, varsa ikinci kayıt açılmaz); `List` sekmesinde üçü açıktır; silme yalnızca talep
|
kaydı açar, varsa ikinci kayıt açılmaz); `List` sekmesinde üçü açıktır; silme yalnızca talep
|
||||||
istiyorsa. Alt form olarak üretilen ekranda (menüsüz ya da menülü) widget yoktur, düzenleme formu
|
istiyorsa. Alt form olarak üretilen ekranda (menüsüz ya da menülü) widget yoktur, düzenleme formu
|
||||||
tek gruplu ise grup başlığı yazılmaz
|
tek gruplu ise grup başlığı yazılmaz
|
||||||
(§4.3). Ayrıntı: §4.6.
|
(§4.3). Ayrıntı: §4.6. Sekmede satır girilen ana ekran için `OpenDetailAfterInsert` kararı
|
||||||
|
(kaydedince Detay'a geçiş) burada yazılır — başlık + kalem ekranında varsayılan `true`.
|
||||||
7. **Etkilenen kısımlar** — uygulamada dokunulacak her yer: yeni/değişen ekranlar, alt formlar,
|
7. **Etkilenen kısımlar** — uygulamada dokunulacak her yer: yeni/değişen ekranlar, alt formlar,
|
||||||
menü, yetkiler (`C/R/U/D` + varsa özel), dil anahtarları, widget'lar, iş akışı/bildirim, SEO (public
|
menü, yetkiler (`C/R/U/D` + varsa özel), dil anahtarları, widget'lar, iş akışı/bildirim, SEO (public
|
||||||
yol varsa), var olan ekranlara etkiler.
|
yol varsa), var olan ekranlara etkiler.
|
||||||
|
|
@ -1297,6 +1298,7 @@ atanmaz (admin dahil); ekranı görecek roller yetkiyi rol ekranından alır.
|
||||||
| `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`. |
|
| `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. |
|
| `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. |
|
||||||
|
| `OpenDetailAfterInsert` | `[ListForm]` Popup'tan **yeni** kayıt kaydedilince listede kalınmaz, aynı sekmede kaydın Detay formuna (`/admin/form/{ListFormCode}/{anahtar}`) geçilir (`EditingOptionDto.OpenDetailAfterInsert`; Grid, Tree, Card, Scheduler). Varsayılan `false`. **Analizde karar verilir:** alt form sekmesinde satır girilen başlık + kalem ekranında (talep + talep satırları, sipariş + sipariş kalemleri, masraf + masraf kalemleri) `true` yazılır — kalemler yalnız Detay'da girildiği için kullanıcı başlığı kaydedip doğrudan kalem girişine geçer; kullanıcı listede kalmak istediğini söylerse `false`. Alt formu olmayan listede ve alt form ekranının kendisinde `false`. `AllowDetail` ile birlikte anlamlıdır; `KeyFieldName` ve insert'in anahtarı döndürmesi şarttır (varsayılan insert döndürür). |
|
||||||
| `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. |
|
||||||
|
|
||||||
|
|
@ -1751,6 +1753,12 @@ ekrandan açılır.
|
||||||
Alan başına DevExtreme doğrulama kuralları dizisi. Wizard `IsRequired: true` alanlara `required`
|
Alan başına DevExtreme doğrulama kuralları dizisi. Wizard `IsRequired: true` alanlara `required`
|
||||||
kuralını kendi yazar; gerisi elle eklenir.
|
kuralını kendi yazar; gerisi elle eklenir.
|
||||||
|
|
||||||
|
Grid/Tree'de kendi şablonuyla çizilen editörlere (lookup'lı `dxTagBox`/`dxGridBox`, `dxImageUpload`,
|
||||||
|
`dxRating`) DevExtreme kolon kuralı uygulamaz (E1050); bu alanlara kolon kuralı verilmez, `required`
|
||||||
|
kuralı kayıttan önce istemcide denetlenir (`getTemplateEditorRequiredErrors`) ve boşsa kayıt
|
||||||
|
iptal edilip popup açık kalır. Bu editörlerde yalnızca `required` çalışır; diğer kural tipleri
|
||||||
|
gerekiyorsa sunucu tarafı kontrol yazılır.
|
||||||
|
|
||||||
| `Type` | Kullanılan alanlar |
|
| `Type` | Kullanılan alanlar |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `required` | `Message`, `Trim` |
|
| `required` | `Message`, `Trim` |
|
||||||
|
|
@ -1830,7 +1838,7 @@ cevaba yazılmaz.
|
||||||
"MenuUrl": "/admin/list/App.Mrp.Orders",
|
"MenuUrl": "/admin/list/App.Mrp.Orders",
|
||||||
"IsTenant": true, "IsBranch": false, "IsOrganizationUnit": false,
|
"IsTenant": true, "IsBranch": false, "IsOrganizationUnit": false,
|
||||||
"AllowAdding": true, "AllowUpdating": true, "AllowDeleting": true,
|
"AllowAdding": true, "AllowUpdating": true, "AllowDeleting": true,
|
||||||
"AllowDetail": false, "ConfirmDelete": true,
|
"AllowDetail": false, "OpenDetailAfterInsert": false, "ConfirmDelete": true,
|
||||||
"DefaultLayout": "grid",
|
"DefaultLayout": "grid",
|
||||||
"Grid": true, "Card": true, "Pivot": true, "Chart": true,
|
"Grid": true, "Card": true, "Pivot": true, "Chart": true,
|
||||||
"Tree": false, "Gantt": false, "Scheduler": false, "Todo": false,
|
"Tree": false, "Gantt": false, "Scheduler": false, "Todo": false,
|
||||||
|
|
@ -2281,7 +2289,26 @@ wizard'ınkinden **küçük** olmalıdır (§0.2).
|
||||||
|
|
||||||
### 4.7 İş akışı (onay süreci)
|
### 4.7 İş akışı (onay süreci)
|
||||||
|
|
||||||
`WorkflowDto` onay alanlarını tabloda karşılığı olan kolonlara bağlar:
|
`WorkflowDto` onay alanlarını tabloda karşılığı olan kolonlara bağlar. **İş akışlı her tablo** tablo
|
||||||
|
tasarımcısının Workflow şablonundaki (`WORKFLOW_COLUMNS`, `SqlTableDesignerDialog`) dört kolonu taşır ve
|
||||||
|
bu dördü dışında akış kolonu açılmaz:
|
||||||
|
|
||||||
|
| Kolon | SQL Server | PostgreSQL | `WorkflowDto` alanı |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ApprovalUserName` | `nvarchar(256) NULL` | `varchar(256) NULL` | `ApprovalUserFieldName` — sıradaki onaycı(lar)ın kullanıcı adı, `\|` ayrılmış |
|
||||||
|
| `ApprovalStatus` | `nvarchar(50) NULL` | `varchar(50) NULL` | `ApprovalStatusFieldName` — bulunulan düğümün başlığı; boş = akış başlamadı (taslak) |
|
||||||
|
| `ApprovalDate` | `datetime NULL` | `timestamp NULL` | `ApprovalDateFieldName` |
|
||||||
|
| `ApprovalDescription` | `nvarchar(200) NULL` | `varchar(200) NULL` | `ApprovalDescriptionFieldName` — karar açıklaması; reddin nedeni de buraya yazılır |
|
||||||
|
|
||||||
|
- Ayrı bir durum kolonu (`Status` vb.) açılmaz; kaydın durumu `ApprovalStatus`'tür ve yalnız akış yazar.
|
||||||
|
Widget/filtre sorguları düğüm başlığıyla süzer (`"ApprovalStatus" = N'Onaylandı'`).
|
||||||
|
- Onaylayan Guid kolonu (`ApproverId`) açılmaz; motor kullanıcı adı yazar.
|
||||||
|
- Red nedeni kolonu açılmaz: onay düğümünün `RejectReason` tanımı (aşağıda) nedeni listeden sorar ve
|
||||||
|
`ApprovalDescription`'a yazar.
|
||||||
|
- Dört alan popup düzenleme formuna konmaz (`EditGroupOrderNo`/`EditOrderNo` boş, ayrı "Onay" grubu
|
||||||
|
açılmaz); listede kolon olarak görünür (`Visible: true`).
|
||||||
|
|
||||||
|
| Alan | Tabloda karşılığı |
|
||||||
|
|
||||||
| Alan | Tabloda karşılığı |
|
| Alan | Tabloda karşılığı |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
|
@ -2290,7 +2317,7 @@ wizard'ınkinden **küçük** olmalıdır (§0.2).
|
||||||
| `ApprovalStatusFieldName` | Durum |
|
| `ApprovalStatusFieldName` | Durum |
|
||||||
| `ApprovalDescriptionFieldName` | Onay/ret açıklaması |
|
| `ApprovalDescriptionFieldName` | Onay/ret açıklaması |
|
||||||
| `ApprovalIsFilterUserName` | Liste yalnızca kullanıcının onayına düşenleri göstersin |
|
| `ApprovalIsFilterUserName` | Liste yalnızca kullanıcının onayına düşenleri göstersin |
|
||||||
| `ApprovalIsResetWorkflow` | Kayıt güncellenince akış başa dönsün |
|
| `ApprovalIsResetWorkflow` | Kayıt güncellenince akış başa dönsün. `ApprovalIsManualStart` açıksa sıfırlama akışı yeniden çalıştırmaz; kayıt taslağa döner ve akış başlat düğmesiyle başlar |
|
||||||
| `ApprovalIsManualStart` | Yeni kayıt akışı kendiliğinden başlatmasın; kayıt taslak kalır, akış toolbar'daki başlat düğmesiyle başlar. Onaya gönderilmiş başlığa satır eklemeyi engelleyen alt formu olan ekranlarda (talep + satırlar) açılır; kapalıyken Grid/Tree yeni kaydın ardından akışı başlatır |
|
| `ApprovalIsManualStart` | Yeni kayıt akışı kendiliğinden başlatmasın; kayıt taslak kalır, akış toolbar'daki başlat düğmesiyle başlar. Onaya gönderilmiş başlığa satır eklemeyi engelleyen alt formu olan ekranlarda (talep + satırlar) açılır; kapalıyken Grid/Tree yeni kaydın ardından akışı başlatır |
|
||||||
|
|
||||||
`Criteria` düğüm grafiğidir. `Kind` değerleri: **`Start`**, **`Compare`**, **`Approval`**,
|
`Criteria` düğüm grafiğidir. `Kind` değerleri: **`Start`**, **`Compare`**, **`Approval`**,
|
||||||
|
|
@ -2298,10 +2325,10 @@ wizard'ınkinden **küçük** olmalıdır (§0.2).
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
"WorkflowDto": {
|
"WorkflowDto": {
|
||||||
"ApprovalUserFieldName": "ApproverId",
|
"ApprovalUserFieldName": "ApprovalUserName",
|
||||||
"ApprovalDateFieldName": "ApprovalDate",
|
"ApprovalDateFieldName": "ApprovalDate",
|
||||||
"ApprovalStatusFieldName": "Status",
|
"ApprovalStatusFieldName": "ApprovalStatus",
|
||||||
"ApprovalDescriptionFieldName": "ApprovalNote",
|
"ApprovalDescriptionFieldName": "ApprovalDescription",
|
||||||
"ApprovalIsFilterUserName": true,
|
"ApprovalIsFilterUserName": true,
|
||||||
"ApprovalIsResetWorkflow": true,
|
"ApprovalIsResetWorkflow": true,
|
||||||
"Criteria": [
|
"Criteria": [
|
||||||
|
|
@ -2348,6 +2375,13 @@ Kurallar:
|
||||||
adımı, `RequiresManagerApproval`, `App.Hr.md` › İzin Yönetimi §5.6).
|
adımı, `RequiresManagerApproval`, `App.Hr.md` › İzin Yönetimi §5.6).
|
||||||
- `Approval` düğümü `NextOnApprove` / `NextOnReject`, `Start` ve `Inform` düğümleri
|
- `Approval` düğümü `NextOnApprove` / `NextOnReject`, `Start` ve `Inform` düğümleri
|
||||||
`NextOnStart` alanını kullanır. `End` düğümünün çıkışı yoktur.
|
`NextOnStart` alanını kullanır. `End` düğümünün çıkışı yoktur.
|
||||||
|
- **Red nedeni listesi:** `Approval` düğümü opsiyonel `RejectReason { TableName, ValueField, TextField,
|
||||||
|
TargetField, Required }` taşır (entity'de `RejectReasonJson`). Boşsa red yalnızca açıklama ister; doluysa
|
||||||
|
karar diyaloğu tablonun Id/metin kolonlarından seçim listeler (`reject-reasons` ucu; tablo ekranın veri
|
||||||
|
kaynağındadır, adlar katalogdan çözülür, `TenantId`/`IsDeleted`/`IsActive` kolonu varsa süzülür).
|
||||||
|
`Required` açıkken neden seçilmeden ret verilemez. Seçilen nedenin metni açıklamanın başına
|
||||||
|
(`ApprovalDescriptionFieldName`) ve iş akışı notuna, Id'si `TargetField` doluysa kaydın o kolonuna yazılır;
|
||||||
|
metin istemciden alınmaz, sunucu tablodan doğrular. Sıfırlama `TargetField` kolonlarını da temizler.
|
||||||
- **Kayda göre değişen onaycı:** `Approver` değeri `@Kolon` ise (ör. `@ManagerUserNames`) onaycı kaydın o
|
- **Kayda göre değişen onaycı:** `Approver` değeri `@Kolon` ise (ör. `@ManagerUserNames`) onaycı kaydın o
|
||||||
kolonundan okunur (`ListFormWorkflowAppService.ResolveNodeApprover`); kolon `|` ile ayrılmış kullanıcı
|
kolonundan okunur (`ListFormWorkflowAppService.ResolveNodeApprover`); kolon `|` ile ayrılmış kullanıcı
|
||||||
adı/e-posta taşır ve ekranın alanı olmalıdır (satır ListForm sorgusundan okunur; gizli alan yeter). Kolon
|
adı/e-posta taşır ve ekranın alanı olmalıdır (satır ListForm sorgusundan okunur; gizli alan yeter). Kolon
|
||||||
|
|
@ -2580,7 +2614,7 @@ bölümünü doldurur.** Keşif turu (§0.6) soruları da bu adımlarla bire bir
|
||||||
| 0 | Komponent Tipi | her zaman | `ComponentKind` (`0` List · `1` Custom) |
|
| 0 | Komponent Tipi | her zaman | `ComponentKind` (`0` List · `1` Custom) |
|
||||||
| 1 | Custom Component | yalnız Custom yolu | `CustomComponentName`, `MenuUrl` (bileşenin `RoutePath`'i) |
|
| 1 | Custom Component | yalnız Custom yolu | `CustomComponentName`, `MenuUrl` (bileşenin `RoutePath`'i) |
|
||||||
| 2 | Menü | her zaman | `WizardName`, `CreateMenu`, `MenuParentCode` (ekranda eksik seviyeler menü diyaloğuyla kurulur; MCP `MenuParentPath` yazar), `MenuCode`, `Language.MenuEn/Tr`, `PermissionGroupName` (+ görünen adlar), `MenuIcon`, `MenuOrder` |
|
| 2 | Menü | her zaman | `WizardName`, `CreateMenu`, `MenuParentCode` (ekranda eksik seviyeler menü diyaloğuyla kurulur; MCP `MenuParentPath` yazar), `MenuCode`, `Language.MenuEn/Tr`, `PermissionGroupName` (+ görünen adlar), `MenuIcon`, `MenuOrder` |
|
||||||
| 3 | Ayarlar | yalnız List yolu | `ListFormCode`, `DataSourceCode` (+`DataSourceConnectionString`), `SelectCommandType`, `SelectCommand`, `KeyFieldName`, `KeyFieldDbSourceType`, `IsTenant/IsBranch/IsOrganizationUnit`, `AllowAdding/Updating/Deleting`, `ConfirmDelete`, `AllowDetail`, `DefaultLayout` + 8 görünüm bayrağı (`SeedData` adımda sorulmaz, düzenlemede korunur), `Language.TitleEn/Tr`, `Language.DescEn/Tr` |
|
| 3 | Ayarlar | yalnız List yolu | `ListFormCode`, `DataSourceCode` (+`DataSourceConnectionString`), `SelectCommandType`, `SelectCommand`, `KeyFieldName`, `KeyFieldDbSourceType`, `IsTenant/IsBranch/IsOrganizationUnit`, `AllowAdding/Updating/Deleting`, `ConfirmDelete`, `AllowDetail`, `OpenDetailAfterInsert`, `DefaultLayout` + 8 görünüm bayrağı (`SeedData` adımda sorulmaz, düzenlemede korunur), `Language.TitleEn/Tr`, `Language.DescEn/Tr` |
|
||||||
| 4 | Alanlar | yalnız List | `Groups[]` (aşağıda) |
|
| 4 | Alanlar | yalnız List | `Groups[]` (aşağıda) |
|
||||||
| 5 | Alt Formlar | yalnız List | `SubForms[]` |
|
| 5 | Alt Formlar | yalnız List | `SubForms[]` |
|
||||||
| 6 | Widget'lar | yalnız List | `Widgets[]` |
|
| 6 | Widget'lar | yalnız List | `Widgets[]` |
|
||||||
|
|
@ -4970,18 +5004,18 @@ isteniyorsa geçilir.
|
||||||
|
|
||||||
**Adım adım:**
|
**Adım adım:**
|
||||||
|
|
||||||
1. **Tablo** — iş kolonları + §8.1 varsayılanları. Onay akışı için dört kolon şarttır:
|
1. **Tablo** — iş kolonları + §8.1 varsayılanları + §4.7'deki dört iş akışı kolonu
|
||||||
durum (`Status`), onaylayan (`ApproverId`), onay tarihi (`ApprovalDate`), açıklama
|
(`ApprovalUserName`, `ApprovalStatus`, `ApprovalDate`, `ApprovalDescription`; ayrı `Status` yok).
|
||||||
(`ApprovalNote`). Talep sahibi için de bir kolon (`EmployeeId`) bulunmalıdır.
|
Talep sahibi için de bir kolon (`EmployeeId`) bulunmalıdır.
|
||||||
2. **Wizard dosyası** — §4.4'teki iskelet:
|
2. **Wizard dosyası** — §4.4'teki iskelet:
|
||||||
- `IsTenant: true` (tabloda `TenantId` var), `IsDeletedField: true`, `IsCreatedField: true`
|
- `IsTenant: true` (tabloda `TenantId` var), `IsDeletedField: true`, `IsCreatedField: true`
|
||||||
- `MenuParentCode` = modül kökü (`App.Hr`); yoksa wizard yaratır, `Order = max + 1`
|
- `MenuParentCode` = modül kökü (`App.Hr`); yoksa wizard yaratır, `Order = max + 1`
|
||||||
- `PermissionGroupName` = `App.Hr`, EN + TR görünen adlarıyla
|
- `PermissionGroupName` = `App.Hr`, EN + TR görünen adlarıyla
|
||||||
- Görünümler: varsayılan dörtlü (`Grid` + `Card` + `Pivot` + `Chart`, `DefaultLayout: "grid"`);
|
- Görünümler: varsayılan dörtlü (`Grid` + `Card` + `Pivot` + `Chart`, `DefaultLayout: "grid"`);
|
||||||
durum akışı görsel isteniyorsa ek olarak `Todo: true` + `TodoOptionDto` (§9.8)
|
durum akışı görsel isteniyorsa ek olarak `Todo: true` + `TodoOptionDto` (§9.8)
|
||||||
3. **Alanlar** — `Groups` içinde iki grup mantıklıdır: "Talep" (tutar, tarih, açıklama) ve
|
3. **Alanlar** — düzenleme formunda yalnız "Talep" grubu (tutar, tarih, açıklama) bulunur. İş akışı
|
||||||
"Onay" (durum, onaylayan, onay tarihi, onay notu). Onay grubundaki alanlar
|
alanları (onay durumu, onaylayan, onay tarihi, onay açıklaması) forma konmaz, listede görünür;
|
||||||
`EditorOptions: "{\"readOnly\":true}"` ile kilitlenir; kullanıcı doldurmaz, akış doldurur.
|
akış doldurur (§4.7).
|
||||||
4. **Hesap ve varsayılanlar** — `EditorScript` ile:
|
4. **Hesap ve varsayılanlar** — `EditorScript` ile:
|
||||||
- Talep tarihi form açılışında bugün: `today` tarifi, tetikleyici `open`
|
- Talep tarihi form açılışında bugün: `today` tarifi, tetikleyici `open`
|
||||||
- Tutar × oran gibi hesaplar: `multiply` / `percent`
|
- Tutar × oran gibi hesaplar: `multiply` / `percent`
|
||||||
|
|
@ -5000,7 +5034,8 @@ isteniyorsa geçilir.
|
||||||
|
|
||||||
**Bu iskeletin türevleri:** izin talebi (tutar yerine gün sayısı, `days` tarifi), masraf talebi
|
**Bu iskeletin türevleri:** izin talebi (tutar yerine gün sayısı, `days` tarifi), masraf talebi
|
||||||
(alt form olarak masraf kalemleri → menüsüz wizard + `SubForms`), satın alma talebi (kalemler +
|
(alt form olarak masraf kalemleri → menüsüz wizard + `SubForms`), satın alma talebi (kalemler +
|
||||||
tedarikçi lookup'ı + iki kademeli onay).
|
tedarikçi lookup'ı + iki kademeli onay). Kalem taşıyan türevlerde ana ekran `AllowDetail: true` +
|
||||||
|
`OpenDetailAfterInsert: true` ile gelir: başlık kaydedilince kalem girişi için Detay açılır.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -5039,6 +5074,7 @@ Prompt ile üretim yaptıysan ek olarak:
|
||||||
- [ ] Açılan her görünümün zorunlu option alanı dolu (§9.8).
|
- [ ] Açılan her görünümün zorunlu option alanı dolu (§9.8).
|
||||||
- [ ] Alt form olarak üretilen her ekranda (menüsüz ya da menülü) `Widgets` boş (§4.6.1 istisna).
|
- [ ] Alt form olarak üretilen her ekranda (menüsüz ya da menülü) `Widgets` boş (§4.6.1 istisna).
|
||||||
- [ ] `AllowDetail: true` yalnızca `SubForms` taşıyan listede; alt formu olmayan liste ve alt form ekranının kendisi `false`.
|
- [ ] `AllowDetail: true` yalnızca `SubForms` taşıyan listede; alt formu olmayan liste ve alt form ekranının kendisi `false`.
|
||||||
|
- [ ] Başlık + kalem ekranında (kalemler alt form sekmesinde giriliyor) `OpenDetailAfterInsert: true`; kararı analiz yazdı (§4.2 Davranış).
|
||||||
- [ ] Menüsüz alt formun yetkisi ana ekranın (ya da bağlı olduğu alt formun) yetkisinin altında;
|
- [ ] Menüsüz alt formun yetkisi ana ekranın (ya da bağlı olduğu alt formun) yetkisinin altında;
|
||||||
`Form` sekmesinin hedefi `AllowAdding` + `AllowUpdating` açık (§4.6).
|
`Form` sekmesinin hedefi `AllowAdding` + `AllowUpdating` açık (§4.6).
|
||||||
- [ ] List ekranında en az 3 kartlık `Widgets` var (istisna değilse); kiracı tablosunda her kart
|
- [ ] List ekranında en az 3 kartlık `Widgets` var (istisna değilse); kiracı tablosunda her kart
|
||||||
|
|
|
||||||
|
|
@ -522,8 +522,9 @@ EditorScript = EditorScript.Build(
|
||||||
`FormTabWorkflow` ve `views/admin/listForm/workflow/WorkflowDesigner.tsx` üzerinden görsel
|
`FormTabWorkflow` ve `views/admin/listForm/workflow/WorkflowDesigner.tsx` üzerinden görsel
|
||||||
tasarlanır. Düğüm türleri koşul karşılaştırması (`CompareColumn`/`CompareOperator`/`CompareValue`)
|
tasarlanır. Düğüm türleri koşul karşılaştırması (`CompareColumn`/`CompareOperator`/`CompareValue`)
|
||||||
ve onay adımlarıdır; her adım için `NextOnTrue`, `NextOnFalse`, `NextOnApprove`, `NextOnReject`
|
ve onay adımlarıdır; her adım için `NextOnTrue`, `NextOnFalse`, `NextOnApprove`, `NextOnReject`
|
||||||
geçişleri tanımlanır. Onaycı birden fazla kişi olabilir. Formlar içinde iş akışına bağlı özel
|
geçişleri tanımlanır. Onaycı birden fazla kişi olabilir. Onay düğümü reddi yalnızca açıklamayla ya da
|
||||||
butonlar gösterilebilir.
|
bir tablodan seçilen nedenle (Id + metin kolonu) aldırabilir (`lowcode.instructions.md` §4 › Red nedeni
|
||||||
|
listesi). Formlar içinde iş akışına bağlı özel butonlar gösterilebilir.
|
||||||
|
|
||||||
### 7.6. Yeni ekran ekleme akışı (Wizard)
|
### 7.6. Yeni ekran ekleme akışı (Wizard)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ public class GridEditingDto
|
||||||
public bool AllowAllDeleting { get; set; } = false;
|
public bool AllowAllDeleting { get; set; } = false;
|
||||||
public bool AllowAdding { get; set; } = false;
|
public bool AllowAdding { get; set; } = false;
|
||||||
public bool AllowDuplicate { get; set; } = false;
|
public bool AllowDuplicate { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Popup'tan eklenen kayıt kaydedilince listede kalmak yerine kaydın detay formuna (form sayfası) geçilir.
|
||||||
|
/// </summary>
|
||||||
|
public bool OpenDetailAfterInsert { get; set; } = false;
|
||||||
public bool UseIcons { get; set; } = false;
|
public bool UseIcons { get; set; } = false;
|
||||||
public bool ConfirmDelete { get; set; } = true;
|
public bool ConfirmDelete { get; set; } = true;
|
||||||
/// <summary>Accepted Values: 'first' | 'last' | 'pageBottom' | 'pageTop' | 'viewportBottom' | 'viewportTop'
|
/// <summary>Accepted Values: 'first' | 'last' | 'pageBottom' | 'pageTop' | 'viewportBottom' | 'viewportTop'
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@ public class ListFormWizardDto
|
||||||
public bool AllowUpdating { get; set; }
|
public bool AllowUpdating { get; set; }
|
||||||
public bool AllowDeleting { get; set; }
|
public bool AllowDeleting { get; set; }
|
||||||
public bool AllowDetail { get; set; }
|
public bool AllowDetail { get; set; }
|
||||||
|
/// <summary>Popup'tan eklenen kayit kaydedilince detay formuna gecilir (<c>GridEditingDto.OpenDetailAfterInsert</c>).</summary>
|
||||||
|
public bool OpenDetailAfterInsert { get; set; }
|
||||||
public bool ConfirmDelete { get; set; }
|
public bool ConfirmDelete { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -341,7 +343,7 @@ public class ListFormWizardDto
|
||||||
dto.EditingOptionJson = WizardConsts.DefaultEditingOptionJson(
|
dto.EditingOptionJson = WizardConsts.DefaultEditingOptionJson(
|
||||||
titleLangKey, formWidth, formHeight,
|
titleLangKey, formWidth, formHeight,
|
||||||
AllowDeleting, AllowAdding, AllowUpdating,
|
AllowDeleting, AllowAdding, AllowUpdating,
|
||||||
ConfirmDelete, false, AllowDetail || subForms.Count > 0);
|
ConfirmDelete, false, AllowDetail || subForms.Count > 0, OpenDetailAfterInsert);
|
||||||
dto.DeleteFieldsDefaultValueJson = isDeletedField
|
dto.DeleteFieldsDefaultValueJson = isDeletedField
|
||||||
? WizardConsts.DefaultDeleteFieldsDefaultValueJson(KeyFieldDbSourceType)
|
? WizardConsts.DefaultDeleteFieldsDefaultValueJson(KeyFieldDbSourceType)
|
||||||
: WizardConsts.DefaultDeleteFieldsJsonOnlyId(KeyFieldDbSourceType);
|
: WizardConsts.DefaultDeleteFieldsJsonOnlyId(KeyFieldDbSourceType);
|
||||||
|
|
@ -498,6 +500,7 @@ public class ListFormWizardDto
|
||||||
AllowUpdating = editing.AllowUpdating;
|
AllowUpdating = editing.AllowUpdating;
|
||||||
AllowDeleting = editing.AllowDeleting;
|
AllowDeleting = editing.AllowDeleting;
|
||||||
AllowDetail = editing.AllowDetail;
|
AllowDetail = editing.AllowDetail;
|
||||||
|
OpenDetailAfterInsert = editing.OpenDetailAfterInsert;
|
||||||
ConfirmDelete = editing.ConfirmDelete;
|
ConfirmDelete = editing.ConfirmDelete;
|
||||||
|
|
||||||
TreeOptionDto = listForm.TreeOptionDto;
|
TreeOptionDto = listForm.TreeOptionDto;
|
||||||
|
|
|
||||||
|
|
@ -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.SeedSync),
|
nameof(ListFormWizardDto.AllowDetail), nameof(ListFormWizardDto.OpenDetailAfterInsert), 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),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
|
@ -20,5 +21,8 @@ public class CreateUpdateListFormWorkflowCriteriaDto
|
||||||
public int PositionX { get; set; }
|
public int PositionX { get; set; }
|
||||||
public int PositionY { get; set; }
|
public int PositionY { get; set; }
|
||||||
public List<CompareOutcomeDto> CompareOutcomes { get; set; } = [];
|
public List<CompareOutcomeDto> CompareOutcomes { get; set; } = [];
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public WorkflowRejectReasonDto RejectReason { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,8 @@ public class DecisionWorkflowDto
|
||||||
public string CurrentNodeId { get; set; }
|
public string CurrentNodeId { get; set; }
|
||||||
public bool Approved { get; set; }
|
public bool Approved { get; set; }
|
||||||
public string Note { get; set; }
|
public string Note { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Reddin listeden seçilen nedeni; düğümün <see cref="WorkflowRejectReasonDto.ValueField"/> değeri.</summary>
|
||||||
|
public string RejectReasonId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Threading.Tasks;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
@ -12,4 +13,5 @@ public interface IListFormWorkflowAppService : IApplicationService
|
||||||
Task<WorkflowRunResultDto> StartAsync(StartWorkflowDto input);
|
Task<WorkflowRunResultDto> StartAsync(StartWorkflowDto input);
|
||||||
Task<WorkflowRunResultDto> ResetAsync(StartWorkflowDto input);
|
Task<WorkflowRunResultDto> ResetAsync(StartWorkflowDto input);
|
||||||
Task<WorkflowRunResultDto> DecisionAsync(DecisionWorkflowDto input);
|
Task<WorkflowRunResultDto> DecisionAsync(DecisionWorkflowDto input);
|
||||||
|
Task<List<WorkflowRejectReasonItemDto>> GetRejectReasonsAsync(string listFormCode, string criteriaId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
@ -20,5 +21,8 @@ public class ListFormWorkflowCriteriaDto : EntityDto<string>
|
||||||
public int PositionX { get; set; }
|
public int PositionX { get; set; }
|
||||||
public int PositionY { get; set; }
|
public int PositionY { get; set; }
|
||||||
public List<CompareOutcomeDto> CompareOutcomes { get; set; } = [];
|
public List<CompareOutcomeDto> CompareOutcomes { get; set; } = [];
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public WorkflowRejectReasonDto RejectReason { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Onay düğümünde reddin bir listeden seçilen nedenle verilmesi. Tablo tanımlı değilse red yalnızca
|
||||||
|
/// açıklama ister; tanımlıysa karar diyaloğu tablonun <see cref="ValueField"/>/<see cref="TextField"/>
|
||||||
|
/// kolonlarından seçenek listeler.
|
||||||
|
/// </summary>
|
||||||
|
public class WorkflowRejectReasonDto
|
||||||
|
{
|
||||||
|
public string TableName { get; set; }
|
||||||
|
public string ValueField { get; set; }
|
||||||
|
public string TextField { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Seçilen nedenin değerinin yazılacağı kayıt kolonu; boşsa yalnızca not ve açıklamaya yazılır.</summary>
|
||||||
|
public string TargetField { get; set; }
|
||||||
|
|
||||||
|
public bool Required { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool IsConfigured =>
|
||||||
|
!string.IsNullOrWhiteSpace(TableName) &&
|
||||||
|
!string.IsNullOrWhiteSpace(ValueField) &&
|
||||||
|
!string.IsNullOrWhiteSpace(TextField);
|
||||||
|
|
||||||
|
/// <summary>Entity kolonu ile DTO arasındaki tek dönüşüm; yapılandırılmamış tanım <c>null</c> saklanır.</summary>
|
||||||
|
public static string Serialize(WorkflowRejectReasonDto value)
|
||||||
|
{
|
||||||
|
return value?.IsConfigured == true ? JsonSerializer.Serialize(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorkflowRejectReasonDto Deserialize(string json)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(json))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = JsonSerializer.Deserialize<WorkflowRejectReasonDto>(json);
|
||||||
|
return value?.IsConfigured == true ? value : null;
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
public class WorkflowRejectReasonItemDto
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Http;
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
using Sozsoft.Platform.ListForms.Workflow;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
|
|
@ -486,152 +487,10 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
|
||||||
return await sequenceManager.GetNextSequenceAsync(SequenceName);
|
return await sequenceManager.GetNextSequenceAsync(SequenceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Workflow kriterlerini alma
|
|
||||||
private async Task<List<ListFormWorkflowCriteriaDto>> GetWorkflowCriteriaAsync(string listFormCode)
|
private async Task<List<ListFormWorkflowCriteriaDto>> GetWorkflowCriteriaAsync(string listFormCode)
|
||||||
{
|
{
|
||||||
var criteria = await listFormWorkflowRepository.GetListAsync(x => x.ListFormCode == listFormCode);
|
return ListFormWorkflowCriteriaMapper.MapOrdered(
|
||||||
return OrderWorkflowCriteria(criteria.Select(MapWorkflowCriteria).ToList());
|
await listFormWorkflowRepository.GetListAsync(x => x.ListFormCode == listFormCode));
|
||||||
}
|
|
||||||
|
|
||||||
private static List<ListFormWorkflowCriteriaDto> OrderWorkflowCriteria(List<ListFormWorkflowCriteriaDto> criteria)
|
|
||||||
{
|
|
||||||
if (criteria.Count <= 1)
|
|
||||||
{
|
|
||||||
return criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
var criteriaById = criteria.ToDictionary(x => x.Id);
|
|
||||||
var ordered = new List<ListFormWorkflowCriteriaDto>();
|
|
||||||
var visited = new HashSet<string>();
|
|
||||||
var visiting = new HashSet<string>();
|
|
||||||
|
|
||||||
var roots = criteria
|
|
||||||
.Where(x => x.Kind == "Start")
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (roots.Count == 0)
|
|
||||||
{
|
|
||||||
var referencedIds = criteria
|
|
||||||
.SelectMany(GetWorkflowTargetIds)
|
|
||||||
.Where(criteriaById.ContainsKey)
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
roots = criteria
|
|
||||||
.Where(x => !referencedIds.Contains(x.Id))
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var root in roots)
|
|
||||||
{
|
|
||||||
VisitWorkflowCriteria(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in criteria.OrderBy(x => x.Id))
|
|
||||||
{
|
|
||||||
VisitWorkflowCriteria(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ordered;
|
|
||||||
|
|
||||||
void VisitWorkflowCriteria(ListFormWorkflowCriteriaDto item)
|
|
||||||
{
|
|
||||||
if (visited.Contains(item.Id) || visiting.Contains(item.Id))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
visiting.Add(item.Id);
|
|
||||||
ordered.Add(item);
|
|
||||||
|
|
||||||
foreach (var targetId in GetWorkflowTargetIds(item))
|
|
||||||
{
|
|
||||||
if (criteriaById.TryGetValue(targetId, out var target))
|
|
||||||
{
|
|
||||||
VisitWorkflowCriteria(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visiting.Remove(item.Id);
|
|
||||||
visited.Add(item.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<string> GetWorkflowTargetIds(ListFormWorkflowCriteriaDto criteria)
|
|
||||||
{
|
|
||||||
if (!criteria.NextOnStart.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var outcome in criteria.CompareOutcomes ?? [])
|
|
||||||
{
|
|
||||||
if (!outcome.TargetId.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return outcome.TargetId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnTrue.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnFalse.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnFalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnApprove.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnApprove;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnReject.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnReject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ListFormWorkflowCriteriaDto MapWorkflowCriteria(ListFormWorkflow criteria)
|
|
||||||
{
|
|
||||||
return new ListFormWorkflowCriteriaDto
|
|
||||||
{
|
|
||||||
Id = criteria.Id,
|
|
||||||
ListFormCode = criteria.ListFormCode,
|
|
||||||
Kind = criteria.Kind,
|
|
||||||
Title = criteria.Title,
|
|
||||||
CompareColumn = criteria.CompareColumn,
|
|
||||||
CompareOperator = criteria.CompareOperator,
|
|
||||||
CompareValue = criteria.CompareValue,
|
|
||||||
Approver = criteria.Approver,
|
|
||||||
NextOnStart = criteria.NextOnStart,
|
|
||||||
NextOnTrue = criteria.NextOnTrue,
|
|
||||||
NextOnFalse = criteria.NextOnFalse,
|
|
||||||
NextOnApprove = criteria.NextOnApprove,
|
|
||||||
NextOnReject = criteria.NextOnReject,
|
|
||||||
PositionX = criteria.PositionX,
|
|
||||||
PositionY = criteria.PositionY,
|
|
||||||
CompareOutcomes = DeserializeCompareOutcomes(criteria.CompareOutcomesJson)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<CompareOutcomeDto> DeserializeCompareOutcomes(string json)
|
|
||||||
{
|
|
||||||
if (json.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<List<CompareOutcomeDto>>(json) ?? [];
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Sozsoft.Platform.Data.Seeds;
|
using Sozsoft.Platform.Data.Seeds;
|
||||||
|
using Sozsoft.Platform.DynamicData;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.ListForms.Select;
|
using Sozsoft.Platform.ListForms.Select;
|
||||||
|
|
@ -43,6 +44,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
private readonly IListFormAuthorizationManager authManager;
|
private readonly IListFormAuthorizationManager authManager;
|
||||||
private readonly IListFormSelectAppService listFormSelectAppService;
|
private readonly IListFormSelectAppService listFormSelectAppService;
|
||||||
private readonly IQueryManager queryManager;
|
private readonly IQueryManager queryManager;
|
||||||
|
private readonly IDynamicDataManager dynamicDataManager;
|
||||||
private readonly IdentityUserManager identityUserManager;
|
private readonly IdentityUserManager identityUserManager;
|
||||||
private readonly ISozsoftEmailSender erpEmailSender;
|
private readonly ISozsoftEmailSender erpEmailSender;
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
@ -55,6 +57,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
IListFormAuthorizationManager authManager,
|
IListFormAuthorizationManager authManager,
|
||||||
IListFormSelectAppService listFormSelectAppService,
|
IListFormSelectAppService listFormSelectAppService,
|
||||||
IQueryManager queryManager,
|
IQueryManager queryManager,
|
||||||
|
IDynamicDataManager dynamicDataManager,
|
||||||
IdentityUserManager identityUserManager,
|
IdentityUserManager identityUserManager,
|
||||||
ISozsoftEmailSender erpEmailSender,
|
ISozsoftEmailSender erpEmailSender,
|
||||||
ISettingProvider settingProvider,
|
ISettingProvider settingProvider,
|
||||||
|
|
@ -66,6 +69,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
this.listFormSelectAppService = listFormSelectAppService;
|
this.listFormSelectAppService = listFormSelectAppService;
|
||||||
this.queryManager = queryManager;
|
this.queryManager = queryManager;
|
||||||
|
this.dynamicDataManager = dynamicDataManager;
|
||||||
this.identityUserManager = identityUserManager;
|
this.identityUserManager = identityUserManager;
|
||||||
this.erpEmailSender = erpEmailSender;
|
this.erpEmailSender = erpEmailSender;
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
|
|
@ -75,118 +79,13 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
[HttpGet("criteria")]
|
[HttpGet("criteria")]
|
||||||
public async Task<ListFormWorkflowStateDto> GetCriteriaAsync(string? listFormCode = null)
|
public async Task<ListFormWorkflowStateDto> GetCriteriaAsync(string? listFormCode = null)
|
||||||
{
|
{
|
||||||
var code = listFormCode;
|
|
||||||
var criteria = (await criteriaRepository.GetListAsync(x => x.ListFormCode == code))
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return new ListFormWorkflowStateDto
|
return new ListFormWorkflowStateDto
|
||||||
{
|
{
|
||||||
Criteria = OrderWorkflowCriteria(criteria.Select(MapCriteria).ToList())
|
Criteria = ListFormWorkflowCriteriaMapper.MapOrdered(
|
||||||
|
await criteriaRepository.GetListAsync(x => x.ListFormCode == listFormCode))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ListFormWorkflowCriteriaDto> OrderWorkflowCriteria(List<ListFormWorkflowCriteriaDto> criteria)
|
|
||||||
{
|
|
||||||
if (criteria.Count <= 1)
|
|
||||||
{
|
|
||||||
return criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
var criteriaById = criteria.ToDictionary(x => x.Id);
|
|
||||||
var ordered = new List<ListFormWorkflowCriteriaDto>();
|
|
||||||
var visited = new HashSet<string>();
|
|
||||||
var visiting = new HashSet<string>();
|
|
||||||
|
|
||||||
var roots = criteria
|
|
||||||
.Where(x => x.Kind == "Start")
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (roots.Count == 0)
|
|
||||||
{
|
|
||||||
var referencedIds = criteria
|
|
||||||
.SelectMany(GetWorkflowTargetIds)
|
|
||||||
.Where(criteriaById.ContainsKey)
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
roots = criteria
|
|
||||||
.Where(x => !referencedIds.Contains(x.Id))
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var root in roots)
|
|
||||||
{
|
|
||||||
VisitWorkflowCriteria(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in criteria.OrderBy(x => x.Id))
|
|
||||||
{
|
|
||||||
VisitWorkflowCriteria(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ordered;
|
|
||||||
|
|
||||||
void VisitWorkflowCriteria(ListFormWorkflowCriteriaDto item)
|
|
||||||
{
|
|
||||||
if (visited.Contains(item.Id) || visiting.Contains(item.Id))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
visiting.Add(item.Id);
|
|
||||||
ordered.Add(item);
|
|
||||||
|
|
||||||
foreach (var targetId in GetWorkflowTargetIds(item))
|
|
||||||
{
|
|
||||||
if (criteriaById.TryGetValue(targetId, out var target))
|
|
||||||
{
|
|
||||||
VisitWorkflowCriteria(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visiting.Remove(item.Id);
|
|
||||||
visited.Add(item.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<string> GetWorkflowTargetIds(ListFormWorkflowCriteriaDto criteria)
|
|
||||||
{
|
|
||||||
if (!criteria.NextOnStart.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var outcome in criteria.CompareOutcomes ?? [])
|
|
||||||
{
|
|
||||||
if (!outcome.TargetId.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return outcome.TargetId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnTrue.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnFalse.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnFalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnApprove.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnApprove;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!criteria.NextOnReject.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
yield return criteria.NextOnReject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("criteria")]
|
[HttpPost("criteria")]
|
||||||
public async Task<ListFormWorkflowCriteriaDto> SaveCriteriaAsync(CreateUpdateListFormWorkflowCriteriaDto input)
|
public async Task<ListFormWorkflowCriteriaDto> SaveCriteriaAsync(CreateUpdateListFormWorkflowCriteriaDto input)
|
||||||
{
|
{
|
||||||
|
|
@ -216,7 +115,10 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
criteria.PositionX = input.PositionX <= 0 ? 32 : input.PositionX;
|
criteria.PositionX = input.PositionX <= 0 ? 32 : input.PositionX;
|
||||||
criteria.PositionY = input.PositionY <= 0 ? 150 : input.PositionY;
|
criteria.PositionY = input.PositionY <= 0 ? 150 : input.PositionY;
|
||||||
var outcomes = NormalizeCompareOutcomeTargets(code, input.CompareOutcomes);
|
var outcomes = NormalizeCompareOutcomeTargets(code, input.CompareOutcomes);
|
||||||
criteria.CompareOutcomesJson = SerializeCompareOutcomes(outcomes);
|
criteria.CompareOutcomesJson = ListFormWorkflowCriteriaMapper.SerializeCompareOutcomes(outcomes);
|
||||||
|
criteria.RejectReasonJson = criteria.Kind == "Approval"
|
||||||
|
? WorkflowRejectReasonDto.Serialize(NormalizeRejectReason(input.RejectReason))
|
||||||
|
: null;
|
||||||
|
|
||||||
if (criteria.Kind == "Compare" && outcomes.Count > 0)
|
if (criteria.Kind == "Compare" && outcomes.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -233,7 +135,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
await criteriaRepository.UpdateAsync(criteria, autoSave: true);
|
await criteriaRepository.UpdateAsync(criteria, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapCriteria(criteria);
|
return ListFormWorkflowCriteriaMapper.Map(criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("criteria/{id}")]
|
[HttpDelete("criteria/{id}")]
|
||||||
|
|
@ -272,7 +174,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
start.NextOnStart = compare.Id;
|
start.NextOnStart = compare.Id;
|
||||||
compare.NextOnTrue = approval.Id;
|
compare.NextOnTrue = approval.Id;
|
||||||
compare.NextOnFalse = inform.Id;
|
compare.NextOnFalse = inform.Id;
|
||||||
compare.CompareOutcomesJson = SerializeCompareOutcomes([
|
compare.CompareOutcomesJson = ListFormWorkflowCriteriaMapper.SerializeCompareOutcomes([
|
||||||
new CompareOutcomeDto
|
new CompareOutcomeDto
|
||||||
{
|
{
|
||||||
Label = "Onay gerekir",
|
Label = "Onay gerekir",
|
||||||
|
|
@ -365,6 +267,13 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
{
|
{
|
||||||
clear[context.Workflow.ApprovalDescriptionFieldName] = null;
|
clear[context.Workflow.ApprovalDescriptionFieldName] = null;
|
||||||
}
|
}
|
||||||
|
foreach (var targetField in context.Criteria
|
||||||
|
.Select(x => WorkflowRejectReasonDto.Deserialize(x.RejectReasonJson)?.TargetField)
|
||||||
|
.Where(x => !x.IsNullOrWhiteSpace())
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
clear[targetField!] = null;
|
||||||
|
}
|
||||||
|
|
||||||
await UpdateRowAsync(context, clear);
|
await UpdateRowAsync(context, clear);
|
||||||
MergeRowValues(context.Row, clear);
|
MergeRowValues(context.Row, clear);
|
||||||
|
|
@ -375,7 +284,11 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.ResetStep", "Reset Step"), LocalizeNodeTitle(previousStatus)));
|
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.ResetStep", "Reset Step"), LocalizeNodeTitle(previousStatus)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await RunUntilWaitAsync(context, start);
|
// Manuel başlatılan akışta sıfırlama kaydı taslağa döndürür; akış yeniden yalnızca
|
||||||
|
// "İş Akışı Başlat" ile başlar (durum alanı boş kaldığı için düğme yeniden açılır).
|
||||||
|
var result = context.Workflow.ApprovalIsManualStart
|
||||||
|
? ToRunResult(context, null, waitingApproval: false, completed: false)
|
||||||
|
: await RunUntilWaitAsync(context, start);
|
||||||
await InsertWorkflowNoteAsync(
|
await InsertWorkflowNoteAsync(
|
||||||
context,
|
context,
|
||||||
$"{Localize("App.WorkflowNote.SubjectReset", "Workflow Reset")}: {LocalizeNodeTitle(start.Title)}",
|
$"{Localize("App.WorkflowNote.SubjectReset", "Workflow Reset")}: {LocalizeNodeTitle(start.Title)}",
|
||||||
|
|
@ -395,6 +308,19 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return await DecisionSingleAsync(input, input.Keys);
|
return await DecisionSingleAsync(input, input.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("reject-reasons")]
|
||||||
|
public async Task<List<WorkflowRejectReasonItemDto>> GetRejectReasonsAsync(string listFormCode, string criteriaId)
|
||||||
|
{
|
||||||
|
await EnsureCanUpdateAsync(listFormCode);
|
||||||
|
|
||||||
|
var criteria = await criteriaRepository.FindAsync(criteriaId);
|
||||||
|
var reason = criteria?.ListFormCode == listFormCode
|
||||||
|
? WorkflowRejectReasonDto.Deserialize(criteria.RejectReasonJson)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return reason == null ? [] : await GetRejectReasonItemsAsync(listFormCode, reason);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<WorkflowRunResultDto> DecisionSingleAsync(DecisionWorkflowDto input, object[]? keys)
|
private async Task<WorkflowRunResultDto> DecisionSingleAsync(DecisionWorkflowDto input, object[]? keys)
|
||||||
{
|
{
|
||||||
var context = await CreateRunContextAsync(input.ListFormCode, keys);
|
var context = await CreateRunContextAsync(input.ListFormCode, keys);
|
||||||
|
|
@ -428,6 +354,10 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
throw new UserFriendlyException("Bu kayıt için onay yetkiniz yok.");
|
throw new UserFriendlyException("Bu kayıt için onay yetkiniz yok.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rejectReasonConfig = input.Approved ? null : WorkflowRejectReasonDto.Deserialize(current.RejectReasonJson);
|
||||||
|
var rejectReason = await ResolveRejectReasonAsync(context.ListFormCode, rejectReasonConfig, input.RejectReasonId);
|
||||||
|
var note = input.Note?.Trim() ?? string.Empty;
|
||||||
|
|
||||||
var update = new Dictionary<string, object?>();
|
var update = new Dictionary<string, object?>();
|
||||||
if (!context.Workflow.ApprovalUserFieldName.IsNullOrWhiteSpace())
|
if (!context.Workflow.ApprovalUserFieldName.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
|
|
@ -439,9 +369,14 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
}
|
}
|
||||||
if (!context.Workflow.ApprovalDescriptionFieldName.IsNullOrWhiteSpace())
|
if (!context.Workflow.ApprovalDescriptionFieldName.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
update[context.Workflow.ApprovalDescriptionFieldName] = input.Note ?? string.Empty;
|
update[context.Workflow.ApprovalDescriptionFieldName] =
|
||||||
|
string.Join(" - ", new[] { rejectReason?.Text, note }.Where(x => !x.IsNullOrWhiteSpace()));
|
||||||
context.UserUpdatedFields.Add(context.Workflow.ApprovalDescriptionFieldName);
|
context.UserUpdatedFields.Add(context.Workflow.ApprovalDescriptionFieldName);
|
||||||
}
|
}
|
||||||
|
if (rejectReason != null && !rejectReasonConfig!.TargetField.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
update[rejectReasonConfig.TargetField] = rejectReason.Id;
|
||||||
|
}
|
||||||
|
|
||||||
if (update.Count > 0)
|
if (update.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -451,7 +386,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject);
|
var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject);
|
||||||
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.DecisionBy", "Decision By"), ResolveCurrentUserDisplayName()));
|
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.DecisionBy", "Decision By"), ResolveCurrentUserDisplayName()));
|
||||||
AddWorkflowDecisionRows(context, current, input.Approved, input.Note ?? string.Empty);
|
AddWorkflowDecisionRows(context, current, input.Approved, rejectReason?.Text, note);
|
||||||
var result = await RunUntilWaitAsync(context, next);
|
var result = await RunUntilWaitAsync(context, next);
|
||||||
var decisionSubject = input.Approved
|
var decisionSubject = input.Approved
|
||||||
? Localize("App.WorkflowNote.SubjectApproved", "Workflow Approved")
|
? Localize("App.WorkflowNote.SubjectApproved", "Workflow Approved")
|
||||||
|
|
@ -493,10 +428,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
private async Task<WorkflowRunContext> CreateRunContextAsync(string listFormCode, object[]? keys)
|
private async Task<WorkflowRunContext> CreateRunContextAsync(string listFormCode, object[]? keys)
|
||||||
{
|
{
|
||||||
var code = listFormCode;
|
var code = listFormCode;
|
||||||
if (!await authManager.CanAccess(code, AuthorizationTypeEnum.Update))
|
await EnsureCanUpdateAsync(code);
|
||||||
{
|
|
||||||
throw new UserFriendlyException("Workflow işlemi için güncelleme yetkiniz yok.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var listForm = await listFormManager.GetUserListForm(code);
|
var listForm = await listFormManager.GetUserListForm(code);
|
||||||
var workflow = DeserializeWorkflow(listForm.WorkflowJson);
|
var workflow = DeserializeWorkflow(listForm.WorkflowJson);
|
||||||
|
|
@ -805,13 +737,15 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
WorkflowRunContext context,
|
WorkflowRunContext context,
|
||||||
ListFormWorkflow current,
|
ListFormWorkflow current,
|
||||||
bool approved,
|
bool approved,
|
||||||
|
string? rejectReason,
|
||||||
string description)
|
string description)
|
||||||
{
|
{
|
||||||
var action = approved
|
var action = approved
|
||||||
? Localize("App.WorkflowNote.Approved", "Approved")
|
? Localize("App.WorkflowNote.Approved", "Approved")
|
||||||
: Localize("App.WorkflowNote.Rejected", "Rejected");
|
: Localize("App.WorkflowNote.Rejected", "Rejected");
|
||||||
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.Decision", "Decision"), $"{action}: {FormatNode(current)}"));
|
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.Decision", "Decision"), $"{action}: {FormatNode(current)}"));
|
||||||
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.Description", "Description"), description ?? string.Empty));
|
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.RejectReason", "Reject Reason"), rejectReason ?? string.Empty));
|
||||||
|
context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.Description", "Description"), description));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddWorkflowNodeRows(
|
private void AddWorkflowNodeRows(
|
||||||
|
|
@ -1096,7 +1030,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
if (current.Kind == "Compare")
|
if (current.Kind == "Compare")
|
||||||
{
|
{
|
||||||
var outcomes = DeserializeCompareOutcomes(current.CompareOutcomesJson);
|
var outcomes = ListFormWorkflowCriteriaMapper.DeserializeCompareOutcomes(current.CompareOutcomesJson);
|
||||||
var matched = outcomes.FirstOrDefault(outcome =>
|
var matched = outcomes.FirstOrDefault(outcome =>
|
||||||
outcome.Conditions == null ||
|
outcome.Conditions == null ||
|
||||||
outcome.Conditions.Count == 0 ||
|
outcome.Conditions.Count == 0 ||
|
||||||
|
|
@ -1294,7 +1228,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
NextOnReject = string.Empty,
|
NextOnReject = string.Empty,
|
||||||
PositionX = positionX,
|
PositionX = positionX,
|
||||||
PositionY = positionY,
|
PositionY = positionY,
|
||||||
CompareOutcomesJson = SerializeCompareOutcomes([])
|
CompareOutcomesJson = ListFormWorkflowCriteriaMapper.SerializeCompareOutcomes([])
|
||||||
};
|
};
|
||||||
|
|
||||||
await criteriaRepository.InsertAsync(criteria, autoSave: true);
|
await criteriaRepository.InsertAsync(criteria, autoSave: true);
|
||||||
|
|
@ -1421,7 +1355,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var outcomes = DeserializeCompareOutcomes(criteria.CompareOutcomesJson);
|
var outcomes = ListFormWorkflowCriteriaMapper.DeserializeCompareOutcomes(criteria.CompareOutcomesJson);
|
||||||
foreach (var outcome in outcomes.Where(x => x.TargetId == deletedId))
|
foreach (var outcome in outcomes.Where(x => x.TargetId == deletedId))
|
||||||
{
|
{
|
||||||
outcome.TargetId = null;
|
outcome.TargetId = null;
|
||||||
|
|
@ -1430,35 +1364,12 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
criteria.CompareOutcomesJson = SerializeCompareOutcomes(outcomes);
|
criteria.CompareOutcomesJson = ListFormWorkflowCriteriaMapper.SerializeCompareOutcomes(outcomes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ListFormWorkflowCriteriaDto MapCriteria(ListFormWorkflow criteria)
|
|
||||||
{
|
|
||||||
return new ListFormWorkflowCriteriaDto
|
|
||||||
{
|
|
||||||
Id = criteria.Id,
|
|
||||||
ListFormCode = criteria.ListFormCode,
|
|
||||||
Kind = criteria.Kind,
|
|
||||||
Title = criteria.Title,
|
|
||||||
CompareColumn = criteria.CompareColumn,
|
|
||||||
CompareOperator = criteria.CompareOperator,
|
|
||||||
CompareValue = criteria.CompareValue,
|
|
||||||
Approver = criteria.Approver,
|
|
||||||
NextOnStart = criteria.NextOnStart,
|
|
||||||
NextOnTrue = criteria.NextOnTrue,
|
|
||||||
NextOnFalse = criteria.NextOnFalse,
|
|
||||||
NextOnApprove = criteria.NextOnApprove,
|
|
||||||
NextOnReject = criteria.NextOnReject,
|
|
||||||
PositionX = criteria.PositionX,
|
|
||||||
PositionY = criteria.PositionY,
|
|
||||||
CompareOutcomes = DeserializeCompareOutcomes(criteria.CompareOutcomesJson)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeRequired(string value, string fallback)
|
private static string NormalizeRequired(string value, string fallback)
|
||||||
{
|
{
|
||||||
return value.IsNullOrWhiteSpace() ? fallback : value.Trim();
|
return value.IsNullOrWhiteSpace() ? fallback : value.Trim();
|
||||||
|
|
@ -1494,26 +1405,128 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SerializeCompareOutcomes(List<CompareOutcomeDto> outcomes)
|
private async Task EnsureCanUpdateAsync(string listFormCode)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(outcomes ?? []);
|
if (!await authManager.CanAccess(listFormCode, AuthorizationTypeEnum.Update))
|
||||||
|
{
|
||||||
|
throw new UserFriendlyException("Workflow işlemi için güncelleme yetkiniz yok.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<CompareOutcomeDto> DeserializeCompareOutcomes(string json)
|
private static WorkflowRejectReasonDto? NormalizeRejectReason(WorkflowRejectReasonDto? input)
|
||||||
{
|
{
|
||||||
if (json.IsNullOrWhiteSpace())
|
if (input?.IsConfigured != true)
|
||||||
{
|
{
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return new WorkflowRejectReasonDto
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize<List<CompareOutcomeDto>>(json) ?? [];
|
TableName = input.TableName.Trim(),
|
||||||
}
|
ValueField = input.ValueField.Trim(),
|
||||||
catch
|
TextField = input.TextField.Trim(),
|
||||||
|
TargetField = input.TargetField?.Trim() ?? string.Empty,
|
||||||
|
Required = input.Required
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Red kararındaki neden. İstemcinin gönderdiği değer tablodan doğrulanır; metin istemciden
|
||||||
|
/// alınmaz, kayda ve nota tablodaki hâli yazılır.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<WorkflowRejectReasonItemDto?> ResolveRejectReasonAsync(
|
||||||
|
string listFormCode,
|
||||||
|
WorkflowRejectReasonDto? reason,
|
||||||
|
string? rejectReasonId)
|
||||||
|
{
|
||||||
|
if (reason == null)
|
||||||
{
|
{
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rejectReasonId.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return reason.Required
|
||||||
|
? throw new UserFriendlyException(L["App.ListFormWorkflow.RejectReasonRequired"])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = await GetRejectReasonItemsAsync(listFormCode, reason);
|
||||||
|
return items.FirstOrDefault(x => string.Equals(x.Id, rejectReasonId.Trim(), StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? throw new UserFriendlyException(L["App.ListFormWorkflow.RejectReasonNotFound"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nedenler ekranın veri kaynağındaki tablodan okunur. Tablo ve kolon adları katalogdan çözülür,
|
||||||
|
/// tanımdaki metin sorguya girmez; kiracı, silinmiş ve pasif satırlar kolon varsa süzülür.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<List<WorkflowRejectReasonItemDto>> GetRejectReasonItemsAsync(
|
||||||
|
string listFormCode,
|
||||||
|
WorkflowRejectReasonDto reason)
|
||||||
|
{
|
||||||
|
var listForm = await listFormManager.GetUserListForm(listFormCode);
|
||||||
|
var (repository, connectionString, _) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
||||||
|
|
||||||
|
var table = (await repository.QueryAsync(
|
||||||
|
"SELECT table_schema AS \"SchemaName\", table_name AS \"TableName\", column_name AS \"ColumnName\" " +
|
||||||
|
"FROM information_schema.columns WHERE table_name = @TableName",
|
||||||
|
connectionString,
|
||||||
|
new Dictionary<string, object> { ["@TableName"] = reason.TableName }))
|
||||||
|
.Cast<IDictionary<string, object>>()
|
||||||
|
.Select(row => (
|
||||||
|
Schema: row["SchemaName"]?.ToString() ?? string.Empty,
|
||||||
|
Table: row["TableName"]?.ToString() ?? string.Empty,
|
||||||
|
Column: row["ColumnName"]?.ToString() ?? string.Empty))
|
||||||
|
.Where(x => !x.Schema.Contains('"') && !x.Table.Contains('"') && !x.Column.Contains('"'))
|
||||||
|
.GroupBy(x => (x.Schema, x.Table))
|
||||||
|
.FirstOrDefault()
|
||||||
|
?? throw new UserFriendlyException(L["App.ListFormWorkflow.RejectReasonTableNotFound", reason.TableName]);
|
||||||
|
|
||||||
|
var columns = table.Select(x => x.Column).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
string Column(string name) => columns.TryGetValue(name, out var actual)
|
||||||
|
? $"\"{actual}\""
|
||||||
|
: throw new UserFriendlyException(L["App.ListFormWorkflow.RejectReasonColumnNotFound", name, reason.TableName]);
|
||||||
|
|
||||||
|
var filters = new List<string>();
|
||||||
|
var parameters = new Dictionary<string, object>();
|
||||||
|
if (columns.Contains("TenantId"))
|
||||||
|
{
|
||||||
|
if (CurrentTenant.Id.HasValue)
|
||||||
|
{
|
||||||
|
filters.Add($"{Column("TenantId")} = @TenantId");
|
||||||
|
parameters["@TenantId"] = CurrentTenant.Id.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filters.Add($"{Column("TenantId")} IS NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (columns.Contains("IsDeleted"))
|
||||||
|
{
|
||||||
|
filters.Add($"{Column("IsDeleted")} = @IsDeleted");
|
||||||
|
parameters["@IsDeleted"] = false;
|
||||||
|
}
|
||||||
|
if (columns.Contains("IsActive"))
|
||||||
|
{
|
||||||
|
filters.Add($"{Column("IsActive")} = @IsActive");
|
||||||
|
parameters["@IsActive"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textColumn = Column(reason.TextField);
|
||||||
|
var sql = $"SELECT {Column(reason.ValueField)} AS \"Id\", {textColumn} AS \"Text\" " +
|
||||||
|
$"FROM \"{table.Key.Schema}\".\"{table.Key.Table}\"" +
|
||||||
|
(filters.Count > 0 ? $" WHERE {string.Join(" AND ", filters)}" : string.Empty) +
|
||||||
|
$" ORDER BY {textColumn}";
|
||||||
|
|
||||||
|
return (await repository.QueryAsync(sql, connectionString, parameters))
|
||||||
|
.Cast<IDictionary<string, object>>()
|
||||||
|
.Select(row => new WorkflowRejectReasonItemDto
|
||||||
|
{
|
||||||
|
Id = Convert.ToString(row["Id"], CultureInfo.InvariantCulture) ?? string.Empty,
|
||||||
|
Text = row["Text"]?.ToString() ?? string.Empty
|
||||||
|
})
|
||||||
|
.Where(x => !x.Id.IsNullOrWhiteSpace())
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WorkflowDto DeserializeWorkflow(string json)
|
private static WorkflowDto DeserializeWorkflow(string json)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Sozsoft.Platform.Entities;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms.Workflow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Workflow düğümünün entity → DTO dönüşümü ve ekranda gösterim sırası; tasarımcı
|
||||||
|
/// (<see cref="ListFormWorkflowAppService"/>) ile liste seçeneklerini dönen servis aynı sonucu üretir.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ListFormWorkflowCriteriaMapper
|
||||||
|
{
|
||||||
|
public static List<ListFormWorkflowCriteriaDto> MapOrdered(IEnumerable<ListFormWorkflow> criteria)
|
||||||
|
{
|
||||||
|
return Order(criteria.OrderBy(x => x.Id).Select(Map).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListFormWorkflowCriteriaDto Map(ListFormWorkflow criteria)
|
||||||
|
{
|
||||||
|
return new ListFormWorkflowCriteriaDto
|
||||||
|
{
|
||||||
|
Id = criteria.Id,
|
||||||
|
ListFormCode = criteria.ListFormCode,
|
||||||
|
Kind = criteria.Kind,
|
||||||
|
Title = criteria.Title,
|
||||||
|
CompareColumn = criteria.CompareColumn,
|
||||||
|
CompareOperator = criteria.CompareOperator,
|
||||||
|
CompareValue = criteria.CompareValue,
|
||||||
|
Approver = criteria.Approver,
|
||||||
|
NextOnStart = criteria.NextOnStart,
|
||||||
|
NextOnTrue = criteria.NextOnTrue,
|
||||||
|
NextOnFalse = criteria.NextOnFalse,
|
||||||
|
NextOnApprove = criteria.NextOnApprove,
|
||||||
|
NextOnReject = criteria.NextOnReject,
|
||||||
|
PositionX = criteria.PositionX,
|
||||||
|
PositionY = criteria.PositionY,
|
||||||
|
CompareOutcomes = DeserializeCompareOutcomes(criteria.CompareOutcomesJson),
|
||||||
|
RejectReason = WorkflowRejectReasonDto.Deserialize(criteria.RejectReasonJson)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SerializeCompareOutcomes(List<CompareOutcomeDto>? outcomes)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(outcomes ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<CompareOutcomeDto> DeserializeCompareOutcomes(string? json)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(json))
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<List<CompareOutcomeDto>>(json) ?? [];
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Düğümler başlangıçtan akış yönünde sıralanır; bağlantısız düğümler sona eklenir.</summary>
|
||||||
|
private static List<ListFormWorkflowCriteriaDto> Order(List<ListFormWorkflowCriteriaDto> criteria)
|
||||||
|
{
|
||||||
|
if (criteria.Count <= 1)
|
||||||
|
{
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
var criteriaById = criteria.ToDictionary(x => x.Id);
|
||||||
|
var ordered = new List<ListFormWorkflowCriteriaDto>();
|
||||||
|
var visited = new HashSet<string>();
|
||||||
|
var visiting = new HashSet<string>();
|
||||||
|
|
||||||
|
var roots = criteria.Where(x => x.Kind == "Start").ToList();
|
||||||
|
if (roots.Count == 0)
|
||||||
|
{
|
||||||
|
var referencedIds = criteria
|
||||||
|
.SelectMany(GetTargetIds)
|
||||||
|
.Where(criteriaById.ContainsKey)
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
roots = criteria.Where(x => !referencedIds.Contains(x.Id)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in roots.Concat(criteria))
|
||||||
|
{
|
||||||
|
Visit(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ordered;
|
||||||
|
|
||||||
|
void Visit(ListFormWorkflowCriteriaDto item)
|
||||||
|
{
|
||||||
|
if (visited.Contains(item.Id) || !visiting.Add(item.Id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ordered.Add(item);
|
||||||
|
|
||||||
|
foreach (var targetId in GetTargetIds(item))
|
||||||
|
{
|
||||||
|
if (criteriaById.TryGetValue(targetId, out var target))
|
||||||
|
{
|
||||||
|
Visit(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visiting.Remove(item.Id);
|
||||||
|
visited.Add(item.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> GetTargetIds(ListFormWorkflowCriteriaDto criteria)
|
||||||
|
{
|
||||||
|
return new[] { criteria.NextOnStart }
|
||||||
|
.Concat((criteria.CompareOutcomes ?? []).Select(outcome => outcome.TargetId))
|
||||||
|
.Concat([criteria.NextOnTrue, criteria.NextOnFalse, criteria.NextOnApprove, criteria.NextOnReject])
|
||||||
|
.Where(id => !string.IsNullOrWhiteSpace(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -226,6 +226,7 @@ public class WizardDataSeeder(
|
||||||
PositionX = criteria.PositionX,
|
PositionX = criteria.PositionX,
|
||||||
PositionY = criteria.PositionY,
|
PositionY = criteria.PositionY,
|
||||||
CompareOutcomesJson = JsonSerializer.Serialize(criteria.CompareOutcomes ?? []),
|
CompareOutcomesJson = JsonSerializer.Serialize(criteria.CompareOutcomes ?? []),
|
||||||
|
RejectReasonJson = WorkflowRejectReasonDto.Serialize(criteria.RejectReason),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,8 @@ public static class WizardConsts
|
||||||
bool AllowUpdating,
|
bool AllowUpdating,
|
||||||
bool ConfirmDelete,
|
bool ConfirmDelete,
|
||||||
bool SendOnlyChangedFormValuesUpdate,
|
bool SendOnlyChangedFormValuesUpdate,
|
||||||
bool AllowDetail = false) => JsonSerializer.Serialize(new
|
bool AllowDetail = false,
|
||||||
|
bool OpenDetailAfterInsert = false) => JsonSerializer.Serialize(new
|
||||||
{
|
{
|
||||||
Popup = new { Title = Title, Width = Width, Height = Height },
|
Popup = new { Title = Title, Width = Width, Height = Height },
|
||||||
AllowDeleting = AllowDeleting,
|
AllowDeleting = AllowDeleting,
|
||||||
|
|
@ -249,7 +250,8 @@ public static class WizardConsts
|
||||||
AllowUpdating = AllowUpdating,
|
AllowUpdating = AllowUpdating,
|
||||||
ConfirmDelete = ConfirmDelete,
|
ConfirmDelete = ConfirmDelete,
|
||||||
SendOnlyChangedFormValuesUpdate = SendOnlyChangedFormValuesUpdate,
|
SendOnlyChangedFormValuesUpdate = SendOnlyChangedFormValuesUpdate,
|
||||||
AllowDetail = AllowDetail
|
AllowDetail = AllowDetail,
|
||||||
|
OpenDetailAfterInsert = OpenDetailAfterInsert
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,10 @@ public class ListFormWorkflow : Entity<string>
|
||||||
public int PositionX { get; set; }
|
public int PositionX { get; set; }
|
||||||
public int PositionY { get; set; }
|
public int PositionY { get; set; }
|
||||||
public string CompareOutcomesJson { get; set; }
|
public string CompareOutcomesJson { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Onay düğümünde red nedeninin seçileceği tablo ve kolonları (<c>WorkflowRejectReasonDto</c>);
|
||||||
|
/// boşsa red yalnızca açıklama ister.
|
||||||
|
/// </summary>
|
||||||
|
public string RejectReasonJson { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,7 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.PositionX).IsRequired();
|
b.Property(x => x.PositionX).IsRequired();
|
||||||
b.Property(x => x.PositionY).IsRequired();
|
b.Property(x => x.PositionY).IsRequired();
|
||||||
b.Property(x => x.CompareOutcomesJson).HasColumnType("nvarchar(max)");
|
b.Property(x => x.CompareOutcomesJson).HasColumnType("nvarchar(max)");
|
||||||
|
b.Property(x => x.RejectReasonJson).HasColumnType("nvarchar(max)");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<Note>(b =>
|
builder.Entity<Note>(b =>
|
||||||
|
|
|
||||||
|
|
@ -4077,6 +4077,9 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Property<int>("PositionY")
|
b.Property<int>("PositionY")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("RejectReasonJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(250)
|
.HasMaxLength(250)
|
||||||
|
|
|
||||||
|
|
@ -3429,7 +3429,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
NextOnReject = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
NextOnReject = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
PositionX = table.Column<int>(type: "int", nullable: false),
|
PositionX = table.Column<int>(type: "int", nullable: false),
|
||||||
PositionY = table.Column<int>(type: "int", nullable: false),
|
PositionY = table.Column<int>(type: "int", nullable: false),
|
||||||
CompareOutcomesJson = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
CompareOutcomesJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
RejectReasonJson = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4074,6 +4074,9 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Property<int>("PositionY")
|
b.Property<int>("PositionY")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("RejectReasonJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(250)
|
.HasMaxLength(250)
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,13 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
alanın varsayılanı `FormFieldsDefaultValueDto`'da `CustomValueType: 5`, `Value` = numaratörün `Name`'i.
|
alanın varsayılanı `FormFieldsDefaultValueDto`'da `CustomValueType: 5`, `Value` = numaratörün `Name`'i.
|
||||||
`@AUTONUMBER`, `MAX + 1` sorgusu ya da SQL ile numara üretilmez (boşluksuz numara açıkça istenmedikçe).
|
`@AUTONUMBER`, `MAX + 1` sorgusu ya da SQL ile numara üretilmez (boşluksuz numara açıkça istenmedikçe).
|
||||||
Ayrıntı: `lowcode.instructions.md` §4.3.5.2.
|
Ayrıntı: `lowcode.instructions.md` §4.3.5.2.
|
||||||
|
24. **İş akışlı liste** → tablo, tasarımcının Workflow şablonundaki dört kolonla kurulur ve `WorkflowDto`
|
||||||
|
yalnız bunlara bağlanır: `ApprovalUserName` nvarchar(256), `ApprovalStatus` nvarchar(50),
|
||||||
|
`ApprovalDate` datetime, `ApprovalDescription` nvarchar(200) (PG: varchar/timestamp). Ayrı durum
|
||||||
|
kolonu (`Status`), Guid onaycı kolonu (`ApproverId`) ve red nedeni kolonu açılmaz: durum akışın
|
||||||
|
düğüm başlığıdır, red nedeni onay düğümünün `RejectReason` listesinden seçilip açıklamaya yazılır.
|
||||||
|
Bu dört alan popup düzenleme formunda yer almaz (akış yazar), listede kolon olarak görünür.
|
||||||
|
Ayrıntı: `lowcode.instructions.md` §4.7.
|
||||||
|
|
||||||
### Cross-cutting: touching one thing means touching these too
|
### Cross-cutting: touching one thing means touching these too
|
||||||
|
|
||||||
|
|
@ -580,7 +587,7 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
`Abp.Identity.Permission{Groups,s}.json`; platformun kendi menü/yetki kayıtları da diğer liste
|
`Abp.Identity.Permission{Groups,s}.json`; platformun kendi menü/yetki kayıtları da diğer liste
|
||||||
verisiyle aynı mekanizmadan (`ListFormSeedDataApplier`) geçer, ayrı bir seeder yoktur. `Order`
|
verisiyle aynı mekanizmadan (`ListFormSeedDataApplier`) geçer, ayrı bir seeder yoktur. `Order`
|
||||||
alanı bağımlılık sırasını kurar: route → modül → menü, yetki grubu → yetki, ülke grubu → ülke →
|
alanı bağımlılık sırasını kurar: route → modül → menü, yetki grubu → yetki, ülke grubu → ülke →
|
||||||
şehir → ilçe (`App.Definitions.{CountryGroup,Country,City,District}.json`), bildirim tipi → kuralı
|
şehir → ilçe (`App.Definitions.{CountryGroup,Country,City,District}.json`), departman → onaycı (`App.Hr.Department` → `App.Definitions.Approver`), bildirim tipi → kuralı
|
||||||
(`App.Notifications.Notification{Types,Rules}.json`). Para birimi, hitap, AI bot
|
(`App.Notifications.Notification{Types,Rules}.json`). Para birimi, hitap, AI bot
|
||||||
(`App.Definitions.{Currency,ContactTitle,AiBot}.json`), ayar tanımları (`App.SettingDefinitions.json`)
|
(`App.Definitions.{Currency,ContactTitle,AiBot}.json`), ayar tanımları (`App.SettingDefinitions.json`)
|
||||||
ve zamanlanmış işler (`App.BackgroundWorkers.RecurringJobs.json`), dil → metin
|
ve zamanlanmış işler (`App.BackgroundWorkers.RecurringJobs.json`), dil → metin
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,13 @@
|
||||||
"ListFormCode": "App.Definitions.Approver",
|
"ListFormCode": "App.Definitions.Approver",
|
||||||
"KeyFieldName": "Id",
|
"KeyFieldName": "Id",
|
||||||
"GeneratedAt": "2026-10-09T18:22:21.5830178Z",
|
"GeneratedAt": "2026-10-09T18:22:21.5830178Z",
|
||||||
"Order": 0,
|
"Order": 11,
|
||||||
"Rows": [
|
"Rows": [
|
||||||
{
|
|
||||||
"Id": "4f91635b-6fa1-4547-b687-2f0c0e188a67",
|
|
||||||
"DepartmentId": "8f14e45f-ea2d-4d9f-9f1d-1a2b3c4d5e6f",
|
|
||||||
"ApprovalArea": "Purchase",
|
|
||||||
"ApproverUserId": "77dc76f8-c567-974c-4bc6-3a2433f9216e",
|
|
||||||
"IsActive": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "db014b58-ec8b-4a65-b406-078f66323b7c",
|
"Id": "db014b58-ec8b-4a65-b406-078f66323b7c",
|
||||||
"DepartmentId": "8f14e45f-ea2d-4d9f-9f1d-1a2b3c4d5e6f",
|
"DepartmentId": "8f14e45f-ea2d-4d9f-9f1d-1a2b3c4d5e6f",
|
||||||
"ApprovalArea": "Purchase",
|
"ApprovalArea": "Purchase",
|
||||||
"ApproverUserId": "e065080e-be54-c62c-a272-3a243467aa16",
|
"ApproverUserId": "0b221f19-f587-b053-d903-3a24394b182b",
|
||||||
"IsActive": true
|
"IsActive": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -16,6 +16,12 @@
|
||||||
"Name": "Üretim",
|
"Name": "Üretim",
|
||||||
"ParentId": "8f14e45f-ea2d-4d9f-9f1d-1a2b3c4d5e6f"
|
"ParentId": "8f14e45f-ea2d-4d9f-9f1d-1a2b3c4d5e6f"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Id": "2c1743a3-91b7-4b57-a6c8-7d8e9f0a112c",
|
||||||
|
"BranchId": "1c125bf3-3ffd-4445-a270-1bc43ac3f61c",
|
||||||
|
"Name": "Satınalma",
|
||||||
|
"ParentId": "8f14e45f-ea2d-4d9f-9f1d-1a2b3c4d5e6f"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Id": "987bcab0-1b92-4a4e-8d7f-3c2b1a0f9e8d",
|
"Id": "987bcab0-1b92-4a4e-8d7f-3c2b1a0f9e8d",
|
||||||
"BranchId": "1c125bf3-3ffd-4445-a270-1bc43ac3f61c",
|
"BranchId": "1c125bf3-3ffd-4445-a270-1bc43ac3f61c",
|
||||||
|
|
|
||||||
|
|
@ -13654,6 +13654,34 @@
|
||||||
"Key": "App.ListFormEdit.EditingAllowDuplicate",
|
"Key": "App.ListFormEdit.EditingAllowDuplicate",
|
||||||
"Value": "Çoğaltmaya İzin Ver"
|
"Value": "Çoğaltmaya İzin Ver"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Id": "33bf5f3a-e6cb-5730-a4dd-0784e9849f4d",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormEdit.EditingGeneral",
|
||||||
|
"Value": "General"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "c8446544-7f1c-596d-84c9-8ca105a2990e",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormEdit.EditingGeneral",
|
||||||
|
"Value": "Genel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "b20d74db-49bd-5ad1-a16d-c9eaae8d3bb0",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormEdit.EditingOpenDetailAfterInsert",
|
||||||
|
"Value": "Open Detail After Insert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "24a24d50-ed42-53f0-8f06-19e8b8b455de",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormEdit.EditingOpenDetailAfterInsert",
|
||||||
|
"Value": "Kaydedince Detaya Git"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Id": "c39c08f3-ced7-574c-9995-04e1c72ab1c3",
|
"Id": "c39c08f3-ced7-574c-9995-04e1c72ab1c3",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -92969,14 +92997,14 @@
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Definitions.Approver.Desc",
|
"Key": "App.Definitions.Approver.Desc",
|
||||||
"Value": "Approvers — the single list of who approves what for each department across the application.\nWhere: Administration › Definitions › Approvers.\nFields: department (shown as Branch - Department), approval area (Purchasing / Purchasing Desk / Warehouse / Human Resources / Finance), approver user, active.\nUsed by: approval workflows read the approvers of the record's department and area. Purchasing › Material Requests: the department approval uses area Purchasing for the requester's department; the purchasing approval uses area Purchasing Desk (any department). Several approvers may be defined for the same department and area; any one of them can approve. Passive rows are ignored by workflows."
|
"Value": "Approvers — the single list of who approves what for each department across the application.\nWhere: Administration › Definitions › Approvers.\nFields: department (shown as Branch - Department), approval area (Purchasing / Warehouse / Human Resources / Finance), approver user, active.\nUsed by: approval workflows read the approvers of the record's department and area. Purchasing › Material Requests: the department approval uses area Purchasing for the requester's department; the purchasing approval is not defined here: it goes to users with update permission on Awaiting Purchasing Approval (directly or through their role). Several approvers may be defined for the same department and area; any one of them can approve. Passive rows are ignored by workflows."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3663c5aa-4036-5381-810f-8acee720724c",
|
"Id": "3663c5aa-4036-5381-810f-8acee720724c",
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Definitions.Approver.Desc",
|
"Key": "App.Definitions.Approver.Desc",
|
||||||
"Value": "Onaycılar — uygulamadaki tüm onay adımlarında her departman için kimin onaylayacağını tutan tek liste.\nNerede: Yönetim › Tanımlamalar › Onaycılar.\nAlanlar: departman (Şube - Departman olarak gösterilir), onay yeri (Satınalma / Satınalma Masası / Depo / İnsan Kaynakları / Finans), onaylayacak kişi, aktif.\nKullanıldığı yer: onay akışları kaydın departmanı ve onay yerine göre onaycıları buradan okur. Satınalma › Satınalma Talepleri: departman onayı talep edenin departmanındaki Satınalma satırlarını, satınalma onayı ise Satınalma Masası satırlarını (departmandan bağımsız) kullanır. Aynı departman ve onay yerine birden çok onaycı girilebilir; herhangi birinin onayı yeterlidir. Pasif satırlar akışlarda kullanılmaz."
|
"Value": "Onaycılar — uygulamadaki tüm onay adımlarında her departman için kimin onaylayacağını tutan tek liste.\nNerede: Yönetim › Tanımlamalar › Onaycılar.\nAlanlar: departman (Şube - Departman olarak gösterilir), onay yeri (Satınalma / Depo / İnsan Kaynakları / Finans), onaylayacak kişi, aktif.\nKullanıldığı yer: onay akışları kaydın departmanı ve onay yerine göre onaycıları buradan okur. Satınalma › Malzeme Talepleri: departman onayı talep edenin departmanındaki Satınalma satırlarını, satınalma onayı burada tanımlanmaz: Satınalma Onayı Bekleyenler ekranında güncelleme yetkisi olan kullanıcılar (doğrudan ya da rolüyle) onaylar. Aynı departman ve onay yerine birden çok onaycı girilebilir; herhangi birinin onayı yeterlidir. Pasif satırlar akışlarda kullanılmaz."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "cd7ff644-eefa-516e-8c8c-8b4fa0ec76f8",
|
"Id": "cd7ff644-eefa-516e-8c8c-8b4fa0ec76f8",
|
||||||
|
|
@ -93062,20 +93090,6 @@
|
||||||
"Key": "App.Listform.ListformField.ApproverUser",
|
"Key": "App.Listform.ListformField.ApproverUser",
|
||||||
"Value": "Onaylayacak Kişi"
|
"Value": "Onaylayacak Kişi"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Id": "a0bd0fe5-757d-57a9-b689-e1b0e9af639e",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.StaticLookup.PurchaseDesk",
|
|
||||||
"Value": "Purchasing Desk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "ed5a8261-8b99-5809-b1fc-5012e9064ad4",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.StaticLookup.PurchaseDesk",
|
|
||||||
"Value": "Satınalma Masası"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "05f3d2d1-2824-5ec8-afa8-2631940be01b",
|
"Id": "05f3d2d1-2824-5ec8-afa8-2631940be01b",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -93116,7 +93130,7 @@
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.RejectReason.Desc",
|
"Key": "App.Prc.RejectReason.Desc",
|
||||||
"Value": "Red Nedenleri — satınalma taleplerinin reddedilme sebepleri (ihtiyaç yok, miktar uygun değil, yanlış ürün, bütçe uygun değil, diğer).\nNerede: Satınalma › Tanımlamalar › Red Nedenleri.\nAlanlar: red nedeni, sıra (seçim listesindeki sırası), aktif.\nKullanıldığı yer: Satınalma › Satınalma Talepleri — departman onaycısı ya da satınalma talebi reddederken neden seçer. Yalnız aktif nedenler listelenir; kullanılmış neden silinmez, pasife alınır."
|
"Value": "Red Nedenleri — satınalma taleplerinin reddedilme sebepleri (ihtiyaç yok, miktar uygun değil, yanlış ürün, bütçe uygun değil, diğer).\nNerede: Satınalma › Tanımlamalar › Red Nedenleri.\nAlanlar: red nedeni, sıra (seçim listesindeki sırası), aktif.\nKullanıldığı yer: Satınalma › Malzeme Talepleri — departman onaycısı ya da satınalma talebi reddederken neden seçer. Yalnız aktif nedenler listelenir; kullanılmış neden silinmez, pasife alınır."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "31115331-7e79-54e9-ae55-a2e682571c43",
|
"Id": "31115331-7e79-54e9-ae55-a2e682571c43",
|
||||||
|
|
@ -93165,14 +93179,14 @@
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequestLine.Desc",
|
"Key": "App.Prc.MaterialRequestLine.Desc",
|
||||||
"Value": "Request Lines — the products requested in a material request.\nWhere: Purchasing › Material Requests › any list › Request Lines tab (no menu of its own).\nFields: product (active, purchasable products), quantity, unit (copied from the product's purchase unit, read-only), fulfilment (Purchasing / Warehouse Transfer), due date, request reason, delivered quantity (read-only), planned quantity (defaults to quantity), purchasing responsible, line status (Waiting / Planned / Done / Cancelled).\nRules: lines can be added or deleted only while the request has not been sent for approval. Changing line statuses recalculates the request status (In Progress / Partially Done / Done)."
|
"Value": "Request Lines — the products requested in a material request.\nWhere: Purchasing › Material Requests › My Requests, Awaiting Department Approval and Awaiting Purchasing Approval › Request Lines tab; All Requests shows the lines as columns (no menu of its own).\nFields: product (active, purchasable products), quantity, unit (copied from the product's purchase unit, read-only), fulfilment (Purchasing / Warehouse Transfer), due date, request reason, delivered quantity (read-only), planned quantity (defaults to quantity), purchasing responsible, line status (Waiting / Planned / Done / Cancelled).\nRules: lines can be added or deleted only while the request has not been sent for approval. Changing line statuses recalculates the request status (In Progress / Partially Done / Done)."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "cd985741-f49b-5a73-80a5-10f6ea403c1e",
|
"Id": "cd985741-f49b-5a73-80a5-10f6ea403c1e",
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequestLine.Desc",
|
"Key": "App.Prc.MaterialRequestLine.Desc",
|
||||||
"Value": "Talep Satırları — malzeme talebinde istenen ürünler.\nNerede: Satınalma › Satınalma Talepleri › herhangi bir liste › Talep Satırları sekmesi (kendi menüsü yoktur).\nAlanlar: ürün (aktif ve satın alınabilir ürünler), miktar, birim (ürünün satınalma biriminden gelir, salt okunur), karşılama şekli (Satınalma / Depodan Transfer), termin tarihi, talep nedeni, teslim edilen miktar (salt okunur), planlanacak miktar (boşsa miktar), satınalma sorumlusu, satır durumu (Bekliyor / Planlandı / Tamamlandı / İptal).\nKurallar: satır yalnız talep onaya gönderilmeden eklenip silinebilir. Satır durumları değişince talebin durumu yeniden hesaplanır (İşlemde / Kısmi Tamamlandı / Tamamlandı)."
|
"Value": "Talep Satırları — malzeme talebinde istenen ürünler.\nNerede: Satınalma › Malzeme Talepleri › Taleplerim, Departman Onayı Bekleyenler ve Satınalma Onayı Bekleyenler › Talep Satırları sekmesi; Tüm Talepler satırları kolon olarak gösterir (kendi menüsü yoktur).\nAlanlar: ürün (aktif ve satın alınabilir ürünler), miktar, birim (ürünün satınalma biriminden gelir, salt okunur), karşılama şekli (Satınalma / Depodan Transfer), termin tarihi, talep nedeni, teslim edilen miktar (salt okunur), planlanacak miktar (boşsa miktar), satınalma sorumlusu, satır durumu (Bekliyor / Planlandı / Tamamlandı / İptal).\nKurallar: satır yalnız talep onaya gönderilmeden eklenip silinebilir. Satır durumları değişince talebin durumu yeniden hesaplanır (İşlemde / Kısmi Tamamlandı / Tamamlandı)."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "d6911bd6-3f4a-582d-8328-e01403808802",
|
"Id": "d6911bd6-3f4a-582d-8328-e01403808802",
|
||||||
|
|
@ -93347,14 +93361,14 @@
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MyMaterialRequest.Desc",
|
"Key": "App.Prc.MyMaterialRequest.Desc",
|
||||||
"Value": "My Requests — the material requests you opened, in every status.\nWhere: Purchasing › Material Requests › My Requests.\nFields: request no (MT-YYYY-00001, automatic), priority, requester (you), department (your department by default), warehouse and location where the goods will be put, request date, status, general description; Request Lines tab: product, quantity, unit, fulfilment, due date, request reason.\nFlow: add lines, then send for approval — the department approver (Approvers › area Purchasing for your department) and then purchasing (area Purchasing Desk) approve or reject. Lines can only be added or removed before sending. Approval / rejection details are shown in the approval group.\nRelated: Awaiting Department Approval, Awaiting Purchasing Approval, Awaiting Planning, All Requests."
|
"Value": "My Requests — the material requests you opened, in every status.\nWhere: Purchasing › Material Requests › My Requests.\nFields: request no (MT-YYYY-00001, automatic), priority, requester (you), department (your department by default), warehouse and location where the goods will be put, request date, general description; Request Lines tab: product, quantity, unit, fulfilment, due date, request reason.\nFlow: add lines, then send for approval — the department approver (Approvers › area Purchasing for your department) and then purchasing (users with update permission on Awaiting Purchasing Approval (directly or through their role)) approve or reject. Lines can only be added or removed before sending. Approval status, approver, date and description (with the rejection reason) are filled by the workflow and shown in the list, not in the edit form.\nRelated: Awaiting Department Approval, Awaiting Purchasing Approval, All Requests."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "cdcb1b32-ecb7-5fd1-af5b-fdfd2614d779",
|
"Id": "cdcb1b32-ecb7-5fd1-af5b-fdfd2614d779",
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MyMaterialRequest.Desc",
|
"Key": "App.Prc.MyMaterialRequest.Desc",
|
||||||
"Value": "Taleplerim — açtığınız malzeme talepleri, tüm durumlarıyla.\nNerede: Satınalma › Satınalma Talepleri › Taleplerim.\nAlanlar: talep no (MT-YYYY-00001, otomatik), öncelik, talep eden (siz), departman (varsayılan olarak sizin departmanınız), malın konulacağı depo ve konum, talep tarihi, durum, genel açıklama; Talep Satırları sekmesi: ürün, miktar, birim, karşılama şekli, termin tarihi, talep nedeni.\nAkış: satırları girip onaya gönderin — önce departman onaycısı (Onaycılar › departmanınız için Satınalma onay yeri), ardından satınalma (Satınalma Masası onay yeri) onaylar ya da reddeder. Satırlar yalnız onaya göndermeden önce eklenip silinebilir. Onay / red bilgileri onay grubunda görünür.\nİlişkili: Departman Onayı Bekleyenler, Satınalma Onayı Bekleyenler, Planlama Bekleyenler, Tüm Talepler."
|
"Value": "Taleplerim — açtığınız malzeme talepleri, tüm durumlarıyla.\nNerede: Satınalma › Malzeme Talepleri › Taleplerim.\nAlanlar: talep no (MT-YYYY-00001, otomatik), öncelik, talep eden (siz), departman (varsayılan olarak sizin departmanınız), malın konulacağı depo ve konum, talep tarihi, genel açıklama; Talep Satırları sekmesi: ürün, miktar, birim, karşılama şekli, termin tarihi, talep nedeni.\nAkış: satırları girip onaya gönderin — önce departman onaycısı (Onaycılar › departmanınız için Satınalma onay yeri), ardından satınalma (Satınalma Onayı Bekleyenler ekranında güncelleme yetkisi olan kullanıcılar (doğrudan ya da rolüyle)) onaylar ya da reddeder. Satırlar yalnız onaya göndermeden önce eklenip silinebilir. Onay durumu, onaylayan, tarih ve açıklama (red nedeniyle birlikte) iş akışınca doldurulur; düzenleme formunda değil listede görünür.\nİlişkili: Departman Onayı Bekleyenler, Satınalma Onayı Bekleyenler, Tüm Talepler."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "ce3d734c-f508-5647-8f8d-2bf6b056c79a",
|
"Id": "ce3d734c-f508-5647-8f8d-2bf6b056c79a",
|
||||||
|
|
@ -93384,20 +93398,6 @@
|
||||||
"Key": "App.Prc.MaterialRequest.Group.Approval",
|
"Key": "App.Prc.MaterialRequest.Group.Approval",
|
||||||
"Value": "Onay / Red Bilgileri"
|
"Value": "Onay / Red Bilgileri"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Id": "048c326c-7709-52a2-919f-928eb4f02fc2",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Draft",
|
|
||||||
"Value": "Draft"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "e490d63f-4209-516f-9bf7-ded12e62c7e1",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Draft",
|
|
||||||
"Value": "Taslak"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "3708a984-4c9b-536b-b4f8-3928bad0c787",
|
"Id": "3708a984-4c9b-536b-b4f8-3928bad0c787",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -93440,48 +93440,6 @@
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Approved",
|
"Key": "App.Prc.MaterialRequest.Status.Approved",
|
||||||
"Value": "Onaylandı"
|
"Value": "Onaylandı"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Id": "fc01609d-1b30-57e7-a8aa-a2ba025f8793",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.InProgress",
|
|
||||||
"Value": "In Progress"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "72fa09da-1c88-59b0-a11e-7d9ed3599ea4",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.InProgress",
|
|
||||||
"Value": "İşlemde"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "c1b67758-5e95-5dfd-81b7-4d601ee1a492",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.PartiallyDone",
|
|
||||||
"Value": "Partially Done"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "9dae2baf-ae05-5877-9194-e2ebacb20945",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.PartiallyDone",
|
|
||||||
"Value": "Kısmi Tamamlandı"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "0ae3f045-c580-5a1c-8ffb-2f34940230a0",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Done",
|
|
||||||
"Value": "Done"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "ccda2c9c-6397-52dc-a159-5b3fd83f8820",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Done",
|
|
||||||
"Value": "Tamamlandı"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "b29fee10-a751-5dec-9eb5-8ac8837d5ef8",
|
"Id": "b29fee10-a751-5dec-9eb5-8ac8837d5ef8",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -93564,7 +93522,7 @@
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequests",
|
"Key": "App.Prc.MaterialRequests",
|
||||||
"Value": "Satınalma Talepleri"
|
"Value": "Malzeme Talepleri"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "cf76b42b-82ed-5838-9fb2-30b0c8caf3e5",
|
"Id": "cf76b42b-82ed-5838-9fb2-30b0c8caf3e5",
|
||||||
|
|
@ -93594,20 +93552,6 @@
|
||||||
"Key": "App.Listform.ListformField.LocationId",
|
"Key": "App.Listform.ListformField.LocationId",
|
||||||
"Value": "Konum"
|
"Value": "Konum"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Id": "caada0e9-b75d-52f2-b07e-c25384a2532e",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.RejectReasonId",
|
|
||||||
"Value": "Rejection Reason"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "787d4dd5-867b-52a0-8689-f55801750fe0",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.RejectReasonId",
|
|
||||||
"Value": "Red Nedeni"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "354564b7-be8f-5e32-9add-9fbda8f912f2",
|
"Id": "354564b7-be8f-5e32-9add-9fbda8f912f2",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -93636,34 +93580,6 @@
|
||||||
"Key": "App.Listform.ListformField.PurchaseApproverUserNames",
|
"Key": "App.Listform.ListformField.PurchaseApproverUserNames",
|
||||||
"Value": "Satınalma Onaycıları"
|
"Value": "Satınalma Onaycıları"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Id": "6f297c45-bc1e-5235-a511-f96c07ef970c",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.ApproverId",
|
|
||||||
"Value": "Approver Id"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "5d442093-8ce2-5d44-9ea1-4306060a8fd4",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.ApproverId",
|
|
||||||
"Value": "Onaylayan Kimlik"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "8647efbf-f775-5e53-a00e-f1ce508c609f",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.ApprovalNote",
|
|
||||||
"Value": "Approval Note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "9d232928-5938-5b1d-b3c3-d752e1cd2077",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.ApprovalNote",
|
|
||||||
"Value": "Onay Notu"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "3cb0bb36-408f-50e8-b260-850cbdd25e7a",
|
"Id": "3cb0bb36-408f-50e8-b260-850cbdd25e7a",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -93678,90 +93594,6 @@
|
||||||
"Key": "App.Listform.ListformField.DeptApproverUserNames",
|
"Key": "App.Listform.ListformField.DeptApproverUserNames",
|
||||||
"Value": "Departman Onaycıları"
|
"Value": "Departman Onaycıları"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Id": "8e05f767-5e90-5aed-8e15-f544883da7b4",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.PurchaseApprovedBy",
|
|
||||||
"Value": "Purchasing Approved By"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "5294b27e-2e61-5c0c-8b69-406425b5f592",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.PurchaseApprovedBy",
|
|
||||||
"Value": "Satınalma Onaylayan"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "7697f43d-e5da-5666-b919-ae0eb6b7a6a0",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.DeptApprovedBy",
|
|
||||||
"Value": "Department Approved By"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "f56eb927-0d12-5192-bf36-edb5847aae45",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.DeptApprovedBy",
|
|
||||||
"Value": "Departman Onaylayan"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "b71494eb-3a96-5f3d-bf22-87421cad6f5c",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.PurchaseApprovalDate",
|
|
||||||
"Value": "Purchasing Approval Date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "d4e99bb0-b797-5dd1-a4f8-18163b533b5b",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.PurchaseApprovalDate",
|
|
||||||
"Value": "Satınalma Onay Tarihi"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "423c12ac-9935-54f0-8973-9e508cc0961a",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.DeptApprovalDate",
|
|
||||||
"Value": "Department Approval Date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "c86753a5-1b33-5b79-8fe4-55a8dfd87e88",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.DeptApprovalDate",
|
|
||||||
"Value": "Departman Onay Tarihi"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "46d7172f-46ca-5b5c-af1b-01b30e4e78e9",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.PurchaseApprovalNote",
|
|
||||||
"Value": "Purchasing Approval Note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "1615218a-57a7-5398-b0d3-1f9eef0039db",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.PurchaseApprovalNote",
|
|
||||||
"Value": "Satınalma Onay Notu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "08ed225a-1c13-5c79-970e-7ec77930277a",
|
|
||||||
"CultureName": "en",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.DeptApprovalNote",
|
|
||||||
"Value": "Department Approval Note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": "238cae24-91e7-5c8e-82a7-ba6807076cdd",
|
|
||||||
"CultureName": "tr",
|
|
||||||
"ResourceName": "Platform",
|
|
||||||
"Key": "App.Listform.ListformField.DeptApprovalNote",
|
|
||||||
"Value": "Departman Onay Notu"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Id": "6992315a-5810-5cf8-9061-6c9f8b61d750",
|
"Id": "6992315a-5810-5cf8-9061-6c9f8b61d750",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
@ -93795,14 +93627,14 @@
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequestDeptApproval.Desc",
|
"Key": "App.Prc.MaterialRequestDeptApproval.Desc",
|
||||||
"Value": "Awaiting Department Approval — material requests waiting for the department approver's decision.\nWhere: Purchasing › Material Requests › Awaiting Department Approval.\nWho: approvers defined in Administration › Definitions › Approvers with area Purchasing for the requester's department; each approver sees only the requests assigned to them.\nWhat to do: open the request, review the Request Lines tab, then Approve (the request moves to purchasing approval) or Reject with a note and a rejection reason (Purchasing › Definitions › Rejection Reasons). Lines cannot be added or removed here.\nRelated: My Requests, Awaiting Purchasing Approval, All Requests."
|
"Value": "Awaiting Department Approval — material requests waiting for the department approver's decision.\nWhere: Purchasing › Material Requests › Awaiting Department Approval.\nWho: approvers defined in Administration › Definitions › Approvers with area Purchasing for the requester's department; each approver sees only the requests assigned to them.\nWhat to do: open the request, review the Request Lines tab, then Approve (the request moves to purchasing approval) or Reject by choosing a rejection reason (Purchasing › Definitions › Rejection Reasons) and an optional note; the reason and note are written to the approval description. Lines cannot be added or removed here.\nRelated: My Requests, Awaiting Purchasing Approval, All Requests."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3467add9-46c6-59ea-b3ea-19d8524bb6bb",
|
"Id": "3467add9-46c6-59ea-b3ea-19d8524bb6bb",
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequestDeptApproval.Desc",
|
"Key": "App.Prc.MaterialRequestDeptApproval.Desc",
|
||||||
"Value": "Departman Onayı Bekleyenler — departman onaycısının kararını bekleyen malzeme talepleri.\nNerede: Satınalma › Satınalma Talepleri › Departman Onayı Bekleyenler.\nKim: Yönetim › Tanımlamalar › Onaycılar'da talep edenin departmanı için Satınalma onay yerinde tanımlı onaycılar; her onaycı yalnız kendisine düşen talepleri görür.\nNe yapılır: talebi açıp Talep Satırları sekmesini inceleyin, ardından Onayla (talep satınalma onayına geçer) ya da not ve red nedeniyle (Satınalma › Tanımlamalar › Red Nedenleri) Reddet. Burada satır eklenip silinemez.\nİlişkili: Taleplerim, Satınalma Onayı Bekleyenler, Tüm Talepler."
|
"Value": "Departman Onayı Bekleyenler — departman onaycısının kararını bekleyen malzeme talepleri.\nNerede: Satınalma › Malzeme Talepleri › Departman Onayı Bekleyenler.\nKim: Yönetim › Tanımlamalar › Onaycılar'da talep edenin departmanı için Satınalma onay yerinde tanımlı onaycılar; her onaycı yalnız kendisine düşen talepleri görür.\nNe yapılır: talebi açıp Talep Satırları sekmesini inceleyin, ardından Onayla (talep satınalma onayına geçer) ya da red nedeni (Satınalma › Tanımlamalar › Red Nedenleri) seçip isteğe bağlı notla Reddet; neden ve not onay açıklamasına yazılır. Burada satır eklenip silinemez.\nİlişkili: Taleplerim, Satınalma Onayı Bekleyenler, Tüm Talepler."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "1c9a636f-fdfa-50bf-b3a4-874deed5dad1",
|
"Id": "1c9a636f-fdfa-50bf-b3a4-874deed5dad1",
|
||||||
|
|
@ -93837,14 +93669,14 @@
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequestPurchaseApproval.Desc",
|
"Key": "App.Prc.MaterialRequestPurchaseApproval.Desc",
|
||||||
"Value": "Awaiting Purchasing Approval — material requests approved by the department and waiting for purchasing.\nWhere: Purchasing › Material Requests › Awaiting Purchasing Approval.\nWho: approvers defined in Administration › Definitions › Approvers with area Purchasing Desk (any department); each approver sees only the requests assigned to them.\nWhat to do: review the request and its lines, then Approve (the request becomes Approved and appears in Awaiting Planning, the requester is notified) or Reject with a note and a rejection reason.\nRelated: Awaiting Department Approval, Awaiting Planning, All Requests."
|
"Value": "Awaiting Purchasing Approval — material requests approved by the department and waiting for purchasing.\nWhere: Purchasing › Material Requests › Awaiting Purchasing Approval.\nWho: users with update permission on Awaiting Purchasing Approval (directly or through their role); the approver list of each request is refreshed when the request is saved, and each approver sees only the requests assigned to them.\nWhat to do: review the request and its lines, then Approve (the request becomes Approved, the requester is notified) or Reject by choosing a rejection reason and an optional note; both are written to the approval description.\nRelated: Awaiting Department Approval, All Requests."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "bbcdb2c8-96fc-5ae3-b948-1d68b8a334c5",
|
"Id": "bbcdb2c8-96fc-5ae3-b948-1d68b8a334c5",
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequestPurchaseApproval.Desc",
|
"Key": "App.Prc.MaterialRequestPurchaseApproval.Desc",
|
||||||
"Value": "Satınalma Onayı Bekleyenler — departmanca onaylanmış ve satınalmanın kararını bekleyen malzeme talepleri.\nNerede: Satınalma › Satınalma Talepleri › Satınalma Onayı Bekleyenler.\nKim: Yönetim › Tanımlamalar › Onaycılar'da Satınalma Masası onay yerinde tanımlı onaycılar (departmandan bağımsız); her onaycı yalnız kendisine düşen talepleri görür.\nNe yapılır: talebi ve satırlarını inceleyip Onayla (talep Onaylandı olur, Planlama Bekleyenler'e düşer, talep edene bilgi gider) ya da not ve red nedeniyle Reddet.\nİlişkili: Departman Onayı Bekleyenler, Planlama Bekleyenler, Tüm Talepler."
|
"Value": "Satınalma Onayı Bekleyenler — departmanca onaylanmış ve satınalmanın kararını bekleyen malzeme talepleri.\nNerede: Satınalma › Malzeme Talepleri › Satınalma Onayı Bekleyenler.\nKim: Satınalma Onayı Bekleyenler ekranında güncelleme yetkisi olan kullanıcılar (doğrudan ya da rolüyle); talebin onaycı listesi talep kaydedilince yenilenir, her onaycı yalnız kendisine düşen talepleri görür.\nNe yapılır: talebi ve satırlarını inceleyip Onayla (talep Onaylandı olur, talep edene bilgi gider) ya da red nedeni seçip isteğe bağlı notla Reddet; ikisi de onay açıklamasına yazılır.\nİlişkili: Departman Onayı Bekleyenler, Tüm Talepler."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "04e8c193-a841-520d-b338-0d4a73ca066b",
|
"Id": "04e8c193-a841-520d-b338-0d4a73ca066b",
|
||||||
|
|
@ -93879,14 +93711,14 @@
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequest.Desc",
|
"Key": "App.Prc.MaterialRequest.Desc",
|
||||||
"Value": "All Requests — every material request of the company in every status; used by purchasing management.\nWhere: Purchasing › Material Requests › All Requests.\nFields: request no (MT-YYYY-00001, automatic), priority, requester, department, warehouse and location, request date, status (Draft › Department Approval › Purchasing Approval › Approved › In Progress › Partially Done › Done, or Rejected), approval status, rejection reason, general description; approval group: department approvers, department / purchasing approver, date and note; Request Lines tab.\nFlow: Grid Workflow — department approver (Approvers › area Purchasing of the request's department) → purchasing (area Purchasing Desk) → Approved / Rejected; the requester is notified. Status after approval follows the line statuses.\nRelated: My Requests, Awaiting Department Approval, Awaiting Purchasing Approval, Awaiting Planning, Purchasing › Definitions › Rejection Reasons."
|
"Value": "All Requests — every material request of the company in every status, listed per request line; list of purchasing management.\nWhere: Purchasing › Material Requests › All Requests.\nFields: each row is one request line — request header (request no, priority, requester, department, warehouse and location, request date, general description, approval status / approver / date / description) next to the line (product, quantity, unit, fulfillment method, due date, reason, delivered quantity, purchasing responsible, line status). A request without lines is shown as a single row with empty line columns.\nFlow: requests are created in My Requests and approved in Awaiting Department Approval › Awaiting Purchasing Approval; this list does not add or delete; header fields and the approval status written by that workflow are read-only, editing a row only sets the line's purchasing responsible and line status. Cards count requests, not lines.\nRelated: My Requests, Awaiting Department Approval, Awaiting Purchasing Approval, Purchasing › Definitions › Rejection Reasons."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "b9ef44e8-a325-544f-b552-bda2fb6ca9c1",
|
"Id": "b9ef44e8-a325-544f-b552-bda2fb6ca9c1",
|
||||||
"CultureName": "tr",
|
"CultureName": "tr",
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Prc.MaterialRequest.Desc",
|
"Key": "App.Prc.MaterialRequest.Desc",
|
||||||
"Value": "Tüm Talepler — kurumun bütün malzeme talepleri, tüm durumlarıyla; satınalma yönetiminin listesi.\nNerede: Satınalma › Satınalma Talepleri › Tüm Talepler.\nAlanlar: talep no (MT-YYYY-00001, otomatik), öncelik, talep eden, departman, depo ve konum, talep tarihi, durum (Taslak › Departman Onayı › Satınalma Onayı › Onaylandı › İşlemde › Kısmi Tamamlandı › Tamamlandı ya da Reddedildi), onay durumu, red nedeni, genel açıklama; onay grubu: departman onaycıları, departman / satınalma onaylayanı, tarihi ve notu; Talep Satırları sekmesi.\nAkış: Grid Workflow — departman onaycısı (Onaycılar › talebin departmanı için Satınalma onay yeri) → satınalma (Satınalma Masası onay yeri) → Onaylandı / Reddedildi; talep edene bilgi gider. Onay sonrası durum satır durumlarını izler.\nİlişkili: Taleplerim, Departman Onayı Bekleyenler, Satınalma Onayı Bekleyenler, Planlama Bekleyenler, Satınalma › Tanımlamalar › Red Nedenleri."
|
"Value": "Tüm Talepler — kurumun bütün malzeme talepleri, tüm durumlarıyla, kalem kalem; satınalma yönetiminin listesi.\nNerede: Satınalma › Malzeme Talepleri › Tüm Talepler.\nAlanlar: her satır bir talep kalemidir — talep başlığı (talep no, öncelik, talep eden, departman, depo ve konum, talep tarihi, genel açıklama, onay durumu / onaylayan / tarih / açıklama) ile kalemin bilgileri (ürün, miktar, birim, karşılama yöntemi, termin, talep nedeni, teslim edilen miktar, satınalma sorumlusu, satır durumu) yan yanadır. Kalemi olmayan talep, kalem kolonları boş tek satır olarak görünür.\nAkış: talep Taleplerim ekranında açılır, Departman Onayı Bekleyenler › Satınalma Onayı Bekleyenler ekranlarında onaylanır; bu liste ekleme ve silme yapmaz; başlık alanları ve o iş akışının yazdığı onay durumu salt okunurdur, satırı düzenlemek yalnız kalemin satınalma sorumlusunu ve satır durumunu değiştirir. Kartlar kalemi değil talebi sayar.\nİlişkili: Taleplerim, Departman Onayı Bekleyenler, Satınalma Onayı Bekleyenler, Satınalma › Tanımlamalar › Red Nedenleri."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3d2aa673-6148-5445-9fb8-c260913a910f",
|
"Id": "3d2aa673-6148-5445-9fb8-c260913a910f",
|
||||||
|
|
@ -93929,6 +93761,202 @@
|
||||||
"ResourceName": "Platform",
|
"ResourceName": "Platform",
|
||||||
"Key": "App.Tenant.Name",
|
"Key": "App.Tenant.Name",
|
||||||
"Value": "Kiracı Adı"
|
"Value": "Kiracı Adı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "4b3114be-222a-5abf-ae77-d2ad2a321e7a",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormEdit.DesignTable",
|
||||||
|
"Value": "Design Table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "95839291-2e69-5129-9796-904fd18dbcee",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormEdit.DesignTable",
|
||||||
|
"Value": "Tablo Tasarımı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "973de671-71f2-547d-97e7-fe6277cf08ed",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.WorkflowNote.RejectReason",
|
||||||
|
"Value": "Reject Reason"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "89ce6320-14b2-54ce-b2fc-9ec20fa9361f",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.WorkflowNote.RejectReason",
|
||||||
|
"Value": "Red Nedeni"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "20cc436a-9277-55bf-8e07-2a79179af9a4",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonRequired",
|
||||||
|
"Value": "A reject reason must be selected."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "5b8cab4e-6616-5681-bd2d-68e42568cdd7",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonRequired",
|
||||||
|
"Value": "Red nedeni seçilmelidir."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "1c5d7dde-28cc-5b2f-8f3c-b6a31a981174",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonNotFound",
|
||||||
|
"Value": "The selected reject reason was not found."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "c709ef5d-57bb-518c-b67d-6182113131c1",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonNotFound",
|
||||||
|
"Value": "Seçilen red nedeni bulunamadı."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "523e57cb-24fe-5087-8682-6adf60ee54e5",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTableNotFound",
|
||||||
|
"Value": "Reject reason table was not found: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "1f7a2c1b-20c1-5717-8c5b-32cba96482b3",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTableNotFound",
|
||||||
|
"Value": "Red nedeni tablosu bulunamadı: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "8b9dcc7a-3d32-5cec-a94b-beb86f4c6900",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonColumnNotFound",
|
||||||
|
"Value": "Column '{0}' was not found in reject reason table '{1}'."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "3f9232e6-4791-5489-a24b-422fdaa574c5",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonColumnNotFound",
|
||||||
|
"Value": "'{1}' red nedeni tablosunda '{0}' kolonu yok."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "3bfaf9e8-3913-590d-8636-5cc69c10990c",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonSection",
|
||||||
|
"Value": "Reject Reason List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "3be0012c-ebc7-562c-8585-1fc376aef1c0",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonSection",
|
||||||
|
"Value": "Red Nedeni Listesi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "27ff219a-0dfe-50b9-98da-ee9564329905",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonHint",
|
||||||
|
"Value": "Leave the table empty to ask only for a description on reject."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "f6ddb820-f67d-5073-8a59-feaa977be779",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonHint",
|
||||||
|
"Value": "Tablo boş bırakılırsa red sırasında yalnızca açıklama sorulur."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "966f9fd2-8a64-547b-8f2b-7c39508b1837",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTable",
|
||||||
|
"Value": "Table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "6da2c479-85f2-5885-beb0-08fdce43abc6",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTable",
|
||||||
|
"Value": "Tablo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "13863cf5-8d59-57cc-a4ae-e3100fc0616d",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonValueField",
|
||||||
|
"Value": "Id Column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "bbfba405-1d51-551e-943f-9bcb67af3e0d",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonValueField",
|
||||||
|
"Value": "Id Kolonu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "4da6dc9f-68a7-5f5a-8153-b3b340372684",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTextField",
|
||||||
|
"Value": "Text Column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "c439c7f8-3cae-54ea-87b7-a6ce2da43e0e",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTextField",
|
||||||
|
"Value": "Metin Kolonu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "a10b8a0d-6a04-5043-893b-da40f739f306",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTargetField",
|
||||||
|
"Value": "Record Field for Id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "8719ba9a-7314-5b95-a38e-1acbd0dab6b0",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonTargetField",
|
||||||
|
"Value": "Id'nin Yazılacağı Kayıt Alanı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "1059eb89-9b04-5524-9272-e40634b3545f",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonIsRequired",
|
||||||
|
"Value": "Selection required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "05786192-1daf-52ef-90b7-daaf730bcfce",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonIsRequired",
|
||||||
|
"Value": "Seçim zorunlu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "634f4c35-51bf-5f1c-9668-ad50814d1f2e",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonPlaceholder",
|
||||||
|
"Value": "Select a reject reason"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "6de88842-9612-556a-9162-048c6eaee634",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "App.ListFormWorkflow.RejectReasonPlaceholder",
|
||||||
|
"Value": "Red nedeni seçin"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ Yetki ağacı: `App.Administration` grubu › `App.Administration.Definitions`
|
||||||
|
|
||||||
### 7. Entegrasyonlar
|
### 7. Entegrasyonlar
|
||||||
|
|
||||||
Dış entegrasyon yok. Ülke/Şehir/İlçe, Para Birimi, Dil, Sektör, Kullanıcılar, Departmanlar, Şubeler ve Satınalma tanımları yalnızca okunur. Onaycılar'ı Satınalma › Satınalma Talepleri departman onayı (`ApprovalArea = 'Purchase'`) okuyacaktır; diğer onay yerleri ilgili modüllerin akışları için hazırdır.
|
Dış entegrasyon yok. Ülke/Şehir/İlçe, Para Birimi, Dil, Sektör, Kullanıcılar, Departmanlar, Şubeler ve Satınalma tanımları yalnızca okunur. Onaycılar'ı Satınalma › Malzeme Talepleri departman onayı (`ApprovalArea = 'Purchase'`) okuyacaktır; diğer onay yerleri ilgili modüllerin akışları için hazırdır.
|
||||||
|
|
||||||
### 8. Akış örneği
|
### 8. Akış örneği
|
||||||
|
|
||||||
|
|
@ -178,7 +178,7 @@ Dış entegrasyon yok. Ülke/Şehir/İlçe, Para Birimi, Dil, Sektör, Kullanıc
|
||||||
| Menü | `App.Administration.Definitions` › `App.Definitions.Approver` (sıra 6) |
|
| Menü | `App.Administration.Definitions` › `App.Definitions.Approver` (sıra 6) |
|
||||||
| Yetki | `App.Definitions.Approver` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Definitions.Approver` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Onaycılar — uygulamadaki tüm onay adımlarında her departman için kimin onaylayacağını tutan tek liste. |
|
| Açıklama | Onaycılar — uygulamadaki tüm onay adımlarında her departman için kimin onaylayacağını tutan tek liste. |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Adm_T_Approver` · filtre `"IsDeleted" = 'false'` |
|
| Kaynak | `Adm_T_Approver` · filtre `"IsDeleted" = 'false'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
||||||
|
|
@ -190,7 +190,7 @@ Dış entegrasyon yok. Ülke/Şehir/İlçe, Para Birimi, Dil, Sektör, Kullanıc
|
||||||
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
||||||
| `ApprovalArea` | Onay Yeri | dxSelectBox | ✓ | ✓ | liste: Purchase, PurchaseDesk, Warehouse, HumanResources, Finance |
|
| `ApprovalArea` | Onay Yeri | dxSelectBox | ✓ | ✓ | liste: Purchase, Warehouse, HumanResources, Finance |
|
||||||
| `ApproverUserId` | Onaylayacak Kişi | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
|
| `ApproverUserId` | Onaylayacak Kişi | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
|
||||||
| `IsActive` | Aktif | dxCheckBox | ✓ | ✓ | |
|
| `IsActive` | Aktif | dxCheckBox | ✓ | ✓ | |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,54 +67,58 @@ Yok. Tanımlar Yönetim › Tanımlamalar › Kontaklar'ın Satış ve Satınalm
|
||||||
- Geri alma: Kontak sekmeleri bu tanımlara bağlı olduğu için önce kontak tarafındaki seçimler temizlenir; ekranlar `delete_wizard` ile silinir, tablolar yalnızca açık onayla `dropTables: true` ile düşürülür.
|
- Geri alma: Kontak sekmeleri bu tanımlara bağlı olduğu için önce kontak tarafındaki seçimler temizlenir; ekranlar `delete_wizard` ile silinir, tablolar yalnızca açık onayla `dropTables: true` ile düşürülür.
|
||||||
|
|
||||||
<!-- module-doc:section App.Prc.MaterialRequests -->
|
<!-- module-doc:section App.Prc.MaterialRequests -->
|
||||||
## Satınalma Talepleri (`App.Prc.MaterialRequests`)
|
## Malzeme Talepleri (`App.Prc.MaterialRequests`)
|
||||||
|
|
||||||
### 1. Amaç ve kapsam
|
### 1. Amaç ve kapsam
|
||||||
|
|
||||||
Departmanların iç ve dış ihtiyaç duydukları malzemeleri satınalmaya iletmesi. Talep eden personel talebi ve satırlarını girer, onaya gönderir; talep önce talep edenin departman onaycısına, sonra satınalmaya (Satınalma Masası) düşer. Onaylanan talep Tüm Talepler listesinde satınalma ekibinin işine düşer; teklif toplama süreci buradan başlar. Teklif, sipariş, transfer ve stok miktarı bu dalın kapsamında değildir.
|
Departmanların iç ve dış ihtiyaç duydukları malzemeleri satınalmaya iletmesi. Talep eden personel talebi ve satırlarını girer, onaya gönderir; talep önce talep edenin departman onaycısına, sonra satınalmaya düşer. Onaylanan talep Tüm Talepler listesinde satınalma ekibinin işine düşer; teklif toplama süreci buradan başlar. Teklif, sipariş, transfer ve stok miktarı bu dalın kapsamında değildir.
|
||||||
|
|
||||||
### 2. Analiz ve kararlar
|
### 2. Analiz ve kararlar
|
||||||
|
|
||||||
- Wizard List ekranları + menüsüz satır alt formu + Grid Workflow. Beş liste aynı tabloyu (`Prc_T_MaterialRequest`) filtreyle gösterir.
|
- Wizard List ekranları + menüsüz satır alt formu + Grid Workflow. Taleplerim ve iki onay listesi aynı tabloyu (`Prc_T_MaterialRequest`) filtreyle gösterir; Tüm Talepler başlık × kalem görünümünden (`Prc_V_MaterialRequestLine`) okur, her kalem bir satırdır.
|
||||||
- Belge no numaratörden: `MaterialRequest` (MT-YYYY-00001, yıllık sıfırlanır). Şirket alanı açılmadı (kiracı kapsamı).
|
- Belge no numaratörden: `MaterialRequest` (MT-YYYY-00001, yıllık sıfırlanır). Şirket alanı açılmadı (kiracı kapsamı).
|
||||||
- Departman onaycısı: `Adm_T_Approver`'da Onay Yeri = Satınalma (`Purchase`) ve talebin departmanı. Satınalma onaycısı: Onaycılar'a eklenen yeni Onay Yeri **Satınalma Masası** (`PurchaseDesk`) satırları, departmandan bağımsız. Herhangi bir onaycının onayı yeterli.
|
- Departman onaycısı: `Adm_T_Approver`'da Onay Yeri = Satınalma (`Purchase`) ve talebin departmanı. Satınalma onaycısı Onaycılar'da tanımlanmaz: Satınalma Onayı Bekleyenler ekranında karar verebilen, yani `App.Prc.MaterialRequestPurchaseApproval.Update` yetkisi doğrudan ya da rolüyle verilmiş aktif kullanıcılardır (satınalma ekibinin rolüne bu yetki verilir). Herhangi bir onaycının onayı yeterli.
|
||||||
- Taslak aşaması: kayıt Taslak açılır; akış platformun Onaya Gönder işlemiyle başlar (`WorkflowDto.ApprovalIsManualStart: true` — yoksa yeni kayıt akışı kendiliğinden başlatır ve satır girilemez).
|
- Taslak aşaması: kayıt Taslak açılır; akış platformun Onaya Gönder işlemiyle başlar (`WorkflowDto.ApprovalIsManualStart: true` — yoksa yeni kayıt akışı kendiliğinden başlatır ve satır girilemez).
|
||||||
- Görseldeki "Kendi Depomda" ve "Tüm Depolar" stok kolonları üretilmedi: uygulamada stok miktarı/hareket tablosu yok; stok modülü gelince ayrı talep.
|
- Görseldeki "Kendi Depomda" ve "Tüm Depolar" stok kolonları üretilmedi: uygulamada stok miktarı/hareket tablosu yok; stok modülü gelince ayrı talep.
|
||||||
- Karşılama Şekli: Satınalma / Depodan Transfer.
|
- Karşılama Şekli: Satınalma / Depodan Transfer.
|
||||||
- Onay akışının "onaylayan" alanı platformda kullanıcı adı (metin) yazdığı için akışa bağlanmadı (`ApprovalUserFieldName` boş); departman/satınalma onaylayanı kayıt sonrası prosedürde kaydı son güncelleyen kullanıcıdan yazılır. `ApproverId` kolonu kullanılmaz.
|
- İş akışı kolonları tablo tasarımcısının Workflow şablonundaki dörtlüdür: `ApprovalUserName` nvarchar(256) (onaycı kullanıcı adları), `ApprovalStatus` nvarchar(50) (bulunulan düğümün başlığı), `ApprovalDate` datetime, `ApprovalDescription` nvarchar(200); dört ekranın `WorkflowDto`'su bunlara bağlıdır. Departman ve satınalma kararları ayrı kolonda tutulmaz; her karar (karar veren, açıklama, red nedeni) iş akışı notlarına yazılır.
|
||||||
|
- Onay ve red bilgileri popup düzenleme formunda yer almaz (iş akışı yazar); listede kolon olarak görünür.
|
||||||
|
- Red nedeni ayrı kolonda tutulmaz: onay düğümleri red sırasında Red Nedenleri listesinden (`Prc_T_RejectReason`, zorunlu) seçim ister; seçilen neden ve not `ApprovalDescription`'a ve iş akışı notuna yazılır.
|
||||||
- Satır tablosu `Prc_T_MaterialRequestItem` adıyla açıldı (birim kolonu `Adm_T_Uom.Id` ile aynı tipte, nvarchar). İlk açılan `Prc_T_MaterialRequestLine` scripti (UomId uniqueidentifier) kullanılmıyor.
|
- Satır tablosu `Prc_T_MaterialRequestItem` adıyla açıldı (birim kolonu `Adm_T_Uom.Id` ile aynı tipte, nvarchar). İlk açılan `Prc_T_MaterialRequestLine` scripti (UomId uniqueidentifier) kullanılmıyor.
|
||||||
- Talep durumu satırlardan türetilir; ayrı Tamamlandı butonu yerine satır durumu (Bekliyor / Tamamlandı / İptal) kullanılır.
|
- Talebin durumu yalnız iş akışından gelir: `ApprovalStatus` boş (taslak) › Departman Onayı › Satınalma Onayı › Onaylandı ya da Reddedildi. Ayrı `Status` kolonu yoktur; karşılama satır durumundan (Bekliyor / Tamamlandı / İptal) izlenir.
|
||||||
- Planlama aşaması kaldırıldı (Planlama Bekleyenler listesi, satırdaki Planlandı durumu ve Planlanacak Miktar). İşlemde (`InProgress`) durumu korunur: talep depoya girdiğinde kullanılacak, şimdilik hiçbir kural yazmaz.
|
- Planlama aşaması kaldırıldı (Planlama Bekleyenler listesi, satırdaki Planlandı durumu ve Planlanacak Miktar).
|
||||||
|
|
||||||
### 3. Menü ve ekranlar
|
### 3. Menü ve ekranlar
|
||||||
|
|
||||||
Satınalma (`App.Prc`) › Satınalma Talepleri (`App.Prc.MaterialRequests`):
|
Satınalma (`App.Prc`) › Malzeme Talepleri (`App.Prc.MaterialRequests`):
|
||||||
|
|
||||||
| Ekran | Kod | Filtre / not |
|
| Ekran | Kod | Filtre / not |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Taleplerim | `App.Prc.MyMaterialRequest` | talep eden = oturumdaki kullanıcı; ekle/sil açık, akış |
|
| Taleplerim | `App.Prc.MyMaterialRequest` | talep eden = oturumdaki kullanıcı; ekle/sil açık, akış |
|
||||||
| Departman Onayı Bekleyenler | `App.Prc.MaterialRequestDeptApproval` | ApprovalStatus = Departman Onayı; yalnız kendine düşenler |
|
| Departman Onayı Bekleyenler | `App.Prc.MaterialRequestDeptApproval` | ApprovalStatus = Departman Onayı; yalnız kendine düşenler |
|
||||||
| Satınalma Onayı Bekleyenler | `App.Prc.MaterialRequestPurchaseApproval` | ApprovalStatus = Satınalma Onayı; yalnız kendine düşenler |
|
| Satınalma Onayı Bekleyenler | `App.Prc.MaterialRequestPurchaseApproval` | ApprovalStatus = Satınalma Onayı; yalnız kendine düşenler |
|
||||||
| Tüm Talepler | `App.Prc.MaterialRequest` | filtresiz; ekle/sil açık, akış |
|
| Tüm Talepler | `App.Prc.MaterialRequest` | filtresiz, kalem kalem; ekle/sil kapalı, yalnız kalemin sorumlusu ve durumu düzenlenir, akış yok |
|
||||||
|
|
||||||
Talep Satırları (`App.Prc.MaterialRequestLine`) menüsüz alt formdur; beş listenin hepsinde sekmedir. Red Nedenleri (`App.Prc.RejectReason`) Satınalma › Tanımlamalar altındadır (bkz. Tanımlamalar).
|
Talep Satırları (`App.Prc.MaterialRequestLine`) menüsüz alt formdur; Taleplerim ve iki onay listesinde sekmedir (Tüm Talepler kalemleri kolon olarak gösterir). Red Nedenleri (`App.Prc.RejectReason`) Satınalma › Tanımlamalar altındadır (bkz. Tanımlamalar).
|
||||||
|
|
||||||
### 4. Veri modeli
|
### 4. Veri modeli
|
||||||
|
|
||||||
Tüm tablolar kiracı + tam audit + soft delete taşır.
|
Tüm tablolar kiracı + tam audit + soft delete taşır.
|
||||||
|
|
||||||
- `Prc_T_MaterialRequest`: RequestNo, Priority (0–3), RequesterUserId, DepartmentId, WarehouseId, LocationId, RequestDate, Description, Status (Draft / DeptApproval / PurchaseApproval / Approved / InProgress / PartiallyDone / Done / Rejected), ApprovalStatus, ApprovalDate, ApprovalNote (akış), DeptApproverUserNames / PurchaseApproverUserNames (`|` ayrılmış kullanıcı adları), DeptApprovedBy/Date/Note, PurchaseApprovedBy/Date/Note, RejectReasonId, ApproverId (kullanılmaz).
|
- `Prc_T_MaterialRequest`: RequestNo, Priority (0–3), RequesterUserId, DepartmentId, WarehouseId, LocationId, RequestDate, Description, ApprovalUserName, ApprovalStatus, ApprovalDate, ApprovalDescription (iş akışı), DeptApproverUserNames / PurchaseApproverUserNames (`|` ayrılmış kullanıcı adları).
|
||||||
- `Prc_T_MaterialRequestItem`: MaterialRequestId (FK, cascade), ProductId, Quantity, UomId (nvarchar, `Adm_T_Uom`), FulfillmentMethod (Purchase / Transfer), DueDate, RequestReason, DeliveredQuantity, PurchaseResponsibleUserId, LineStatus (Waiting / Done / Cancelled).
|
- `Prc_T_MaterialRequestItem`: MaterialRequestId (FK, cascade), ProductId, Quantity, UomId (nvarchar, `Adm_T_Uom`), FulfillmentMethod (Purchase / Transfer), DueDate, RequestReason, DeliveredQuantity, PurchaseResponsibleUserId, LineStatus (Waiting / Done / Cancelled).
|
||||||
- `Prc_T_RejectReason`: Name, SortOrder, IsActive.
|
- `Prc_T_RejectReason`: Name, SortOrder, IsActive.
|
||||||
- Prosedür `Prc_P_MaterialRequestAfterSave(@Id, @UserId)`.
|
- Görünüm `Prc_V_MaterialRequestLine`: talep başlığı LEFT JOIN silinmemiş kalemler; `Id` kalemin Id'si (kalemi olmayan talepte talebin Id'si), `MaterialRequestId` talep. Tüm Talepler'in kaynağıdır.
|
||||||
|
- Prosedür `Prc_P_MaterialRequestAfterSave(@Id)`.
|
||||||
|
|
||||||
### 5. İş kuralları ve hesaplar
|
### 5. İş kuralları ve hesaplar
|
||||||
|
|
||||||
- `Prc_P_MaterialRequestAfterSave` talep kaydı/güncellemesi ve satır ekleme/güncelleme/silme sonrasında çalışır: onaycı kullanıcı listelerini Onaycılar'dan yeniden yazar; ApprovalStatus departman adımını geçince departman onay bilgilerini, Onaylandı/Reddedildi olunca satınalma onay bilgilerini (onaylayan, tarih, not) kopyalar; Status'u türetir: Departman Onayı → DeptApproval, Satınalma Onayı → PurchaseApproval, Reddedildi → Rejected, Onaylandı → satırlara göre (tümü Tamamlandı/İptal → Done, bir kısmı Tamamlandı → PartiallyDone, değilse Approved), boş → Draft.
|
- `Prc_P_MaterialRequestAfterSave` talep kaydı/güncellemesi sonrasında çalışır ve onaycı kullanıcı listelerini (`DeptApproverUserNames`, `PurchaseApproverUserNames`) Onaycılar'dan yeniden yazar. Onaycılar sonradan değişirse talep yeniden kaydedilene kadar liste eski kalır. İki liste formda ve listede gizlidir; iş akışı onaycıyı bu alanlardan okur (`@DeptApproverUserNames`, `@PurchaseApproverUserNames`).
|
||||||
- Onaya gönderilmiş talebe satır eklenemez ve satır silinemez (alt form Insert/Delete Before komutu). Onay sürecindeki ya da onaylanmış talep silinemez; reddedilen talep silinebilir.
|
- Onaya gönderilmiş talebe satır eklenemez ve satır silinemez (alt form Insert/Delete Before komutu). Onay sürecindeki ya da onaylanmış talep silinemez; reddedilen talep silinebilir.
|
||||||
- Ürün listesi yalnız aktif ve satın alınabilir ürünleri gösterir; birim ürünün satınalma biriminden (yoksa biriminden) kopyalanır, salt okunurdur.
|
- Ürün listesi yalnız aktif ve satın alınabilir ürünleri gösterir; birim ürünün satınalma biriminden (yoksa biriminden) kopyalanır, salt okunurdur.
|
||||||
- Depo değişince konum temizlenir; konum listesi depoya göre süzülür. Talep eden değişince departmanı kullanıcı kartından gelir; yeni kayıtta departman oturumdaki kullanıcının departmanıdır.
|
- Depo değişince konum temizlenir; konum listesi depoya göre süzülür. Talep eden değişince departmanı kullanıcı kartından gelir; yeni kayıtta departman oturumdaki kullanıcının departmanıdır.
|
||||||
- Onaycı listesi boşsa akış o adımda hata verir: Onaycılar'a ilgili departman için Satınalma ve en az bir Satınalma Masası satırı tanımlanmalıdır.
|
- Tüm Talepler'de düzenleme `UpdateCommand` ile yalnız `Prc_T_MaterialRequestItem`'ın `PurchaseResponsibleUserId` ve `LineStatus` kolonlarını yazar; başlık alanları salt okunurdur. Kartlar kalemi değil talebi sayar (tablodan okur).
|
||||||
|
- Onaycı listesi boşsa akış o adımda hata verir: Onaycılar'a ilgili departman için Satınalma satırı tanımlanmalı ve en az bir kullanıcıya (ya da rolüne) Satınalma Onayı Bekleyenler güncelleme yetkisi verilmelidir.
|
||||||
|
|
||||||
### 6. Yetki ve görünürlük
|
### 6. Yetki ve görünürlük
|
||||||
|
|
||||||
|
|
@ -122,26 +126,27 @@ Yetki ağacı: `App.Prc` › `App.Prc.MaterialRequests` › her liste (+ Create/
|
||||||
|
|
||||||
### 7. Entegrasyonlar
|
### 7. Entegrasyonlar
|
||||||
|
|
||||||
Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma Masası); Envanter › Tanımlamalar › Ürünler, Depolar, Konumlar; İK departmanları; Yönetim › Tanımlamalar › Diziler (`MaterialRequest`). Akış e-posta bildirimi platformun iş akışı altyapısındadır. Teklif/sipariş modülüyle entegrasyon yok.
|
Yönetim › Tanımlamalar › Onaycılar (onay yeri Satınalma); Yönetim › Roller (Satınalma Onayı Bekleyenler yetkisi); Envanter › Tanımlamalar › Ürünler, Depolar, Konumlar; İK departmanları; Yönetim › Tanımlamalar › Diziler (`MaterialRequest`). Akış e-posta bildirimi platformun iş akışı altyapısındadır. Teklif/sipariş modülüyle entegrasyon yok.
|
||||||
|
|
||||||
### 8. Akış örneği
|
### 8. Akış örneği
|
||||||
|
|
||||||
1. Onaycılar'da BT departmanı için Satınalma yerinde Ayşe, Satınalma Masası yerinde Mehmet tanımlıdır.
|
1. Onaycılar'da BT departmanı için Satınalma yerinde Ayşe tanımlıdır; Mehmet'in rolünde Satınalma Onayı Bekleyenler güncelleme yetkisi vardır.
|
||||||
2. BT'den Ali Taleplerim'de yeni talep açar (MT-2026-00001), depo/konum seçer, Talep Satırları'na 10 adet ürün, termin ve neden girer; Onaya Gönder der.
|
2. BT'den Ali Taleplerim'de yeni talep açar (MT-2026-00001), depo/konum seçer, Talep Satırları'na 10 adet ürün, termin ve neden girer; Onaya Gönder der.
|
||||||
3. Ayşe Departman Onayı Bekleyenler'de talebi onaylar; talep Satınalma Onayı Bekleyenler'e geçer.
|
3. Ayşe Departman Onayı Bekleyenler'de talebi onaylar; talep Satınalma Onayı Bekleyenler'e geçer.
|
||||||
4. Mehmet onaylar; talep Onaylandı olur, Ali'ye bilgi gider.
|
4. Mehmet onaylar; talep Onaylandı olur, Ali'ye bilgi gider.
|
||||||
5. Satınalma Tüm Talepler'de satıra sorumluyu atar ve satırı Tamamlandı yapar (talep Tamamlandı).
|
5. Satınalma Tüm Talepler'de satıra sorumluyu atar ve satırı Tamamlandı yapar.
|
||||||
|
|
||||||
### 9. Sık sorulanlar
|
### 9. Sık sorulanlar
|
||||||
|
|
||||||
- **Talebim onay listesinde görünmüyor?** Onaycılar'da talebin departmanı için aktif Satınalma satırı olmalı; satınalma adımı için aktif Satınalma Masası satırı gerekir.
|
- **Talebim onay listesinde görünmüyor?** Onaycılar'da talebin departmanı için aktif Satınalma satırı olmalı; satınalma adımı için en az bir aktif kullanıcının (doğrudan ya da rolüyle) Satınalma Onayı Bekleyenler güncelleme yetkisi olmalıdır. Onaycılar ya da yetkiler talep açıldıktan sonra değiştiyse talebi bir kez kaydedin.
|
||||||
- **Onaya gönderdikten sonra satır ekleyebilir miyim?** Hayır; talep reddedilirse ya da akış başa dönerse yeniden düzenlenir.
|
- **Onaya gönderdikten sonra satır ekleyebilir miyim?** Hayır; talep reddedilirse ya da akış başa dönerse yeniden düzenlenir.
|
||||||
- **Kendi Depomda / Tüm Depolar miktarları nerede?** Stok modülü geldiğinde eklenecek.
|
- **Kendi Depomda / Tüm Depolar miktarları nerede?** Stok modülü geldiğinde eklenecek.
|
||||||
- **Red nedeni nereden seçilir?** Onay listelerindeki talep formunda Red Nedeni alanından (Satınalma › Tanımlamalar › Red Nedenleri).
|
- **Red nedeni nereden seçilir?** Reddet düğmesinin açtığı karar penceresinde, Satınalma › Tanımlamalar › Red Nedenleri listesinden; neden ve not Onay Açıklaması kolonunda görünür.
|
||||||
|
|
||||||
### 10. Dosyalar ve geri alma
|
### 10. Dosyalar ve geri alma
|
||||||
|
|
||||||
- Tablolar: `sql/object/Prc_T_MaterialRequest.sql`, `Prc_T_MaterialRequestItem.sql`, `Prc_T_RejectReason.sql` (+ postgres eşleri); kullanılmayan `Prc_T_MaterialRequestLine.sql`.
|
- Tablolar: `sql/object/Prc_T_MaterialRequest.sql`, `Prc_T_MaterialRequestItem.sql`, `Prc_T_RejectReason.sql` (+ postgres eşleri); kullanılmayan `Prc_T_MaterialRequestLine.sql`.
|
||||||
|
- Görünüm: `sql/object/Prc_V_MaterialRequestLine.sql` (+ postgres).
|
||||||
- Prosedür: `sql/object/Prc_P_MaterialRequestAfterSave.sql` (+ postgres).
|
- Prosedür: `sql/object/Prc_P_MaterialRequestAfterSave.sql` (+ postgres).
|
||||||
- Wizard'lar: `Prc_MaterialRequestLine.json`, `Prc_MyMaterialRequest.json`, `Prc_MaterialRequestDeptApproval.json`, `Prc_MaterialRequestPurchaseApproval.json`, `Prc_MaterialRequest.json`, `Prc_RejectReason.json`; güncellenen `Adm_Approver.json`.
|
- Wizard'lar: `Prc_MaterialRequestLine.json`, `Prc_MyMaterialRequest.json`, `Prc_MaterialRequestDeptApproval.json`, `Prc_MaterialRequestPurchaseApproval.json`, `Prc_MaterialRequest.json`, `Prc_RejectReason.json`; güncellenen `Adm_Approver.json`.
|
||||||
- Numaratör: `App.Definitions.Sequence` › `MaterialRequest`.
|
- Numaratör: `App.Definitions.Sequence` › `MaterialRequest`.
|
||||||
|
|
@ -255,7 +260,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Menü | `App.Prc.Definitions` › `App.Prc.RejectReason` (sıra 5) |
|
| Menü | `App.Prc.Definitions` › `App.Prc.RejectReason` (sıra 5) |
|
||||||
| Yetki | `App.Prc.RejectReason` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Prc.RejectReason` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Red Nedenleri — satınalma taleplerinin reddedilme sebepleri (ihtiyaç yok, miktar uygun değil, yanlış ürün, bütçe uygun değil, diğer). |
|
| Açıklama | Red Nedenleri — satınalma taleplerinin reddedilme sebepleri (ihtiyaç yok, miktar uygun değil, yanlış ürün, bütçe uygun değil, diğer). |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Prc_T_RejectReason` · filtre `"IsDeleted" = 'false'` |
|
| Kaynak | `Prc_T_RejectReason` · filtre `"IsDeleted" = 'false'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
||||||
|
|
@ -270,7 +275,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| `SortOrder` | Sıra | dxNumberBox | ✓ | ✓ | |
|
| `SortOrder` | Sıra | dxNumberBox | ✓ | ✓ | |
|
||||||
| `IsActive` | Aktif | dxCheckBox | ✓ | ✓ | |
|
| `IsActive` | Aktif | dxCheckBox | ✓ | ✓ | |
|
||||||
|
|
||||||
### Satınalma Talepleri (`App.Prc.MaterialRequests`)
|
### Malzeme Talepleri (`App.Prc.MaterialRequests`)
|
||||||
|
|
||||||
#### Taleplerim — `App.Prc.MyMaterialRequest`
|
#### Taleplerim — `App.Prc.MyMaterialRequest`
|
||||||
|
|
||||||
|
|
@ -281,7 +286,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MyMaterialRequest` (sıra 1) |
|
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MyMaterialRequest` (sıra 1) |
|
||||||
| Yetki | `App.Prc.MyMaterialRequest` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Prc.MyMaterialRequest` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Taleplerim — açtığınız malzeme talepleri, tüm durumlarıyla. |
|
| Açıklama | Taleplerim — açtığınız malzeme talepleri, tüm durumlarıyla. |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false' AND "RequesterUserId" = '@USERID'` |
|
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false' AND "RequesterUserId" = '@USERID'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
||||||
|
|
@ -289,7 +294,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
||||||
| Widget'lar | `App.Prc.MaterialRequest.Widget.Mine`, `App.Prc.MaterialRequest.Widget.Pending`, `App.Prc.MaterialRequest.Widget.Approved`, `App.Prc.MaterialRequest.Widget.Rejected` |
|
| Widget'lar | `App.Prc.MaterialRequest.Widget.Mine`, `App.Prc.MaterialRequest.Widget.Pending`, `App.Prc.MaterialRequest.Widget.Approved`, `App.Prc.MaterialRequest.Widget.Rejected` |
|
||||||
| Komutlar | InsertAfter: `Prc_P_MaterialRequestAfterSave` · UpdateAfter: `Prc_P_MaterialRequestAfterSave` · DeleteBefore: SQL |
|
| Komutlar | InsertAfter: `Prc_P_MaterialRequestAfterSave` · UpdateAfter: `Prc_P_MaterialRequestAfterSave` · DeleteBefore: SQL |
|
||||||
| Form varsayılanları | RequestNo=MaterialRequest1, RequesterUserId=@USERID, DepartmentId=, RequestDate=@NOW, Priority=0, Status=Draft |
|
| Form varsayılanları | RequestNo=MaterialRequest1, RequesterUserId=@USERID, DepartmentId=, RequestDate=@NOW, Priority=0 |
|
||||||
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
||||||
|
|
||||||
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
||||||
|
|
@ -301,21 +306,13 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script |
|
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script |
|
||||||
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location (← WarehouseId) |
|
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location (← WarehouseId) |
|
||||||
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
||||||
| `Status` | Durum | dxSelectBox | ✓ | ✓ | liste: Draft, DeptApproval, PurchaseApproval, Approved, InProgress, PartiallyDone, Done, Rejected |
|
|
||||||
| `ApprovalStatus` | Onay Durumu | dxTextBox | | ✓ | |
|
|
||||||
| `RejectReasonId` | Red Nedeni | dxSelectBox | | ✓ | sorgu: Prc_T_RejectReason |
|
|
||||||
| `Description` | Genel Açıklama | dxTextBox | | ✓ | |
|
| `Description` | Genel Açıklama | dxTextBox | | ✓ | |
|
||||||
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
| `ApprovalStatus` | Onay Durumu | dxTextBox | | | |
|
||||||
| `ApproverId` | Onaylayan Kimlik | dxTextBox | | | |
|
| `ApprovalUserName` | Onaylayan Kullanıcı Adı | dxTextBox | | | |
|
||||||
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
||||||
| `ApprovalNote` | Onay Notu | dxTextArea | | | |
|
| `ApprovalDescription` | Onay Açıklaması | dxTextBox | | | |
|
||||||
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | ✓ | |
|
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
||||||
| `PurchaseApprovedBy` | Satınalma Onaylayan | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | | |
|
||||||
| `DeptApprovedBy` | Departman Onaylayan | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
|
||||||
| `PurchaseApprovalDate` | Satınalma Onay Tarihi | dxDateBox | | ✓ | |
|
|
||||||
| `DeptApprovalDate` | Departman Onay Tarihi | dxDateBox | | ✓ | |
|
|
||||||
| `PurchaseApprovalNote` | Satınalma Onay Notu | dxTextArea | | ✓ | |
|
|
||||||
| `DeptApprovalNote` | Departman Onay Notu | dxTextArea | | ✓ | |
|
|
||||||
|
|
||||||
#### Departman Onayı Bekleyenler — `App.Prc.MaterialRequestDeptApproval`
|
#### Departman Onayı Bekleyenler — `App.Prc.MaterialRequestDeptApproval`
|
||||||
|
|
||||||
|
|
@ -326,7 +323,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MaterialRequestDeptApproval` (sıra 2) |
|
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MaterialRequestDeptApproval` (sıra 2) |
|
||||||
| Yetki | `App.Prc.MaterialRequestDeptApproval` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Prc.MaterialRequestDeptApproval` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Departman Onayı Bekleyenler — departman onaycısının kararını bekleyen malzeme talepleri. |
|
| Açıklama | Departman Onayı Bekleyenler — departman onaycısının kararını bekleyen malzeme talepleri. |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false' AND "ApprovalStatus" = N'Departman Onayı'` |
|
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false' AND "ApprovalStatus" = N'Departman Onayı'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✗ · düzenle ✓ · sil ✗ · popup |
|
| Düzenleme | ekle ✗ · düzenle ✓ · sil ✗ · popup |
|
||||||
|
|
@ -334,7 +331,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
||||||
| Widget'lar | `App.Prc.MaterialRequest.Status.DeptApproval`, `App.Prc.MaterialRequest.Status.PurchaseApproval`, `App.Prc.MaterialRequest.Status.Rejected` |
|
| Widget'lar | `App.Prc.MaterialRequest.Status.DeptApproval`, `App.Prc.MaterialRequest.Status.PurchaseApproval`, `App.Prc.MaterialRequest.Status.Rejected` |
|
||||||
| Komutlar | UpdateAfter: `Prc_P_MaterialRequestAfterSave` |
|
| Komutlar | UpdateAfter: `Prc_P_MaterialRequestAfterSave` |
|
||||||
| Form varsayılanları | Priority=0, Status=Draft |
|
| Form varsayılanları | Priority=0 |
|
||||||
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
||||||
|
|
||||||
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
||||||
|
|
@ -346,14 +343,13 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
|
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
|
||||||
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location |
|
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location |
|
||||||
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
||||||
| `Status` | Durum | dxSelectBox | ✓ | ✓ | liste: Draft, DeptApproval, PurchaseApproval, Approved, InProgress, PartiallyDone, Done, Rejected |
|
|
||||||
| `ApprovalStatus` | Onay Durumu | dxTextBox | | ✓ | |
|
|
||||||
| `RejectReasonId` | Red Nedeni | dxSelectBox | | ✓ | sorgu: Prc_T_RejectReason |
|
|
||||||
| `Description` | Genel Açıklama | dxTextArea | | ✓ | |
|
| `Description` | Genel Açıklama | dxTextArea | | ✓ | |
|
||||||
|
| `ApprovalStatus` | Onay Durumu | dxTextBox | | | |
|
||||||
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | | |
|
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | | |
|
||||||
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
||||||
|
| `ApprovalUserName` | Onaylayan Kullanıcı Adı | dxTextBox | | | |
|
||||||
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
||||||
| `ApprovalNote` | Onay Notu | dxTextArea | | | |
|
| `ApprovalDescription` | Onay Açıklaması | dxTextBox | | | |
|
||||||
|
|
||||||
#### Satınalma Onayı Bekleyenler — `App.Prc.MaterialRequestPurchaseApproval`
|
#### Satınalma Onayı Bekleyenler — `App.Prc.MaterialRequestPurchaseApproval`
|
||||||
|
|
||||||
|
|
@ -364,7 +360,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MaterialRequestPurchaseApproval` (sıra 3) |
|
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MaterialRequestPurchaseApproval` (sıra 3) |
|
||||||
| Yetki | `App.Prc.MaterialRequestPurchaseApproval` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Prc.MaterialRequestPurchaseApproval` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Satınalma Onayı Bekleyenler — departmanca onaylanmış ve satınalmanın kararını bekleyen malzeme talepleri. |
|
| Açıklama | Satınalma Onayı Bekleyenler — departmanca onaylanmış ve satınalmanın kararını bekleyen malzeme talepleri. |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false' AND "ApprovalStatus" = N'Satınalma Onayı'` |
|
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false' AND "ApprovalStatus" = N'Satınalma Onayı'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✗ · düzenle ✓ · sil ✗ · popup |
|
| Düzenleme | ekle ✗ · düzenle ✓ · sil ✗ · popup |
|
||||||
|
|
@ -372,7 +368,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
||||||
| Widget'lar | `App.Prc.MaterialRequest.Status.PurchaseApproval`, `App.Prc.MaterialRequest.Status.Approved`, `App.Prc.MaterialRequest.Status.Rejected` |
|
| Widget'lar | `App.Prc.MaterialRequest.Status.PurchaseApproval`, `App.Prc.MaterialRequest.Status.Approved`, `App.Prc.MaterialRequest.Status.Rejected` |
|
||||||
| Komutlar | UpdateAfter: `Prc_P_MaterialRequestAfterSave` |
|
| Komutlar | UpdateAfter: `Prc_P_MaterialRequestAfterSave` |
|
||||||
| Form varsayılanları | Priority=0, Status=Draft |
|
| Form varsayılanları | Priority=0 |
|
||||||
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
||||||
|
|
||||||
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
||||||
|
|
@ -384,17 +380,13 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
|
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
|
||||||
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location |
|
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location |
|
||||||
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
||||||
| `Status` | Durum | dxSelectBox | ✓ | ✓ | liste: Draft, DeptApproval, PurchaseApproval, Approved, InProgress, PartiallyDone, Done, Rejected |
|
|
||||||
| `ApprovalStatus` | Onay Durumu | dxTextBox | | ✓ | |
|
|
||||||
| `RejectReasonId` | Red Nedeni | dxSelectBox | | ✓ | sorgu: Prc_T_RejectReason |
|
|
||||||
| `Description` | Genel Açıklama | dxTextArea | | ✓ | |
|
| `Description` | Genel Açıklama | dxTextArea | | ✓ | |
|
||||||
|
| `ApprovalStatus` | Onay Durumu | dxTextBox | | | |
|
||||||
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | | |
|
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | | |
|
||||||
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
||||||
|
| `ApprovalUserName` | Onaylayan Kullanıcı Adı | dxTextBox | | | |
|
||||||
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
||||||
| `ApprovalNote` | Onay Notu | dxTextArea | | | |
|
| `ApprovalDescription` | Onay Açıklaması | dxTextBox | | | |
|
||||||
| `DeptApprovedBy` | Departman Onaylayan | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
|
||||||
| `DeptApprovalDate` | Departman Onay Tarihi | dxDateBox | | ✓ | |
|
|
||||||
| `DeptApprovalNote` | Departman Onay Notu | dxTextArea | | ✓ | |
|
|
||||||
|
|
||||||
#### Tüm Talepler — `App.Prc.MaterialRequest`
|
#### Tüm Talepler — `App.Prc.MaterialRequest`
|
||||||
|
|
||||||
|
|
@ -404,42 +396,38 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Yol | Liste (Wizard) |
|
| Yol | Liste (Wizard) |
|
||||||
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MaterialRequest` (sıra 5) |
|
| Menü | `App.Prc.MaterialRequests` › `App.Prc.MaterialRequest` (sıra 5) |
|
||||||
| Yetki | `App.Prc.MaterialRequest` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Prc.MaterialRequest` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Tüm Talepler — kurumun bütün malzeme talepleri, tüm durumlarıyla; satınalma yönetiminin listesi. |
|
| Açıklama | Tüm Talepler — kurumun bütün malzeme talepleri, tüm durumlarıyla, kalem kalem; satınalma yönetiminin listesi. |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Prc_T_MaterialRequest` · filtre `"IsDeleted" = 'false'` |
|
| Kaynak | `Prc_V_MaterialRequestLine` · filtre `"IsDeleted" = 'false'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
| Düzenleme | ekle ✗ · düzenle ✓ · sil ✗ · popup |
|
||||||
| Kapsam | Kiracı |
|
| Kapsam | Kiracı |
|
||||||
| Alt formlar | `App.Prc.MaterialRequestLine` (Id → MaterialRequestId) |
|
|
||||||
| Widget'lar | `App.Prc.MaterialRequest.Status.DeptApproval`, `App.Prc.MaterialRequest.Status.PurchaseApproval`, `App.Prc.MaterialRequest.Status.Approved`, `App.Prc.MaterialRequest.Status.Rejected` |
|
| Widget'lar | `App.Prc.MaterialRequest.Status.DeptApproval`, `App.Prc.MaterialRequest.Status.PurchaseApproval`, `App.Prc.MaterialRequest.Status.Approved`, `App.Prc.MaterialRequest.Status.Rejected` |
|
||||||
| Komutlar | InsertAfter: `Prc_P_MaterialRequestAfterSave` · UpdateAfter: `Prc_P_MaterialRequestAfterSave` · DeleteBefore: SQL |
|
|
||||||
| Form varsayılanları | RequestNo=MaterialRequest1, RequesterUserId=@USERID, DepartmentId=, RequestDate=@NOW, Priority=0, Status=Draft |
|
|
||||||
| Onay akışı | Start 'Onaya Gönderildi' → Approval 'Departman Onayı' → Approval 'Satınalma Onayı' → End 'Onaylandı' → End 'Reddedildi' |
|
|
||||||
|
|
||||||
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| `RequestNo` | Talep No | dxTextBox | | ✓ | |
|
| `RequestNo` | Talep No | dxTextBox | | | |
|
||||||
| `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
|
| `Priority` | Öncelik (0-3) | dxRating | ✓ | | |
|
||||||
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script |
|
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | | sorgu: AbpUsers |
|
||||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
| `DepartmentId` | Departman | dxSelectBox | ✓ | | sorgu: Hr_T_Department |
|
||||||
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script |
|
| `WarehouseId` | Depo | dxSelectBox | ✓ | | sorgu: Inv_T_Warehouse |
|
||||||
| `LocationId` | Konum | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Location (← WarehouseId) |
|
| `LocationId` | Konum | dxSelectBox | ✓ | | sorgu: Inv_T_Location (← WarehouseId) |
|
||||||
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | ✓ | |
|
| `RequestDate` | Talep Tarihi | dxDateBox | ✓ | | |
|
||||||
| `Status` | Durum | dxSelectBox | ✓ | ✓ | liste: Draft, DeptApproval, PurchaseApproval, Approved, InProgress, PartiallyDone, Done, Rejected |
|
| `Description` | Genel Açıklama | dxTextBox | | | |
|
||||||
| `ApprovalStatus` | Onay Durumu | dxTextBox | | ✓ | |
|
| `ApprovalStatus` | Onay Durumu | dxTextBox | | | |
|
||||||
| `RejectReasonId` | Red Nedeni | dxSelectBox | | ✓ | sorgu: Prc_T_RejectReason |
|
| `ApprovalUserName` | Onaylayan Kullanıcı Adı | dxTextBox | | | |
|
||||||
| `Description` | Genel Açıklama | dxTextBox | | ✓ | |
|
|
||||||
| `PurchaseApproverUserNames` | Satınalma Onaycıları | dxTextBox | | | |
|
|
||||||
| `ApproverId` | Onaylayan Kimlik | dxTextBox | | | |
|
|
||||||
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
| `ApprovalDate` | Onay Tarihi | dxDateBox | | | |
|
||||||
| `ApprovalNote` | Onay Notu | dxTextArea | | | |
|
| `ApprovalDescription` | Onay Açıklaması | dxTextBox | | | |
|
||||||
| `DeptApproverUserNames` | Departman Onaycıları | dxTextBox | | ✓ | |
|
| `MaterialRequestId` | Malzeme Talebi | dxTextBox | | | |
|
||||||
| `PurchaseApprovedBy` | Satınalma Onaylayan | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
| `ProductId` | Ürün | dxSelectBox | | | sorgu: Inv_T_Product |
|
||||||
| `DeptApprovedBy` | Departman Onaylayan | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
| `Quantity` | Miktar | dxNumberBox | | | |
|
||||||
| `PurchaseApprovalDate` | Satınalma Onay Tarihi | dxDateBox | | ✓ | |
|
| `UomId` | Birim | dxSelectBox | | | sorgu: Adm_T_Uom |
|
||||||
| `DeptApprovalDate` | Departman Onay Tarihi | dxDateBox | | ✓ | |
|
| `FulfillmentMethod` | Karşılama Şekli | dxSelectBox | | | liste: Purchase, Transfer |
|
||||||
| `PurchaseApprovalNote` | Satınalma Onay Notu | dxTextArea | | ✓ | |
|
| `DueDate` | Termin Tarihi | dxDateBox | | | |
|
||||||
| `DeptApprovalNote` | Departman Onay Notu | dxTextArea | | ✓ | |
|
| `RequestReason` | Talep Nedeni | dxTextArea | | | |
|
||||||
|
| `DeliveredQuantity` | Teslim | dxNumberBox | | | |
|
||||||
|
| `PurchaseResponsibleUserId` | Satınalma Sorumlusu | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
||||||
|
| `LineStatus` | Satır Durumu | dxSelectBox | ✓ | ✓ | liste: Waiting, Done, Cancelled |
|
||||||
|
|
||||||
#### Talep Satırları — `App.Prc.MaterialRequestLine`
|
#### Talep Satırları — `App.Prc.MaterialRequestLine`
|
||||||
|
|
||||||
|
|
@ -450,12 +438,12 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| Menü | Menüsüz (alt form) |
|
| Menü | Menüsüz (alt form) |
|
||||||
| Yetki | `App.Prc.MaterialRequestLine` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
| Yetki | `App.Prc.MaterialRequestLine` + `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
||||||
| Açıklama | Talep Satırları — malzeme talebinde istenen ürünler. |
|
| Açıklama | Talep Satırları — malzeme talebinde istenen ürünler. |
|
||||||
| Geliştirme talebi | Satınalma -> Satınalma Talepleri |
|
| Geliştirme talebi | Satınalma -> Malzeme Talepleri |
|
||||||
| Kaynak | `Prc_T_MaterialRequestItem` · filtre `"IsDeleted" = 'false'` |
|
| Kaynak | `Prc_T_MaterialRequestItem` · filtre `"IsDeleted" = 'false'` |
|
||||||
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
| Görünümler | Grid, Card, Pivot, Chart (açılış grid) |
|
||||||
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
| Düzenleme | ekle ✓ · düzenle ✓ · sil ✓ · popup |
|
||||||
| Kapsam | Kiracı |
|
| Kapsam | Kiracı |
|
||||||
| Komutlar | InsertBefore: SQL · InsertAfter: `Prc_P_MaterialRequestAfterSave` · UpdateAfter: `Prc_P_MaterialRequestAfterSave` · DeleteBefore: SQL · DeleteAfter: `Prc_P_MaterialRequestAfterSave` |
|
| Komutlar | InsertBefore: SQL · DeleteBefore: SQL |
|
||||||
| Form varsayılanları | FulfillmentMethod=Purchase, DeliveredQuantity=0, LineStatus=Waiting, Quantity=1, PurchaseResponsibleUserId=@USERID |
|
| Form varsayılanları | FulfillmentMethod=Purchase, DeliveredQuantity=0, LineStatus=Waiting, Quantity=1, PurchaseResponsibleUserId=@USERID |
|
||||||
|
|
||||||
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
|
||||||
|
|
@ -466,7 +454,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
|
||||||
| `UomId` | Birim | dxSelectBox | ✓ | ✓ | sorgu: Adm_T_Uom |
|
| `UomId` | Birim | dxSelectBox | ✓ | ✓ | sorgu: Adm_T_Uom |
|
||||||
| `FulfillmentMethod` | Karşılama Şekli | dxSelectBox | ✓ | ✓ | liste: Purchase, Transfer |
|
| `FulfillmentMethod` | Karşılama Şekli | dxSelectBox | ✓ | ✓ | liste: Purchase, Transfer |
|
||||||
| `DueDate` | Termin Tarihi | dxDateBox | ✓ | ✓ | |
|
| `DueDate` | Termin Tarihi | dxDateBox | ✓ | ✓ | |
|
||||||
| `RequestReason` | Talep Nedeni | dxTextBox | ✓ | ✓ | |
|
| `RequestReason` | Talep Nedeni | dxTextArea | ✓ | ✓ | |
|
||||||
| `DeliveredQuantity` | Teslim | dxNumberBox | | ✓ | |
|
| `DeliveredQuantity` | Teslim | dxNumberBox | | ✓ | |
|
||||||
| `PurchaseResponsibleUserId` | Satınalma Sorumlusu | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
| `PurchaseResponsibleUserId` | Satınalma Sorumlusu | dxSelectBox | | ✓ | sorgu: AbpUsers |
|
||||||
| `LineStatus` | Satır Durumu | dxSelectBox | ✓ | ✓ | liste: Waiting, Done, Cancelled |
|
| `LineStatus` | Satır Durumu | dxSelectBox | ✓ | ✓ | liste: Waiting, Done, Cancelled |
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
CREATE OR REPLACE PROCEDURE public."Prc_P_MaterialRequestAfterSave"(
|
CREATE OR REPLACE PROCEDURE public."Prc_P_MaterialRequestAfterSave"(
|
||||||
"p_Id" uuid,
|
"p_Id" uuid
|
||||||
"p_UserId" uuid DEFAULT NULL
|
|
||||||
)
|
)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
|
|
@ -17,45 +16,20 @@ BEGIN
|
||||||
AND a."TenantId" IS NOT DISTINCT FROM r."TenantId"),
|
AND a."TenantId" IS NOT DISTINCT FROM r."TenantId"),
|
||||||
"PurchaseApproverUserNames" = (
|
"PurchaseApproverUserNames" = (
|
||||||
SELECT string_agg(u."UserName", '|')
|
SELECT string_agg(u."UserName", '|')
|
||||||
FROM "Adm_T_Approver" a
|
FROM "AbpUsers" u
|
||||||
INNER JOIN "AbpUsers" u ON u."Id" = a."ApproverUserId" AND u."IsDeleted" = false
|
WHERE u."IsDeleted" = false AND u."IsActive" = true
|
||||||
WHERE a."IsDeleted" = false AND a."IsActive" = true AND a."ApprovalArea" = 'PurchaseDesk'
|
AND u."TenantId" IS NOT DISTINCT FROM r."TenantId"
|
||||||
AND a."TenantId" IS NOT DISTINCT FROM r."TenantId")
|
AND EXISTS (
|
||||||
WHERE r."Id" = "p_Id";
|
SELECT 1
|
||||||
|
FROM "AbpPermissionGrants" g
|
||||||
UPDATE "Prc_T_MaterialRequest" r SET
|
WHERE g."Name" = 'App.Prc.MaterialRequestPurchaseApproval.Update'
|
||||||
"PurchaseApprovedBy" = COALESCE("p_UserId", r."LastModifierId"),
|
AND g."TenantId" IS NOT DISTINCT FROM r."TenantId"
|
||||||
"PurchaseApprovalDate" = COALESCE(r."ApprovalDate", now()),
|
AND ((g."ProviderName" = 'U' AND g."ProviderKey" = u."Id"::text)
|
||||||
"PurchaseApprovalNote" = r."ApprovalNote"
|
OR (g."ProviderName" = 'R' AND g."ProviderKey" IN (
|
||||||
WHERE r."Id" = "p_Id" AND r."PurchaseApprovedBy" IS NULL AND r."DeptApprovedBy" IS NOT NULL
|
SELECT ro."Name"
|
||||||
AND r."ApprovalStatus" IN ('Onaylandı', 'Reddedildi');
|
FROM "AbpUserRoles" ur
|
||||||
|
INNER JOIN "AbpRoles" ro ON ro."Id" = ur."RoleId"
|
||||||
UPDATE "Prc_T_MaterialRequest" r SET
|
WHERE ur."UserId" = u."Id")))))
|
||||||
"DeptApprovedBy" = COALESCE("p_UserId", r."LastModifierId"),
|
|
||||||
"DeptApprovalDate" = COALESCE(r."ApprovalDate", now()),
|
|
||||||
"DeptApprovalNote" = r."ApprovalNote"
|
|
||||||
WHERE r."Id" = "p_Id" AND r."DeptApprovedBy" IS NULL
|
|
||||||
AND r."ApprovalStatus" IN ('Satınalma Onayı', 'Onaylandı', 'Reddedildi');
|
|
||||||
|
|
||||||
UPDATE "Prc_T_MaterialRequest" r SET "Status" =
|
|
||||||
CASE
|
|
||||||
WHEN r."ApprovalStatus" = 'Departman Onayı' THEN 'DeptApproval'
|
|
||||||
WHEN r."ApprovalStatus" = 'Satınalma Onayı' THEN 'PurchaseApproval'
|
|
||||||
WHEN r."ApprovalStatus" = 'Reddedildi' THEN 'Rejected'
|
|
||||||
WHEN r."ApprovalStatus" = 'Onaylandı' THEN
|
|
||||||
CASE
|
|
||||||
WHEN s."Total" > 0 AND s."Closed" = s."Total" THEN 'Done'
|
|
||||||
WHEN s."Closed" > 0 THEN 'PartiallyDone'
|
|
||||||
ELSE 'Approved'
|
|
||||||
END
|
|
||||||
ELSE 'Draft'
|
|
||||||
END
|
|
||||||
FROM (
|
|
||||||
SELECT COUNT(*) AS "Total",
|
|
||||||
COALESCE(SUM(CASE WHEN l."LineStatus" IN ('Done', 'Cancelled') THEN 1 ELSE 0 END), 0) AS "Closed"
|
|
||||||
FROM "Prc_T_MaterialRequestItem" l
|
|
||||||
WHERE l."MaterialRequestId" = "p_Id" AND l."IsDeleted" = false
|
|
||||||
) s
|
|
||||||
WHERE r."Id" = "p_Id";
|
WHERE r."Id" = "p_Id";
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,12 @@ CREATE TABLE IF NOT EXISTS public."Prc_T_MaterialRequest"
|
||||||
"LocationId" uuid NOT NULL,
|
"LocationId" uuid NOT NULL,
|
||||||
"RequestDate" timestamp NOT NULL DEFAULT LOCALTIMESTAMP,
|
"RequestDate" timestamp NOT NULL DEFAULT LOCALTIMESTAMP,
|
||||||
"Description" text NULL,
|
"Description" text NULL,
|
||||||
"Status" varchar(32) NOT NULL DEFAULT 'Draft',
|
"ApprovalUserName" varchar(256) NULL,
|
||||||
"ApprovalStatus" varchar(32) NULL,
|
"ApprovalStatus" varchar(50) NULL,
|
||||||
"ApproverId" uuid NULL,
|
|
||||||
"ApprovalDate" timestamp NULL,
|
"ApprovalDate" timestamp NULL,
|
||||||
"ApprovalNote" text NULL,
|
"ApprovalDescription" varchar(200) NULL,
|
||||||
"DeptApproverUserNames" text NULL,
|
"DeptApproverUserNames" text NULL,
|
||||||
"PurchaseApproverUserNames" text NULL,
|
"PurchaseApproverUserNames" text NULL,
|
||||||
"DeptApprovedBy" uuid NULL,
|
|
||||||
"DeptApprovalDate" timestamp NULL,
|
|
||||||
"DeptApprovalNote" text NULL,
|
|
||||||
"PurchaseApprovedBy" uuid NULL,
|
|
||||||
"PurchaseApprovalDate" timestamp NULL,
|
|
||||||
"PurchaseApprovalNote" text NULL,
|
|
||||||
"RejectReasonId" uuid NULL,
|
|
||||||
"CreationTime" timestamp NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
"CreationTime" timestamp NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||||
"CreatorId" uuid NULL,
|
"CreatorId" uuid NULL,
|
||||||
"LastModificationTime" timestamp NULL,
|
"LastModificationTime" timestamp NULL,
|
||||||
|
|
@ -34,6 +26,6 @@ CREATE TABLE IF NOT EXISTS public."Prc_T_MaterialRequest"
|
||||||
CONSTRAINT "PK_Prc_T_MaterialRequest" PRIMARY KEY ("Id")
|
CONSTRAINT "PK_Prc_T_MaterialRequest" PRIMARY KEY ("Id")
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS "IX_Prc_T_MaterialRequest_TenantId_Status" ON public."Prc_T_MaterialRequest" ("TenantId", "Status");
|
CREATE INDEX IF NOT EXISTS "IX_Prc_T_MaterialRequest_TenantId_ApprovalStatus" ON public."Prc_T_MaterialRequest" ("TenantId", "ApprovalStatus");
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS "IX_Prc_T_MaterialRequest_TenantId_RequesterUserId" ON public."Prc_T_MaterialRequest" ("TenantId", "RequesterUserId");
|
CREATE INDEX IF NOT EXISTS "IX_Prc_T_MaterialRequest_TenantId_RequesterUserId" ON public."Prc_T_MaterialRequest" ("TenantId", "RequesterUserId");
|
||||||
|
|
|
||||||
11
configs/seeds/postgres/object/Prc_V_MaterialRequestLine.sql
Normal file
11
configs/seeds/postgres/object/Prc_V_MaterialRequestLine.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
CREATE OR REPLACE VIEW public."Prc_V_MaterialRequestLine"
|
||||||
|
AS
|
||||||
|
-- Tüm Talepler listesinin kaynağı: talep başlığı × kalem, her kalem bir satır. Kalemi olmayan talep de
|
||||||
|
-- listeden düşmesin diye LEFT JOIN; o satırın anahtarı talebin kendi Id'sidir.
|
||||||
|
SELECT COALESCE(i."Id", r."Id") AS "Id", r."Id" AS "MaterialRequestId", r."TenantId", r."IsDeleted",
|
||||||
|
r."RequestNo", r."Priority", r."RequesterUserId", r."DepartmentId", r."WarehouseId", r."LocationId", r."RequestDate",
|
||||||
|
r."Description", r."ApprovalStatus", r."ApprovalUserName", r."ApprovalDate", r."ApprovalDescription",
|
||||||
|
i."ProductId", i."Quantity", i."UomId", i."FulfillmentMethod", i."DueDate", i."RequestReason", i."DeliveredQuantity",
|
||||||
|
i."PurchaseResponsibleUserId", i."LineStatus"
|
||||||
|
FROM "Prc_T_MaterialRequest" r
|
||||||
|
LEFT JOIN "Prc_T_MaterialRequestItem" i ON i."MaterialRequestId" = r."Id" AND i."IsDeleted" = FALSE;
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
CREATE OR ALTER PROCEDURE [dbo].[Prc_P_MaterialRequestAfterSave]
|
CREATE OR ALTER PROCEDURE [dbo].[Prc_P_MaterialRequestAfterSave]
|
||||||
@Id uniqueidentifier,
|
@Id uniqueidentifier
|
||||||
@UserId uniqueidentifier = NULL
|
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON;
|
SET NOCOUNT ON;
|
||||||
IF @Id IS NULL RETURN;
|
IF @Id IS NULL RETURN;
|
||||||
|
|
||||||
-- Onaycılar (Adm_T_Approver): departman onayı = Purchase + talebin departmanı, satınalma onayı = PurchaseDesk
|
-- Departman onaycıları: Onaycılar'da talebin departmanı için Satınalma onay yeri.
|
||||||
|
-- Satınalma onaycıları: Satınalma Onayı Bekleyenler ekranında karar verebilen (güncelleme yetkisi
|
||||||
|
-- doğrudan ya da rolüyle verilmiş) aktif kullanıcılar.
|
||||||
UPDATE r SET
|
UPDATE r SET
|
||||||
DeptApproverUserNames = (
|
DeptApproverUserNames = (
|
||||||
SELECT STRING_AGG(u.UserName, '|')
|
SELECT STRING_AGG(u.UserName, '|')
|
||||||
|
|
@ -17,52 +18,21 @@ BEGIN
|
||||||
AND ((a.TenantId IS NULL AND r.TenantId IS NULL) OR a.TenantId = r.TenantId)),
|
AND ((a.TenantId IS NULL AND r.TenantId IS NULL) OR a.TenantId = r.TenantId)),
|
||||||
PurchaseApproverUserNames = (
|
PurchaseApproverUserNames = (
|
||||||
SELECT STRING_AGG(u.UserName, '|')
|
SELECT STRING_AGG(u.UserName, '|')
|
||||||
FROM Adm_T_Approver a
|
FROM AbpUsers u
|
||||||
INNER JOIN AbpUsers u ON u.Id = a.ApproverUserId AND u.IsDeleted = 0
|
WHERE u.IsDeleted = 0 AND u.IsActive = 1
|
||||||
WHERE a.IsDeleted = 0 AND a.IsActive = 1 AND a.ApprovalArea = 'PurchaseDesk'
|
AND ((u.TenantId IS NULL AND r.TenantId IS NULL) OR u.TenantId = r.TenantId)
|
||||||
AND ((a.TenantId IS NULL AND r.TenantId IS NULL) OR a.TenantId = r.TenantId))
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM AbpPermissionGrants g
|
||||||
|
WHERE g.Name = N'App.Prc.MaterialRequestPurchaseApproval.Update'
|
||||||
|
AND ((g.TenantId IS NULL AND r.TenantId IS NULL) OR g.TenantId = r.TenantId)
|
||||||
|
AND ((g.ProviderName = N'U' AND g.ProviderKey = CAST(u.Id AS nvarchar(64)))
|
||||||
|
OR (g.ProviderName = N'R' AND g.ProviderKey IN (
|
||||||
|
SELECT ro.Name
|
||||||
|
FROM AbpUserRoles ur
|
||||||
|
INNER JOIN AbpRoles ro ON ro.Id = ur.RoleId
|
||||||
|
WHERE ur.UserId = u.Id)))))
|
||||||
FROM Prc_T_MaterialRequest r
|
FROM Prc_T_MaterialRequest r
|
||||||
WHERE r.Id = @Id;
|
WHERE r.Id = @Id;
|
||||||
|
|
||||||
-- Satınalma kararı: departman adımı daha önce kaydedilmişse
|
|
||||||
UPDATE r SET
|
|
||||||
PurchaseApprovedBy = COALESCE(@UserId, r.LastModifierId),
|
|
||||||
PurchaseApprovalDate = COALESCE(r.ApprovalDate, GETDATE()),
|
|
||||||
PurchaseApprovalNote = r.ApprovalNote
|
|
||||||
FROM Prc_T_MaterialRequest r
|
|
||||||
WHERE r.Id = @Id AND r.PurchaseApprovedBy IS NULL AND r.DeptApprovedBy IS NOT NULL
|
|
||||||
AND r.ApprovalStatus IN (N'Onaylandı', N'Reddedildi');
|
|
||||||
|
|
||||||
-- Departman kararı (onaylanıp satınalmaya geçti ya da departmanda reddedildi)
|
|
||||||
UPDATE r SET
|
|
||||||
DeptApprovedBy = COALESCE(@UserId, r.LastModifierId),
|
|
||||||
DeptApprovalDate = COALESCE(r.ApprovalDate, GETDATE()),
|
|
||||||
DeptApprovalNote = r.ApprovalNote
|
|
||||||
FROM Prc_T_MaterialRequest r
|
|
||||||
WHERE r.Id = @Id AND r.DeptApprovedBy IS NULL
|
|
||||||
AND r.ApprovalStatus IN (N'Satınalma Onayı', N'Onaylandı', N'Reddedildi');
|
|
||||||
|
|
||||||
-- Durum: onay akışından ve satır durumlarından türetilir
|
|
||||||
UPDATE r SET Status =
|
|
||||||
CASE
|
|
||||||
WHEN r.ApprovalStatus = N'Departman Onayı' THEN 'DeptApproval'
|
|
||||||
WHEN r.ApprovalStatus = N'Satınalma Onayı' THEN 'PurchaseApproval'
|
|
||||||
WHEN r.ApprovalStatus = N'Reddedildi' THEN 'Rejected'
|
|
||||||
WHEN r.ApprovalStatus = N'Onaylandı' THEN
|
|
||||||
CASE
|
|
||||||
WHEN s.Total > 0 AND s.Closed = s.Total THEN 'Done'
|
|
||||||
WHEN s.Closed > 0 THEN 'PartiallyDone'
|
|
||||||
ELSE 'Approved'
|
|
||||||
END
|
|
||||||
ELSE 'Draft'
|
|
||||||
END
|
|
||||||
FROM Prc_T_MaterialRequest r
|
|
||||||
CROSS APPLY (
|
|
||||||
SELECT COUNT(*) AS Total,
|
|
||||||
ISNULL(SUM(CASE WHEN l.LineStatus IN ('Done', 'Cancelled') THEN 1 ELSE 0 END), 0) AS Closed
|
|
||||||
FROM Prc_T_MaterialRequestItem l
|
|
||||||
WHERE l.MaterialRequestId = r.Id AND l.IsDeleted = 0
|
|
||||||
) s
|
|
||||||
WHERE r.Id = @Id;
|
|
||||||
END
|
END
|
||||||
GO
|
GO
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,12 @@ BEGIN
|
||||||
[LocationId] uniqueidentifier NOT NULL,
|
[LocationId] uniqueidentifier NOT NULL,
|
||||||
[RequestDate] datetime NOT NULL DEFAULT GETDATE(),
|
[RequestDate] datetime NOT NULL DEFAULT GETDATE(),
|
||||||
[Description] nvarchar(MAX) NULL,
|
[Description] nvarchar(MAX) NULL,
|
||||||
[Status] nvarchar(32) NOT NULL DEFAULT 'Draft',
|
[ApprovalUserName] nvarchar(256) NULL,
|
||||||
[ApprovalStatus] nvarchar(32) NULL,
|
[ApprovalStatus] nvarchar(50) NULL,
|
||||||
[ApproverId] uniqueidentifier NULL,
|
|
||||||
[ApprovalDate] datetime NULL,
|
[ApprovalDate] datetime NULL,
|
||||||
[ApprovalNote] nvarchar(MAX) NULL,
|
[ApprovalDescription] nvarchar(200) NULL,
|
||||||
[DeptApproverUserNames] nvarchar(MAX) NULL,
|
[DeptApproverUserNames] nvarchar(MAX) NULL,
|
||||||
[PurchaseApproverUserNames] nvarchar(MAX) NULL,
|
[PurchaseApproverUserNames] nvarchar(MAX) NULL,
|
||||||
[DeptApprovedBy] uniqueidentifier NULL,
|
|
||||||
[DeptApprovalDate] datetime NULL,
|
|
||||||
[DeptApprovalNote] nvarchar(MAX) NULL,
|
|
||||||
[PurchaseApprovedBy] uniqueidentifier NULL,
|
|
||||||
[PurchaseApprovalDate] datetime NULL,
|
|
||||||
[PurchaseApprovalNote] nvarchar(MAX) NULL,
|
|
||||||
[RejectReasonId] uniqueidentifier NULL,
|
|
||||||
[CreationTime] datetime2 NOT NULL DEFAULT GETUTCDATE(),
|
[CreationTime] datetime2 NOT NULL DEFAULT GETUTCDATE(),
|
||||||
[CreatorId] uniqueidentifier NULL,
|
[CreatorId] uniqueidentifier NULL,
|
||||||
[LastModificationTime] datetime2 NULL,
|
[LastModificationTime] datetime2 NULL,
|
||||||
|
|
@ -41,9 +33,9 @@ BEGIN
|
||||||
END
|
END
|
||||||
GO
|
GO
|
||||||
|
|
||||||
IF INDEXPROPERTY(OBJECT_ID(N'[dbo].[Prc_T_MaterialRequest]'), 'IX_Prc_T_MaterialRequest_TenantId_Status', 'IndexID') IS NULL
|
IF INDEXPROPERTY(OBJECT_ID(N'[dbo].[Prc_T_MaterialRequest]'), 'IX_Prc_T_MaterialRequest_TenantId_ApprovalStatus', 'IndexID') IS NULL
|
||||||
BEGIN
|
BEGIN
|
||||||
CREATE INDEX [IX_Prc_T_MaterialRequest_TenantId_Status] ON [dbo].[Prc_T_MaterialRequest] ([TenantId] ASC, [Status] ASC);
|
CREATE INDEX [IX_Prc_T_MaterialRequest_TenantId_ApprovalStatus] ON [dbo].[Prc_T_MaterialRequest] ([TenantId] ASC, [ApprovalStatus] ASC);
|
||||||
END
|
END
|
||||||
GO
|
GO
|
||||||
|
|
||||||
|
|
|
||||||
12
configs/seeds/sql/object/Prc_V_MaterialRequestLine.sql
Normal file
12
configs/seeds/sql/object/Prc_V_MaterialRequestLine.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE OR ALTER VIEW [dbo].[Prc_V_MaterialRequestLine]
|
||||||
|
AS
|
||||||
|
-- Tüm Talepler listesinin kaynağı: talep başlığı × kalem, her kalem bir satır. Kalemi olmayan talep de
|
||||||
|
-- listeden düşmesin diye LEFT JOIN; o satırın anahtarı talebin kendi Id'sidir.
|
||||||
|
SELECT COALESCE(i.Id, r.Id) AS Id, r.Id AS MaterialRequestId, r.TenantId, r.IsDeleted,
|
||||||
|
r.RequestNo, r.Priority, r.RequesterUserId, r.DepartmentId, r.WarehouseId, r.LocationId, r.RequestDate,
|
||||||
|
r.Description, r.ApprovalStatus, r.ApprovalUserName, r.ApprovalDate, r.ApprovalDescription,
|
||||||
|
i.ProductId, i.Quantity, i.UomId, i.FulfillmentMethod, i.DueDate, i.RequestReason, i.DeliveredQuantity,
|
||||||
|
i.PurchaseResponsibleUserId, i.LineStatus
|
||||||
|
FROM Prc_T_MaterialRequest r
|
||||||
|
LEFT JOIN Prc_T_MaterialRequestItem i ON i.MaterialRequestId = r.Id AND i.IsDeleted = 0;
|
||||||
|
GO
|
||||||
|
|
@ -32,19 +32,14 @@
|
||||||
"MenuTr": "Onaycılar",
|
"MenuTr": "Onaycılar",
|
||||||
"TitleEn": "Approvers",
|
"TitleEn": "Approvers",
|
||||||
"TitleTr": "Onaycılar",
|
"TitleTr": "Onaycılar",
|
||||||
"DescEn": "Approvers — the single list of who approves what for each department across the application.\nWhere: Administration › Definitions › Approvers.\nFields: department (shown as Branch - Department), approval area (Purchasing / Purchasing Desk / Warehouse / Human Resources / Finance), approver user, active.\nUsed by: approval workflows read the approvers of the record's department and area. Purchasing › Material Requests: the department approval uses area Purchasing for the requester's department; the purchasing approval uses area Purchasing Desk (any department). Several approvers may be defined for the same department and area; any one of them can approve. Passive rows are ignored by workflows.",
|
"DescEn": "Approvers — the single list of who approves what for each department across the application.\nWhere: Administration › Definitions › Approvers.\nFields: department (shown as Branch - Department), approval area (Purchasing / Warehouse / Human Resources / Finance), approver user, active.\nUsed by: approval workflows read the approvers of the record's department and area. Purchasing › Material Requests: the department approval uses area Purchasing for the requester's department; the purchasing approval is not defined here: it goes to users with update permission on Awaiting Purchasing Approval (directly or through their role). Several approvers may be defined for the same department and area; any one of them can approve. Passive rows are ignored by workflows.",
|
||||||
"DescTr": "Onaycılar — uygulamadaki tüm onay adımlarında her departman için kimin onaylayacağını tutan tek liste.\nNerede: Yönetim › Tanımlamalar › Onaycılar.\nAlanlar: departman (Şube - Departman olarak gösterilir), onay yeri (Satınalma / Satınalma Masası / Depo / İnsan Kaynakları / Finans), onaylayacak kişi, aktif.\nKullanıldığı yer: onay akışları kaydın departmanı ve onay yerine göre onaycıları buradan okur. Satınalma › Satınalma Talepleri: departman onayı talep edenin departmanındaki Satınalma satırlarını, satınalma onayı ise Satınalma Masası satırlarını (departmandan bağımsız) kullanır. Aynı departman ve onay yerine birden çok onaycı girilebilir; herhangi birinin onayı yeterlidir. Pasif satırlar akışlarda kullanılmaz.",
|
"DescTr": "Onaycılar — uygulamadaki tüm onay adımlarında her departman için kimin onaylayacağını tutan tek liste.\nNerede: Yönetim › Tanımlamalar › Onaycılar.\nAlanlar: departman (Şube - Departman olarak gösterilir), onay yeri (Satınalma / Depo / İnsan Kaynakları / Finans), onaylayacak kişi, aktif.\nKullanıldığı yer: onay akışları kaydın departmanı ve onay yerine göre onaycıları buradan okur. Satınalma › Malzeme Talepleri: departman onayı talep edenin departmanındaki Satınalma satırlarını, satınalma onayı burada tanımlanmaz: Satınalma Onayı Bekleyenler ekranında güncelleme yetkisi olan kullanıcılar (doğrudan ya da rolüyle) onaylar. Aynı departman ve onay yerine birden çok onaycı girilebilir; herhangi birinin onayı yeterlidir. Pasif satırlar akışlarda kullanılmaz.",
|
||||||
"Texts": [
|
"Texts": [
|
||||||
{
|
{
|
||||||
"Key": "App.StaticLookup.Purchase",
|
"Key": "App.StaticLookup.Purchase",
|
||||||
"En": "Purchasing",
|
"En": "Purchasing",
|
||||||
"Tr": "Satınalma"
|
"Tr": "Satınalma"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Key": "App.StaticLookup.PurchaseDesk",
|
|
||||||
"En": "Purchasing Desk",
|
|
||||||
"Tr": "Satınalma Masası"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Key": "App.StaticLookup.Warehouse",
|
"Key": "App.StaticLookup.Warehouse",
|
||||||
"En": "Warehouse",
|
"En": "Warehouse",
|
||||||
|
|
@ -1061,7 +1056,7 @@
|
||||||
"CascadeFilterOperator": "=",
|
"CascadeFilterOperator": "=",
|
||||||
"CascadeParentFields": null,
|
"CascadeParentFields": null,
|
||||||
"CascadeEmptyFields": null,
|
"CascadeEmptyFields": null,
|
||||||
"LookupQuery": "[{\"key\":\"Purchase\",\"name\":\"App.StaticLookup.Purchase\"},{\"key\":\"PurchaseDesk\",\"name\":\"App.StaticLookup.PurchaseDesk\"},{\"key\":\"Warehouse\",\"name\":\"App.StaticLookup.Warehouse\"},{\"key\":\"HumanResources\",\"name\":\"App.StaticLookup.HumanResources\"},{\"key\":\"Finance\",\"name\":\"App.StaticLookup.Finance\"}]"
|
"LookupQuery": "[{\"key\":\"Purchase\",\"name\":\"App.StaticLookup.Purchase\"},{\"key\":\"Warehouse\",\"name\":\"App.StaticLookup.Warehouse\"},{\"key\":\"HumanResources\",\"name\":\"App.StaticLookup.HumanResources\"},{\"key\":\"Finance\",\"name\":\"App.StaticLookup.Finance\"}]"
|
||||||
},
|
},
|
||||||
"ValidationRuleDto": [
|
"ValidationRuleDto": [
|
||||||
{
|
{
|
||||||
|
|
@ -1403,7 +1398,6 @@
|
||||||
"App.Definitions.Approver.Title",
|
"App.Definitions.Approver.Title",
|
||||||
"App.Definitions.Approver.Desc",
|
"App.Definitions.Approver.Desc",
|
||||||
"App.StaticLookup.Purchase",
|
"App.StaticLookup.Purchase",
|
||||||
"App.StaticLookup.PurchaseDesk",
|
|
||||||
"App.StaticLookup.Warehouse",
|
"App.StaticLookup.Warehouse",
|
||||||
"App.StaticLookup.HumanResources",
|
"App.StaticLookup.HumanResources",
|
||||||
"App.StaticLookup.Finance",
|
"App.StaticLookup.Finance",
|
||||||
|
|
@ -1428,6 +1422,6 @@
|
||||||
},
|
},
|
||||||
"DevRequest": {
|
"DevRequest": {
|
||||||
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
||||||
"Title": "Satınalma -> Satınalma Talepleri"
|
"Title": "Satınalma -> Malzeme Talepleri"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -25,15 +25,15 @@
|
||||||
},
|
},
|
||||||
"Language": {
|
"Language": {
|
||||||
"MenuParentEn": "Material Requests",
|
"MenuParentEn": "Material Requests",
|
||||||
"MenuParentTr": "Satınalma Talepleri",
|
"MenuParentTr": "Malzeme Talepleri",
|
||||||
"PermissionGroupEn": "Purchasing",
|
"PermissionGroupEn": "Purchasing",
|
||||||
"PermissionGroupTr": "Satınalma",
|
"PermissionGroupTr": "Satınalma",
|
||||||
"MenuEn": "Awaiting Department Approval",
|
"MenuEn": "Awaiting Department Approval",
|
||||||
"MenuTr": "Departman Onayı Bekleyenler",
|
"MenuTr": "Departman Onayı Bekleyenler",
|
||||||
"TitleEn": "Awaiting Department Approval",
|
"TitleEn": "Awaiting Department Approval",
|
||||||
"TitleTr": "Departman Onayı Bekleyenler",
|
"TitleTr": "Departman Onayı Bekleyenler",
|
||||||
"DescEn": "Awaiting Department Approval — material requests waiting for the department approver's decision.\nWhere: Purchasing › Material Requests › Awaiting Department Approval.\nWho: approvers defined in Administration › Definitions › Approvers with area Purchasing for the requester's department; each approver sees only the requests assigned to them.\nWhat to do: open the request, review the Request Lines tab, then Approve (the request moves to purchasing approval) or Reject with a note and a rejection reason (Purchasing › Definitions › Rejection Reasons). Lines cannot be added or removed here.\nRelated: My Requests, Awaiting Purchasing Approval, All Requests.",
|
"DescEn": "Awaiting Department Approval — material requests waiting for the department approver's decision.\nWhere: Purchasing › Material Requests › Awaiting Department Approval.\nWho: approvers defined in Administration › Definitions › Approvers with area Purchasing for the requester's department; each approver sees only the requests assigned to them.\nWhat to do: open the request, review the Request Lines tab, then Approve (the request moves to purchasing approval) or Reject by choosing a rejection reason (Purchasing › Definitions › Rejection Reasons) and an optional note; the reason and note are written to the approval description. Lines cannot be added or removed here.\nRelated: My Requests, Awaiting Purchasing Approval, All Requests.",
|
||||||
"DescTr": "Departman Onayı Bekleyenler — departman onaycısının kararını bekleyen malzeme talepleri.\nNerede: Satınalma › Satınalma Talepleri › Departman Onayı Bekleyenler.\nKim: Yönetim › Tanımlamalar › Onaycılar'da talep edenin departmanı için Satınalma onay yerinde tanımlı onaycılar; her onaycı yalnız kendisine düşen talepleri görür.\nNe yapılır: talebi açıp Talep Satırları sekmesini inceleyin, ardından Onayla (talep satınalma onayına geçer) ya da not ve red nedeniyle (Satınalma › Tanımlamalar › Red Nedenleri) Reddet. Burada satır eklenip silinemez.\nİlişkili: Taleplerim, Satınalma Onayı Bekleyenler, Tüm Talepler.",
|
"DescTr": "Departman Onayı Bekleyenler — departman onaycısının kararını bekleyen malzeme talepleri.\nNerede: Satınalma › Malzeme Talepleri › Departman Onayı Bekleyenler.\nKim: Yönetim › Tanımlamalar › Onaycılar'da talep edenin departmanı için Satınalma onay yerinde tanımlı onaycılar; her onaycı yalnız kendisine düşen talepleri görür.\nNe yapılır: talebi açıp Talep Satırları sekmesini inceleyin, ardından Onayla (talep satınalma onayına geçer) ya da red nedeni (Satınalma › Tanımlamalar › Red Nedenleri) seçip isteğe bağlı notla Reddet; neden ve not onay açıklamasına yazılır. Burada satır eklenip silinemez.\nİlişkili: Taleplerim, Satınalma Onayı Bekleyenler, Tüm Talepler.",
|
||||||
"Texts": [
|
"Texts": [
|
||||||
{
|
{
|
||||||
"Key": "App.Prc.MaterialRequest.Group.Request",
|
"Key": "App.Prc.MaterialRequest.Group.Request",
|
||||||
|
|
@ -45,11 +45,6 @@
|
||||||
"En": "Approval / Rejection",
|
"En": "Approval / Rejection",
|
||||||
"Tr": "Onay / Red Bilgileri"
|
"Tr": "Onay / Red Bilgileri"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Draft",
|
|
||||||
"En": "Draft",
|
|
||||||
"Tr": "Taslak"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Key": "App.Prc.MaterialRequest.Status.DeptApproval",
|
"Key": "App.Prc.MaterialRequest.Status.DeptApproval",
|
||||||
"En": "Department Approval",
|
"En": "Department Approval",
|
||||||
|
|
@ -65,21 +60,6 @@
|
||||||
"En": "Approved",
|
"En": "Approved",
|
||||||
"Tr": "Onaylandı"
|
"Tr": "Onaylandı"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.InProgress",
|
|
||||||
"En": "In Progress",
|
|
||||||
"Tr": "İşlemde"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.PartiallyDone",
|
|
||||||
"En": "Partially Done",
|
|
||||||
"Tr": "Kısmi Tamamlandı"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Done",
|
|
||||||
"En": "Done",
|
|
||||||
"Tr": "Tamamlandı"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Key": "App.Prc.MaterialRequest.Status.Rejected",
|
"Key": "App.Prc.MaterialRequest.Status.Rejected",
|
||||||
"En": "Rejected",
|
"En": "Rejected",
|
||||||
|
|
@ -139,10 +119,10 @@
|
||||||
"Tr": "Talep Tarihi"
|
"Tr": "Talep Tarihi"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FieldName": "Status",
|
"FieldName": "Description",
|
||||||
"CaptionName": "App.Listform.ListformField.Status",
|
"CaptionName": "App.Listform.ListformField.GeneralDescription",
|
||||||
"En": "Status",
|
"En": "General Description",
|
||||||
"Tr": "Durum"
|
"Tr": "Genel Açıklama"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FieldName": "ApprovalStatus",
|
"FieldName": "ApprovalStatus",
|
||||||
|
|
@ -150,18 +130,6 @@
|
||||||
"En": "Approval Status",
|
"En": "Approval Status",
|
||||||
"Tr": "Onay Durumu"
|
"Tr": "Onay Durumu"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"FieldName": "RejectReasonId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.RejectReasonId",
|
|
||||||
"En": "Rejection Reason",
|
|
||||||
"Tr": "Red Nedeni"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Description",
|
|
||||||
"CaptionName": "App.Listform.ListformField.GeneralDescription",
|
|
||||||
"En": "General Description",
|
|
||||||
"Tr": "Genel Açıklama"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"FieldName": "DeptApproverUserNames",
|
"FieldName": "DeptApproverUserNames",
|
||||||
"CaptionName": "App.Listform.ListformField.DeptApproverUserNames",
|
"CaptionName": "App.Listform.ListformField.DeptApproverUserNames",
|
||||||
|
|
@ -174,6 +142,12 @@
|
||||||
"En": "Purchasing Approvers",
|
"En": "Purchasing Approvers",
|
||||||
"Tr": "Satınalma Onaycıları"
|
"Tr": "Satınalma Onaycıları"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"FieldName": "ApprovalUserName",
|
||||||
|
"CaptionName": "App.Listform.ListformField.ApprovalUserName",
|
||||||
|
"En": "Approval User Name",
|
||||||
|
"Tr": "Onaylayan Kullanıcı Adı"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"FieldName": "ApprovalDate",
|
"FieldName": "ApprovalDate",
|
||||||
"CaptionName": "App.Listform.ListformField.ApprovalDate",
|
"CaptionName": "App.Listform.ListformField.ApprovalDate",
|
||||||
|
|
@ -181,10 +155,10 @@
|
||||||
"Tr": "Onay Tarihi"
|
"Tr": "Onay Tarihi"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FieldName": "ApprovalNote",
|
"FieldName": "ApprovalDescription",
|
||||||
"CaptionName": "App.Listform.ListformField.ApprovalNote",
|
"CaptionName": "App.Listform.ListformField.ApprovalDescription",
|
||||||
"En": "Approval Note",
|
"En": "Approval Description",
|
||||||
"Tr": "Onay Notu"
|
"Tr": "Onay Açıklaması"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +231,7 @@
|
||||||
],
|
],
|
||||||
"UpdateCommand": null,
|
"UpdateCommand": null,
|
||||||
"UpdateBeforeCommand": null,
|
"UpdateBeforeCommand": null,
|
||||||
"UpdateAfterCommand": "DECLARE @_id uniqueidentifier = (SELECT TOP 1 Id FROM Prc_T_MaterialRequest WHERE Id IN @Id); EXEC dbo.Prc_P_MaterialRequestAfterSave @_id, @LastModifierId",
|
"UpdateAfterCommand": "DECLARE @_id uniqueidentifier = (SELECT TOP 1 Id FROM Prc_T_MaterialRequest WHERE Id IN @Id); EXEC dbo.Prc_P_MaterialRequestAfterSave @_id",
|
||||||
"UpdateFieldsDefaultValueDto": [
|
"UpdateFieldsDefaultValueDto": [
|
||||||
{
|
{
|
||||||
"FieldName": "LastModificationTime",
|
"FieldName": "LastModificationTime",
|
||||||
|
|
@ -281,20 +255,13 @@
|
||||||
"Value": "0",
|
"Value": "0",
|
||||||
"CustomValueType": 1,
|
"CustomValueType": 1,
|
||||||
"SqlQuery": null
|
"SqlQuery": null
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Status",
|
|
||||||
"FieldDbType": 16,
|
|
||||||
"Value": "Draft",
|
|
||||||
"CustomValueType": 1,
|
|
||||||
"SqlQuery": null
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"WidgetsDto": [
|
"WidgetsDto": [
|
||||||
{
|
{
|
||||||
"ColGap": 3,
|
"ColGap": 3,
|
||||||
"ColSpan": 4,
|
"ColSpan": 4,
|
||||||
"SqlQuery": "SELECT 'App.Prc.MaterialRequest.Status.DeptApproval' AS \"Title\", CAST(COUNT(*) AS VARCHAR(32)) AS \"Value\", 'orange' AS \"Color\", 'App.Prc.MaterialRequestDeptApproval' AS \"SubTitle\", 'FaUserTie' AS \"Icon\" FROM \"Prc_T_MaterialRequest\" WHERE \"TenantId\" = '@TENANTID' AND \"Status\" = 'DeptApproval' UNION ALL SELECT 'App.Prc.MaterialRequest.Status.PurchaseApproval', CAST(COUNT(*) AS VARCHAR(32)), 'purple', 'App.Prc.MaterialRequestDeptApproval', 'FaHourglassHalf' FROM \"Prc_T_MaterialRequest\" WHERE \"TenantId\" = '@TENANTID' AND \"Status\" = 'PurchaseApproval' UNION ALL SELECT 'App.Prc.MaterialRequest.Status.Rejected', CAST(COUNT(*) AS VARCHAR(32)), 'red', 'App.Prc.MaterialRequestDeptApproval', 'FaTimesCircle' FROM \"Prc_T_MaterialRequest\" WHERE \"TenantId\" = '@TENANTID' AND \"Status\" = 'Rejected'",
|
"SqlQuery": "SELECT 'App.Prc.MaterialRequest.Status.DeptApproval' AS \"Title\", CAST(COUNT(*) AS VARCHAR(32)) AS \"Value\", 'orange' AS \"Color\", 'App.Prc.MaterialRequestDeptApproval' AS \"SubTitle\", 'FaUserTie' AS \"Icon\" FROM \"Prc_T_MaterialRequest\" WHERE \"TenantId\" = '@TENANTID' AND \"ApprovalStatus\" = N'Departman Onayı' UNION ALL SELECT 'App.Prc.MaterialRequest.Status.PurchaseApproval', CAST(COUNT(*) AS VARCHAR(32)), 'purple', 'App.Prc.MaterialRequestDeptApproval', 'FaHourglassHalf' FROM \"Prc_T_MaterialRequest\" WHERE \"TenantId\" = '@TENANTID' AND \"ApprovalStatus\" = N'Satınalma Onayı' UNION ALL SELECT 'App.Prc.MaterialRequest.Status.Rejected', CAST(COUNT(*) AS VARCHAR(32)), 'red', 'App.Prc.MaterialRequestDeptApproval', 'FaTimesCircle' FROM \"Prc_T_MaterialRequest\" WHERE \"TenantId\" = '@TENANTID' AND \"ApprovalStatus\" = N'Reddedildi'",
|
||||||
"Title": "Title",
|
"Title": "Title",
|
||||||
"Value": "Value",
|
"Value": "Value",
|
||||||
"ValueClassName": "text-3xl",
|
"ValueClassName": "text-3xl",
|
||||||
|
|
@ -569,13 +536,13 @@
|
||||||
"DefaultLayout": "grid"
|
"DefaultLayout": "grid"
|
||||||
},
|
},
|
||||||
"WorkflowDto": {
|
"WorkflowDto": {
|
||||||
"ApprovalUserFieldName": null,
|
"ApprovalUserFieldName": "ApprovalUserName",
|
||||||
"ApprovalDateFieldName": "ApprovalDate",
|
"ApprovalDateFieldName": "ApprovalDate",
|
||||||
"ApprovalStatusFieldName": "ApprovalStatus",
|
"ApprovalStatusFieldName": "ApprovalStatus",
|
||||||
"ApprovalDescriptionFieldName": "ApprovalNote",
|
"ApprovalDescriptionFieldName": "ApprovalDescription",
|
||||||
"ApprovalIsFilterUserName": true,
|
"ApprovalIsFilterUserName": true,
|
||||||
"ApprovalIsResetWorkflow": true,
|
"ApprovalIsResetWorkflow": true,
|
||||||
"ApprovalIsManualStart": false,
|
"ApprovalIsManualStart": true,
|
||||||
"Criteria": [
|
"Criteria": [
|
||||||
{
|
{
|
||||||
"ListFormCode": "App.Prc.MaterialRequestDeptApproval",
|
"ListFormCode": "App.Prc.MaterialRequestDeptApproval",
|
||||||
|
|
@ -611,6 +578,13 @@
|
||||||
"PositionX": 40,
|
"PositionX": 40,
|
||||||
"PositionY": 160,
|
"PositionY": 160,
|
||||||
"CompareOutcomes": [],
|
"CompareOutcomes": [],
|
||||||
|
"RejectReason": {
|
||||||
|
"TableName": "Prc_T_RejectReason",
|
||||||
|
"ValueField": "Id",
|
||||||
|
"TextField": "Name",
|
||||||
|
"TargetField": "",
|
||||||
|
"Required": true
|
||||||
|
},
|
||||||
"Id": "App.Prc.MaterialRequestDeptApproval-N2"
|
"Id": "App.Prc.MaterialRequestDeptApproval-N2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -629,6 +603,13 @@
|
||||||
"PositionX": 40,
|
"PositionX": 40,
|
||||||
"PositionY": 280,
|
"PositionY": 280,
|
||||||
"CompareOutcomes": [],
|
"CompareOutcomes": [],
|
||||||
|
"RejectReason": {
|
||||||
|
"TableName": "Prc_T_RejectReason",
|
||||||
|
"ValueField": "Id",
|
||||||
|
"TextField": "Name",
|
||||||
|
"TargetField": "",
|
||||||
|
"Required": true
|
||||||
|
},
|
||||||
"Id": "App.Prc.MaterialRequestDeptApproval-N3"
|
"Id": "App.Prc.MaterialRequestDeptApproval-N3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -2024,8 +2005,8 @@
|
||||||
"JoinFieldName2": null,
|
"JoinFieldName2": null,
|
||||||
"BaseTableJoinFieldName2_OrValue": null
|
"BaseTableJoinFieldName2_OrValue": null
|
||||||
},
|
},
|
||||||
"FieldName": "Status",
|
"FieldName": "Description",
|
||||||
"CaptionName": "App.Listform.ListformField.Status",
|
"CaptionName": "App.Listform.ListformField.GeneralDescription",
|
||||||
"PlaceHolder": null,
|
"PlaceHolder": null,
|
||||||
"ReadOnly": false,
|
"ReadOnly": false,
|
||||||
"Visible": true,
|
"Visible": true,
|
||||||
|
|
@ -2082,28 +2063,16 @@
|
||||||
"ShowInColumn": null
|
"ShowInColumn": null
|
||||||
},
|
},
|
||||||
"LookupDto": {
|
"LookupDto": {
|
||||||
"DataSourceType": 1,
|
"DataSourceType": 0,
|
||||||
"ValueExpr": "key",
|
"ValueExpr": "Key",
|
||||||
"DisplayExpr": "name",
|
"DisplayExpr": "Name",
|
||||||
"CascadeRelationField": null,
|
"CascadeRelationField": null,
|
||||||
"CascadeFilterOperator": "=",
|
"CascadeFilterOperator": "=",
|
||||||
"CascadeParentFields": null,
|
"CascadeParentFields": null,
|
||||||
"CascadeEmptyFields": null,
|
"CascadeEmptyFields": null,
|
||||||
"LookupQuery": "[{\"key\":\"Draft\",\"name\":\"App.Prc.MaterialRequest.Status.Draft\"},{\"key\":\"DeptApproval\",\"name\":\"App.Prc.MaterialRequest.Status.DeptApproval\"},{\"key\":\"PurchaseApproval\",\"name\":\"App.Prc.MaterialRequest.Status.PurchaseApproval\"},{\"key\":\"Approved\",\"name\":\"App.Prc.MaterialRequest.Status.Approved\"},{\"key\":\"InProgress\",\"name\":\"App.Prc.MaterialRequest.Status.InProgress\"},{\"key\":\"PartiallyDone\",\"name\":\"App.Prc.MaterialRequest.Status.PartiallyDone\"},{\"key\":\"Done\",\"name\":\"App.Prc.MaterialRequest.Status.Done\"},{\"key\":\"Rejected\",\"name\":\"App.Prc.MaterialRequest.Status.Rejected\"}]"
|
"LookupQuery": null
|
||||||
},
|
},
|
||||||
"ValidationRuleDto": [
|
"ValidationRuleDto": [],
|
||||||
{
|
|
||||||
"Type": "required",
|
|
||||||
"Message": null,
|
|
||||||
"IgnoreEmptyValue": false,
|
|
||||||
"Reevaluate": true,
|
|
||||||
"ComparisonType": "",
|
|
||||||
"Pattern": "",
|
|
||||||
"Max": null,
|
|
||||||
"Min": null,
|
|
||||||
"Trim": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ColumnStylingDto": [],
|
"ColumnStylingDto": [],
|
||||||
"PermissionDto": {
|
"PermissionDto": {
|
||||||
"Deny": false,
|
"Deny": false,
|
||||||
|
|
@ -2133,8 +2102,8 @@
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 8,
|
"EditOrderNo": 8,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
"EditorType2": "dxSelectBox",
|
"EditorType2": "dxTextArea",
|
||||||
"ColSpan": 1,
|
"ColSpan": 4,
|
||||||
"EditorOptions": "{\"readOnly\":true}",
|
"EditorOptions": "{\"readOnly\":true}",
|
||||||
"GridBoxOptions": null,
|
"GridBoxOptions": null,
|
||||||
"TagBoxOptions": null,
|
"TagBoxOptions": null,
|
||||||
|
|
@ -2257,8 +2226,8 @@
|
||||||
},
|
},
|
||||||
"Alignment": "left",
|
"Alignment": "left",
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 9,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": null,
|
||||||
"EditorType2": "dxTextBox",
|
"EditorType2": "dxTextBox",
|
||||||
"ColSpan": 1,
|
"ColSpan": 1,
|
||||||
"EditorOptions": "{\"readOnly\":true}",
|
"EditorOptions": "{\"readOnly\":true}",
|
||||||
|
|
@ -2272,258 +2241,6 @@
|
||||||
"CreatorId": null,
|
"CreatorId": null,
|
||||||
"Id": "00000000-0000-0000-0000-000000000000"
|
"Id": "00000000-0000-0000-0000-000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"EditType": null,
|
|
||||||
"ListFormCode": "App.Prc.MaterialRequestDeptApproval",
|
|
||||||
"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": "RejectReasonId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.RejectReasonId",
|
|
||||||
"PlaceHolder": null,
|
|
||||||
"ReadOnly": false,
|
|
||||||
"Visible": true,
|
|
||||||
"IsActive": true,
|
|
||||||
"Width": 0,
|
|
||||||
"ListOrderNo": 11,
|
|
||||||
"SourceDbType": 9,
|
|
||||||
"CultureName": "en",
|
|
||||||
"DataType": "string",
|
|
||||||
"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": 2,
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name",
|
|
||||||
"CascadeRelationField": null,
|
|
||||||
"CascadeFilterOperator": "=",
|
|
||||||
"CascadeParentFields": null,
|
|
||||||
"CascadeEmptyFields": null,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM Prc_T_RejectReason WHERE IsDeleted = 'false' AND IsActive = 1 AND TenantId = '@TENANTID' ORDER BY SortOrder, Name"
|
|
||||||
},
|
|
||||||
"ValidationRuleDto": [],
|
|
||||||
"ColumnStylingDto": [],
|
|
||||||
"PermissionDto": {
|
|
||||||
"Deny": false,
|
|
||||||
"C": "App.Prc.MaterialRequestDeptApproval.Create",
|
|
||||||
"R": "App.Prc.MaterialRequestDeptApproval",
|
|
||||||
"U": "App.Prc.MaterialRequestDeptApproval.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": 10,
|
|
||||||
"EditGroupOrderNo": 1,
|
|
||||||
"EditorType2": "dxSelectBox",
|
|
||||||
"ColSpan": 3,
|
|
||||||
"EditorOptions": "{\"showClearButton\":true}",
|
|
||||||
"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.Prc.MaterialRequestDeptApproval",
|
|
||||||
"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": "Description",
|
|
||||||
"CaptionName": "App.Listform.ListformField.GeneralDescription",
|
|
||||||
"PlaceHolder": null,
|
|
||||||
"ReadOnly": false,
|
|
||||||
"Visible": true,
|
|
||||||
"IsActive": true,
|
|
||||||
"Width": 0,
|
|
||||||
"ListOrderNo": 12,
|
|
||||||
"SourceDbType": 16,
|
|
||||||
"CultureName": "en",
|
|
||||||
"DataType": "string",
|
|
||||||
"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.Prc.MaterialRequestDeptApproval.Create",
|
|
||||||
"R": "App.Prc.MaterialRequestDeptApproval",
|
|
||||||
"U": "App.Prc.MaterialRequestDeptApproval.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": 11,
|
|
||||||
"EditGroupOrderNo": 1,
|
|
||||||
"EditorType2": "dxTextArea",
|
|
||||||
"ColSpan": 4,
|
|
||||||
"EditorOptions": "{\"readOnly\":true}",
|
|
||||||
"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,
|
"EditType": null,
|
||||||
"ListFormCode": "App.Prc.MaterialRequestDeptApproval",
|
"ListFormCode": "App.Prc.MaterialRequestDeptApproval",
|
||||||
|
|
@ -2547,7 +2264,7 @@
|
||||||
"Visible": false,
|
"Visible": false,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 13,
|
"ListOrderNo": 11,
|
||||||
"SourceDbType": 16,
|
"SourceDbType": 16,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "string",
|
"DataType": "string",
|
||||||
|
|
@ -2673,7 +2390,133 @@
|
||||||
"Visible": false,
|
"Visible": false,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 14,
|
"ListOrderNo": 12,
|
||||||
|
"SourceDbType": 16,
|
||||||
|
"CultureName": "en",
|
||||||
|
"DataType": "string",
|
||||||
|
"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.Prc.MaterialRequestDeptApproval.Create",
|
||||||
|
"R": "App.Prc.MaterialRequestDeptApproval",
|
||||||
|
"U": "App.Prc.MaterialRequestDeptApproval.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": null,
|
||||||
|
"EditGroupOrderNo": null,
|
||||||
|
"EditorType2": "dxTextBox",
|
||||||
|
"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.Prc.MaterialRequestDeptApproval",
|
||||||
|
"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": "ApprovalUserName",
|
||||||
|
"CaptionName": "App.Listform.ListformField.ApprovalUserName",
|
||||||
|
"PlaceHolder": null,
|
||||||
|
"ReadOnly": false,
|
||||||
|
"Visible": true,
|
||||||
|
"IsActive": true,
|
||||||
|
"Width": 0,
|
||||||
|
"ListOrderNo": 13,
|
||||||
"SourceDbType": 16,
|
"SourceDbType": 16,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "string",
|
"DataType": "string",
|
||||||
|
|
@ -2796,10 +2639,10 @@
|
||||||
"CaptionName": "App.Listform.ListformField.ApprovalDate",
|
"CaptionName": "App.Listform.ListformField.ApprovalDate",
|
||||||
"PlaceHolder": null,
|
"PlaceHolder": null,
|
||||||
"ReadOnly": false,
|
"ReadOnly": false,
|
||||||
"Visible": false,
|
"Visible": true,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 15,
|
"ListOrderNo": 14,
|
||||||
"SourceDbType": 6,
|
"SourceDbType": 6,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "datetime",
|
"DataType": "datetime",
|
||||||
|
|
@ -2918,14 +2761,14 @@
|
||||||
"JoinFieldName2": null,
|
"JoinFieldName2": null,
|
||||||
"BaseTableJoinFieldName2_OrValue": null
|
"BaseTableJoinFieldName2_OrValue": null
|
||||||
},
|
},
|
||||||
"FieldName": "ApprovalNote",
|
"FieldName": "ApprovalDescription",
|
||||||
"CaptionName": "App.Listform.ListformField.ApprovalNote",
|
"CaptionName": "App.Listform.ListformField.ApprovalDescription",
|
||||||
"PlaceHolder": null,
|
"PlaceHolder": null,
|
||||||
"ReadOnly": false,
|
"ReadOnly": false,
|
||||||
"Visible": false,
|
"Visible": true,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 16,
|
"ListOrderNo": 15,
|
||||||
"SourceDbType": 16,
|
"SourceDbType": 16,
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
"DataType": "string",
|
"DataType": "string",
|
||||||
|
|
@ -3015,7 +2858,7 @@
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": null,
|
"EditOrderNo": null,
|
||||||
"EditGroupOrderNo": null,
|
"EditGroupOrderNo": null,
|
||||||
"EditorType2": "dxTextArea",
|
"EditorType2": "dxTextBox",
|
||||||
"ColSpan": 1,
|
"ColSpan": 1,
|
||||||
"EditorOptions": null,
|
"EditorOptions": null,
|
||||||
"GridBoxOptions": null,
|
"GridBoxOptions": null,
|
||||||
|
|
@ -3035,7 +2878,9 @@
|
||||||
"LanguageKeys": [
|
"LanguageKeys": [
|
||||||
"App.Prc.MaterialRequestDeptApproval",
|
"App.Prc.MaterialRequestDeptApproval",
|
||||||
"App.Prc.MaterialRequestDeptApproval.Title",
|
"App.Prc.MaterialRequestDeptApproval.Title",
|
||||||
"App.Prc.MaterialRequestDeptApproval.Desc"
|
"App.Prc.MaterialRequestDeptApproval.Desc",
|
||||||
|
"App.Listform.ListformField.ApprovalUserName",
|
||||||
|
"App.Listform.ListformField.ApprovalDescription"
|
||||||
],
|
],
|
||||||
"PermissionGroupNames": [],
|
"PermissionGroupNames": [],
|
||||||
"PermissionNames": [
|
"PermissionNames": [
|
||||||
|
|
@ -3055,6 +2900,6 @@
|
||||||
},
|
},
|
||||||
"DevRequest": {
|
"DevRequest": {
|
||||||
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
||||||
"Title": "Satınalma -> Satınalma Talepleri"
|
"Title": "Satınalma -> Malzeme Talepleri"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,15 +26,15 @@
|
||||||
},
|
},
|
||||||
"Language": {
|
"Language": {
|
||||||
"MenuParentEn": "Material Requests",
|
"MenuParentEn": "Material Requests",
|
||||||
"MenuParentTr": "Satınalma Talepleri",
|
"MenuParentTr": "Malzeme Talepleri",
|
||||||
"PermissionGroupEn": "Purchasing",
|
"PermissionGroupEn": "Purchasing",
|
||||||
"PermissionGroupTr": "Satınalma",
|
"PermissionGroupTr": "Satınalma",
|
||||||
"MenuEn": "Request Lines",
|
"MenuEn": "Request Lines",
|
||||||
"MenuTr": "Talep Satırları",
|
"MenuTr": "Talep Satırları",
|
||||||
"TitleEn": "Request Lines",
|
"TitleEn": "Request Lines",
|
||||||
"TitleTr": "Talep Satırları",
|
"TitleTr": "Talep Satırları",
|
||||||
"DescEn": "Request Lines — the products requested in a material request.\nWhere: Purchasing › Material Requests › any list › Request Lines tab (no menu of its own).\nFields: product (active, purchasable products), quantity, unit (copied from the product's purchase unit, read-only), fulfilment (Purchasing / Warehouse Transfer), due date, request reason, delivered quantity (read-only), purchasing responsible, line status (Waiting / Done / Cancelled).\nRules: lines can be added or deleted only while the request has not been sent for approval. Changing line statuses recalculates the request status (Partially Done / Done).",
|
"DescEn": "Request Lines — the products requested in a material request.\nWhere: Purchasing › Material Requests › My Requests, Awaiting Department Approval and Awaiting Purchasing Approval › Request Lines tab; All Requests shows the lines as columns (no menu of its own).\nFields: product (active, purchasable products), quantity, unit (copied from the product's purchase unit, read-only), fulfilment (Purchasing / Warehouse Transfer), due date, request reason, delivered quantity (read-only), purchasing responsible, line status (Waiting / Done / Cancelled).\nRules: lines can be added or deleted only while the request has not been sent for approval. Changing line statuses recalculates the request status (Partially Done / Done).",
|
||||||
"DescTr": "Talep Satırları — malzeme talebinde istenen ürünler.\nNerede: Satınalma › Satınalma Talepleri › herhangi bir liste › Talep Satırları sekmesi (kendi menüsü yoktur).\nAlanlar: ürün (aktif ve satın alınabilir ürünler), miktar, birim (ürünün satınalma biriminden gelir, salt okunur), karşılama şekli (Satınalma / Depodan Transfer), termin tarihi, talep nedeni, teslim edilen miktar (salt okunur), satınalma sorumlusu, satır durumu (Bekliyor / Tamamlandı / İptal).\nKurallar: satır yalnız talep onaya gönderilmeden eklenip silinebilir. Satır durumları değişince talebin durumu yeniden hesaplanır (Kısmi Tamamlandı / Tamamlandı).",
|
"DescTr": "Talep Satırları — malzeme talebinde istenen ürünler.\nNerede: Satınalma › Malzeme Talepleri › Taleplerim, Departman Onayı Bekleyenler ve Satınalma Onayı Bekleyenler › Talep Satırları sekmesi; Tüm Talepler satırları kolon olarak gösterir (kendi menüsü yoktur).\nAlanlar: ürün (aktif ve satın alınabilir ürünler), miktar, birim (ürünün satınalma biriminden gelir, salt okunur), karşılama şekli (Satınalma / Depodan Transfer), termin tarihi, talep nedeni, teslim edilen miktar (salt okunur), satınalma sorumlusu, satır durumu (Bekliyor / Tamamlandı / İptal).\nKurallar: satır yalnız talep onaya gönderilmeden eklenip silinebilir. Satır durumları değişince talebin durumu yeniden hesaplanır (Kısmi Tamamlandı / Tamamlandı).",
|
||||||
"Texts": [
|
"Texts": [
|
||||||
{
|
{
|
||||||
"Key": "App.StaticLookup.Purchase",
|
"Key": "App.StaticLookup.Purchase",
|
||||||
|
|
@ -151,7 +151,7 @@
|
||||||
"SeedSyncDelete": false,
|
"SeedSyncDelete": false,
|
||||||
"DeleteCommand": "UPDATE \"Prc_T_MaterialRequestItem\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
"DeleteCommand": "UPDATE \"Prc_T_MaterialRequestItem\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\" IN @Id",
|
||||||
"DeleteBeforeCommand": "IF EXISTS (SELECT 1 FROM Prc_T_MaterialRequestItem i INNER JOIN Prc_T_MaterialRequest r ON r.Id = i.MaterialRequestId WHERE i.Id IN @Id AND ISNULL(r.ApprovalStatus, N'') <> N'') THROW 50002, N'Onaya gönderilmiş talepten satır silinemez.', 1;",
|
"DeleteBeforeCommand": "IF EXISTS (SELECT 1 FROM Prc_T_MaterialRequestItem i INNER JOIN Prc_T_MaterialRequest r ON r.Id = i.MaterialRequestId WHERE i.Id IN @Id AND ISNULL(r.ApprovalStatus, N'') <> N'') THROW 50002, N'Onaya gönderilmiş talepten satır silinemez.', 1;",
|
||||||
"DeleteAfterCommand": "DECLARE @_r uniqueidentifier = (SELECT TOP 1 MaterialRequestId FROM Prc_T_MaterialRequestItem WHERE Id IN @Id); EXEC dbo.Prc_P_MaterialRequestAfterSave @_r, @DeleterId",
|
"DeleteAfterCommand": null,
|
||||||
"DeleteFieldsDefaultValueDto": [
|
"DeleteFieldsDefaultValueDto": [
|
||||||
{
|
{
|
||||||
"FieldName": "DeleterId",
|
"FieldName": "DeleterId",
|
||||||
|
|
@ -170,7 +170,7 @@
|
||||||
],
|
],
|
||||||
"InsertCommand": null,
|
"InsertCommand": null,
|
||||||
"InsertBeforeCommand": "IF EXISTS (SELECT 1 FROM Prc_T_MaterialRequest WHERE Id = @MaterialRequestId AND ISNULL(ApprovalStatus, N'') <> N'') THROW 50001, N'Onaya gönderilmiş talebe satır eklenemez.', 1;",
|
"InsertBeforeCommand": "IF EXISTS (SELECT 1 FROM Prc_T_MaterialRequest WHERE Id = @MaterialRequestId AND ISNULL(ApprovalStatus, N'') <> N'') THROW 50001, N'Onaya gönderilmiş talebe satır eklenemez.', 1;",
|
||||||
"InsertAfterCommand": "EXEC dbo.Prc_P_MaterialRequestAfterSave @MaterialRequestId, @CreatorId",
|
"InsertAfterCommand": null,
|
||||||
"InsertFieldsDefaultValueDto": [
|
"InsertFieldsDefaultValueDto": [
|
||||||
{
|
{
|
||||||
"FieldName": "CreationTime",
|
"FieldName": "CreationTime",
|
||||||
|
|
@ -203,7 +203,7 @@
|
||||||
],
|
],
|
||||||
"UpdateCommand": null,
|
"UpdateCommand": null,
|
||||||
"UpdateBeforeCommand": null,
|
"UpdateBeforeCommand": null,
|
||||||
"UpdateAfterCommand": "DECLARE @_r uniqueidentifier = (SELECT TOP 1 MaterialRequestId FROM Prc_T_MaterialRequestItem WHERE Id IN @Id); EXEC dbo.Prc_P_MaterialRequestAfterSave @_r, @LastModifierId",
|
"UpdateAfterCommand": null,
|
||||||
"UpdateFieldsDefaultValueDto": [
|
"UpdateFieldsDefaultValueDto": [
|
||||||
{
|
{
|
||||||
"FieldName": "LastModificationTime",
|
"FieldName": "LastModificationTime",
|
||||||
|
|
@ -513,6 +513,7 @@
|
||||||
"ApprovalDescriptionFieldName": null,
|
"ApprovalDescriptionFieldName": null,
|
||||||
"ApprovalIsFilterUserName": false,
|
"ApprovalIsFilterUserName": false,
|
||||||
"ApprovalIsResetWorkflow": false,
|
"ApprovalIsResetWorkflow": false,
|
||||||
|
"ApprovalIsManualStart": false,
|
||||||
"Criteria": []
|
"Criteria": []
|
||||||
},
|
},
|
||||||
"UserId": null,
|
"UserId": null,
|
||||||
|
|
@ -920,7 +921,7 @@
|
||||||
"CaptionName": "App.Listform.ListformField.MaterialRequestId",
|
"CaptionName": "App.Listform.ListformField.MaterialRequestId",
|
||||||
"PlaceHolder": null,
|
"PlaceHolder": null,
|
||||||
"ReadOnly": false,
|
"ReadOnly": false,
|
||||||
"Visible": true,
|
"Visible": false,
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"Width": 0,
|
"Width": 0,
|
||||||
"ListOrderNo": 2,
|
"ListOrderNo": 2,
|
||||||
|
|
@ -1841,7 +1842,7 @@
|
||||||
"Format": null,
|
"Format": null,
|
||||||
"EditOrderNo": 6,
|
"EditOrderNo": 6,
|
||||||
"EditGroupOrderNo": 1,
|
"EditGroupOrderNo": 1,
|
||||||
"EditorType2": "dxTextBox",
|
"EditorType2": "dxTextArea",
|
||||||
"ColSpan": 2,
|
"ColSpan": 2,
|
||||||
"EditorOptions": "{\"showClearButton\":true,\"maxLength\":256}",
|
"EditorOptions": "{\"showClearButton\":true,\"maxLength\":256}",
|
||||||
"GridBoxOptions": null,
|
"GridBoxOptions": null,
|
||||||
|
|
@ -2279,6 +2280,6 @@
|
||||||
},
|
},
|
||||||
"DevRequest": {
|
"DevRequest": {
|
||||||
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
||||||
"Title": "Satınalma -> Satınalma Talepleri"
|
"Title": "Satınalma -> Malzeme Talepleri"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,7 @@
|
||||||
"TitleEn": "Rejection Reasons",
|
"TitleEn": "Rejection Reasons",
|
||||||
"TitleTr": "Red Nedenleri",
|
"TitleTr": "Red Nedenleri",
|
||||||
"DescEn": "Rejection Reasons — reasons a purchasing request can be rejected for (no need, quantity not suitable, wrong product, budget not suitable, other).\nWhere: Purchasing › Definitions › Rejection Reasons.\nFields: reason, sort order (order in the selection list), active.\nUsed by: Purchasing › Material Requests — the department approver or purchasing selects a reason when rejecting a request. Only active reasons are offered; deactivate a used reason instead of deleting it.",
|
"DescEn": "Rejection Reasons — reasons a purchasing request can be rejected for (no need, quantity not suitable, wrong product, budget not suitable, other).\nWhere: Purchasing › Definitions › Rejection Reasons.\nFields: reason, sort order (order in the selection list), active.\nUsed by: Purchasing › Material Requests — the department approver or purchasing selects a reason when rejecting a request. Only active reasons are offered; deactivate a used reason instead of deleting it.",
|
||||||
"DescTr": "Red Nedenleri — satınalma taleplerinin reddedilme sebepleri (ihtiyaç yok, miktar uygun değil, yanlış ürün, bütçe uygun değil, diğer).\nNerede: Satınalma › Tanımlamalar › Red Nedenleri.\nAlanlar: red nedeni, sıra (seçim listesindeki sırası), aktif.\nKullanıldığı yer: Satınalma › Satınalma Talepleri — departman onaycısı ya da satınalma talebi reddederken neden seçer. Yalnız aktif nedenler listelenir; kullanılmış neden silinmez, pasife alınır."
|
"DescTr": "Red Nedenleri — satınalma taleplerinin reddedilme sebepleri (ihtiyaç yok, miktar uygun değil, yanlış ürün, bütçe uygun değil, diğer).\nNerede: Satınalma › Tanımlamalar › Red Nedenleri.\nAlanlar: red nedeni, sıra (seçim listesindeki sırası), aktif.\nKullanıldığı yer: Satınalma › Malzeme Talepleri — departman onaycısı ya da satınalma talebi reddederken neden seçer. Yalnız aktif nedenler listelenir; kullanılmış neden silinmez, pasife alınır."
|
||||||
},
|
},
|
||||||
"Groups": [
|
"Groups": [
|
||||||
{
|
{
|
||||||
|
|
@ -1251,6 +1251,6 @@
|
||||||
},
|
},
|
||||||
"DevRequest": {
|
"DevRequest": {
|
||||||
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
"Id": "53294e77-a4fb-45e4-a80e-acf07b5d75be",
|
||||||
"Title": "Satınalma -> Satınalma Talepleri"
|
"Title": "Satınalma -> Malzeme Talepleri"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +72,7 @@ export interface ListFormWizardDto {
|
||||||
allowDeleting: boolean
|
allowDeleting: boolean
|
||||||
confirmDelete: boolean
|
confirmDelete: boolean
|
||||||
allowDetail: boolean
|
allowDetail: boolean
|
||||||
|
openDetailAfterInsert: boolean
|
||||||
/**
|
/**
|
||||||
* Ekran verisi `data/{ListFormCode}.json` seed dosyasına aynalanır; migrate satırları geri yükler.
|
* Ekran verisi `data/{ListFormCode}.json` seed dosyasına aynalanır; migrate satırları geri yükler.
|
||||||
* 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.
|
||||||
|
|
|
||||||
|
|
@ -490,6 +490,7 @@ export interface GridEditingDto {
|
||||||
allowAllDeleting: boolean
|
allowAllDeleting: boolean
|
||||||
allowAdding: boolean
|
allowAdding: boolean
|
||||||
allowDuplicate: boolean
|
allowDuplicate: boolean
|
||||||
|
openDetailAfterInsert: boolean
|
||||||
useIcons: boolean
|
useIcons: boolean
|
||||||
confirmDelete: boolean
|
confirmDelete: boolean
|
||||||
newRowPosition?: NewRowPosition
|
newRowPosition?: NewRowPosition
|
||||||
|
|
@ -982,6 +983,16 @@ export interface ListFormWorkflowCriteriaDto {
|
||||||
positionX: number
|
positionX: number
|
||||||
positionY: number
|
positionY: number
|
||||||
compareOutcomes: CompareOutcomeDto[]
|
compareOutcomes: CompareOutcomeDto[]
|
||||||
|
rejectReason?: WorkflowRejectReasonDto | null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Onay düğümünde reddin listeden seçilen nedeni; tablo boşsa red yalnızca açıklama ister. */
|
||||||
|
export interface WorkflowRejectReasonDto {
|
||||||
|
tableName: string
|
||||||
|
valueField: string
|
||||||
|
textField: string
|
||||||
|
targetField?: string | null
|
||||||
|
required: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LayoutDto {
|
export interface LayoutDto {
|
||||||
|
|
|
||||||
|
|
@ -237,3 +237,9 @@
|
||||||
// },
|
// },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Kaydın form sayfası adresi: görüntüleme (`formView`) ya da düzenleme (`formEdit`). */
|
||||||
|
export const getFormRecordUrl = (listFormCode: string, id: unknown, edit = false) =>
|
||||||
|
(edit ? ROUTES_ENUM.protected.admin.formEdit : ROUTES_ENUM.protected.admin.formView)
|
||||||
|
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
||||||
|
.replace(':id', encodeURIComponent(String(id)))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import apiService from './api.service'
|
import apiService from './api.service'
|
||||||
|
import type { WorkflowRejectReasonDto } from '@/proxy/form/models'
|
||||||
|
|
||||||
|
export type { WorkflowRejectReasonDto }
|
||||||
|
|
||||||
export interface WorkflowConditionDto {
|
export interface WorkflowConditionDto {
|
||||||
compareColumn: string
|
compareColumn: string
|
||||||
|
|
@ -12,6 +15,11 @@ export interface CompareOutcomeDto {
|
||||||
conditions: WorkflowConditionDto[]
|
conditions: WorkflowConditionDto[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorkflowRejectReasonItemDto {
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface WorkflowCriteriaDto {
|
export interface WorkflowCriteriaDto {
|
||||||
id: string
|
id: string
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -30,6 +38,7 @@ export interface WorkflowCriteriaDto {
|
||||||
positionX: number
|
positionX: number
|
||||||
positionY: number
|
positionY: number
|
||||||
compareOutcomes: CompareOutcomeDto[]
|
compareOutcomes: CompareOutcomeDto[]
|
||||||
|
rejectReason?: WorkflowRejectReasonDto | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowCriteriasDto {
|
export interface WorkflowCriteriasDto {
|
||||||
|
|
@ -117,11 +126,22 @@ export const workflowService = {
|
||||||
approved: boolean,
|
approved: boolean,
|
||||||
note?: string,
|
note?: string,
|
||||||
currentNodeId?: string,
|
currentNodeId?: string,
|
||||||
|
rejectReasonId?: string,
|
||||||
) {
|
) {
|
||||||
const response = await apiService.fetchData<WorkflowRunResultDto>({
|
const response = await apiService.fetchData<WorkflowRunResultDto>({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${baseUrl}/decision`,
|
url: `${baseUrl}/decision`,
|
||||||
data: { listFormCode, keys, approved, note, currentNodeId },
|
data: { listFormCode, keys, approved, note, currentNodeId, rejectReasonId },
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRejectReasons(listFormCode: string, criteriaId: string) {
|
||||||
|
const response = await apiService.fetchData<WorkflowRejectReasonItemDto[]>({
|
||||||
|
method: 'GET',
|
||||||
|
url: `${baseUrl}/reject-reasons`,
|
||||||
|
params: { listFormCode, criteriaId },
|
||||||
})
|
})
|
||||||
|
|
||||||
return response.data
|
return response.data
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import type {
|
||||||
SaveCriteriaInput,
|
SaveCriteriaInput,
|
||||||
WorkflowConditionDto,
|
WorkflowConditionDto,
|
||||||
WorkflowCriteriaDto,
|
WorkflowCriteriaDto,
|
||||||
|
WorkflowRejectReasonDto,
|
||||||
} from '@/services/workflow.service'
|
} from '@/services/workflow.service'
|
||||||
|
|
||||||
export type WorkflowCriteriaForm = Partial<WorkflowCriteriaDto> & {
|
export type WorkflowCriteriaForm = Partial<WorkflowCriteriaDto> & {
|
||||||
|
|
@ -470,6 +471,22 @@ export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput
|
||||||
positionX: Number(item.positionX || 32),
|
positionX: Number(item.positionX || 32),
|
||||||
positionY: Number(item.positionY || 150),
|
positionY: Number(item.positionY || 150),
|
||||||
compareOutcomes,
|
compareOutcomes,
|
||||||
|
rejectReason: item.kind === 'Approval' ? normalizeRejectReason(item.rejectReason) : null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tablo, Id ve metin kolonu birlikte seçilmedikçe tanım saklanmaz; red yalnızca açıklama ister. */
|
||||||
|
function normalizeRejectReason(
|
||||||
|
value?: WorkflowRejectReasonDto | null,
|
||||||
|
): WorkflowRejectReasonDto | null {
|
||||||
|
if (!value?.tableName || !value.valueField || !value.textField) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableName: value.tableName,
|
||||||
|
valueField: value.valueField,
|
||||||
|
textField: value.textField,
|
||||||
|
targetField: value.targetField || '',
|
||||||
|
required: value.required !== false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import { useEffect, useState } from 'react'
|
import { lazy, Suspense, useEffect, useState } from 'react'
|
||||||
|
import { FaTable } from 'react-icons/fa'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { FormEditProps } from './FormEdit'
|
import { FormEditProps } from './FormEdit'
|
||||||
import { dbSourceTypeOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from './options'
|
import { dbSourceTypeOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from './options'
|
||||||
|
|
@ -15,6 +16,8 @@ import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
|
|
||||||
|
const SqlTableDesignerDialog = lazy(() => import('@/views/developerKit/SqlTableDesignerDialog'))
|
||||||
|
|
||||||
const schema = Yup.object().shape({
|
const schema = Yup.object().shape({
|
||||||
isOrganizationUnit: Yup.bool(),
|
isOrganizationUnit: Yup.bool(),
|
||||||
isTenant: Yup.bool(),
|
isTenant: Yup.bool(),
|
||||||
|
|
@ -133,6 +136,21 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dbObjects])
|
}, [dbObjects])
|
||||||
|
|
||||||
|
// ── Table Designer ──────────────────────────────────────────────────────────
|
||||||
|
const [designTable, setDesignTable] = useState<{ schemaName: string; tableName: string } | null>(
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
const findSelectedTable = (selectCommand?: string, selectCommandType?: SelectCommandTypeEnum) =>
|
||||||
|
selectCommandType === SelectCommandTypeEnum.Table && selectCommand
|
||||||
|
? dbObjects?.tables.find(
|
||||||
|
(t) =>
|
||||||
|
t.tableName === selectCommand ||
|
||||||
|
t.fullName === selectCommand ||
|
||||||
|
`${t.schemaName}.${t.tableName}` === selectCommand,
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
|
||||||
if (!initialValues) {
|
if (!initialValues) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +178,9 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
checked={field.value === true}
|
checked={field.value === true}
|
||||||
onChange={(checked) => form.setFieldValue(field.name, checked)}
|
onChange={(checked) => form.setFieldValue(field.name, checked)}
|
||||||
>
|
>
|
||||||
{translate(`::App.ListFormEdit.${name[0].toUpperCase()}${name.slice(1)}`)}
|
{translate(
|
||||||
|
`::App.ListFormEdit.${name[0].toUpperCase()}${name.slice(1)}`,
|
||||||
|
)}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
@ -262,56 +282,80 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
|
const selectedTable = findSelectedTable(field.value, values.selectCommandType)
|
||||||
return (
|
return (
|
||||||
<Select
|
<div className="flex items-center gap-2">
|
||||||
isClearable
|
<div className="flex-1 min-w-0">
|
||||||
componentAs={CreatableSelect}
|
<Select
|
||||||
field={field}
|
isClearable
|
||||||
form={form}
|
componentAs={CreatableSelect}
|
||||||
isLoading={isLoadingDbObjects}
|
field={field}
|
||||||
options={grouped}
|
form={form}
|
||||||
placeholder={
|
isLoading={isLoadingDbObjects}
|
||||||
isLoadingDbObjects
|
options={grouped}
|
||||||
? translate('::App.Platform.Loading')
|
placeholder={
|
||||||
: translate(
|
isLoadingDbObjects
|
||||||
'::App.ListFormEdit.DatabaseDataSourceSelectCommand',
|
? translate('::App.Platform.Loading')
|
||||||
)
|
: translate('::App.ListFormEdit.DatabaseDataSourceSelectCommand')
|
||||||
}
|
}
|
||||||
value={field.value ? { label: field.value, value: field.value } : null}
|
value={
|
||||||
onChange={(option: any) => {
|
field.value ? { label: field.value, value: field.value } : null
|
||||||
if (!option) {
|
}
|
||||||
form.setFieldValue(field.name, '')
|
onChange={(option: any) => {
|
||||||
form.setFieldValue('selectCommandType', undefined)
|
if (!option) {
|
||||||
form.setFieldValue('keyFieldName', '')
|
form.setFieldValue(field.name, '')
|
||||||
form.setFieldValue('keyFieldDbSourceType', undefined)
|
form.setFieldValue('selectCommandType', undefined)
|
||||||
setSelectCommandColumns([])
|
form.setFieldValue('keyFieldName', '')
|
||||||
return
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
form.setFieldValue(field.name, option.value)
|
||||||
|
const type = option.__isNew__
|
||||||
|
? SelectCommandTypeEnum.Query
|
||||||
|
: (option.__type ?? SelectCommandTypeEnum.Query)
|
||||||
|
form.setFieldValue('selectCommandType', type)
|
||||||
|
form.setFieldValue('keyFieldName', '')
|
||||||
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
if (
|
||||||
|
!option.__isNew__ &&
|
||||||
|
option.__schema != null &&
|
||||||
|
option.__rawName
|
||||||
|
) {
|
||||||
|
loadColumns(
|
||||||
|
values.dataSourceCode ?? '',
|
||||||
|
option.__schema,
|
||||||
|
option.__rawName,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onCreateOption={(inputValue: string) => {
|
||||||
|
form.setFieldValue(field.name, inputValue)
|
||||||
|
form.setFieldValue('selectCommandType', SelectCommandTypeEnum.Query)
|
||||||
|
form.setFieldValue('keyFieldName', '')
|
||||||
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
icon={<FaTable />}
|
||||||
|
disabled={!values.dataSourceCode || !selectedTable}
|
||||||
|
onClick={() =>
|
||||||
|
selectedTable &&
|
||||||
|
setDesignTable({
|
||||||
|
schemaName: selectedTable.schemaName,
|
||||||
|
tableName: selectedTable.tableName,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
form.setFieldValue(field.name, option.value)
|
>
|
||||||
const type = option.__isNew__
|
{translate('::App.ListFormEdit.DesignTable')}
|
||||||
? SelectCommandTypeEnum.Query
|
</Button>
|
||||||
: (option.__type ?? SelectCommandTypeEnum.Query)
|
</div>
|
||||||
form.setFieldValue('selectCommandType', type)
|
|
||||||
form.setFieldValue('keyFieldName', '')
|
|
||||||
form.setFieldValue('keyFieldDbSourceType', undefined)
|
|
||||||
if (!option.__isNew__ && option.__schema != null && option.__rawName) {
|
|
||||||
loadColumns(
|
|
||||||
values.dataSourceCode ?? '',
|
|
||||||
option.__schema,
|
|
||||||
option.__rawName,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
setSelectCommandColumns([])
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onCreateOption={(inputValue: string) => {
|
|
||||||
form.setFieldValue(field.name, inputValue)
|
|
||||||
form.setFieldValue('selectCommandType', SelectCommandTypeEnum.Query)
|
|
||||||
form.setFieldValue('keyFieldName', '')
|
|
||||||
form.setFieldValue('keyFieldDbSourceType', undefined)
|
|
||||||
setSelectCommandColumns([])
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
@ -343,9 +387,7 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
) : selectCommandColumns.length === 0 && !isLoadingColumns ? (
|
) : selectCommandColumns.length === 0 && !isLoadingColumns ? (
|
||||||
<span className="text-xs ml-2 text-gray-400">
|
<span className="text-xs ml-2 text-gray-400">
|
||||||
{translate('::App.ListFormEdit.ColumnsLoadAfterSelect', {
|
{translate('::App.ListFormEdit.ColumnsLoadAfterSelect', {
|
||||||
command: translate(
|
command: translate('::App.ListFormEdit.DatabaseDataSourceSelectCommand'),
|
||||||
'::App.ListFormEdit.DatabaseDataSourceSelectCommand',
|
|
||||||
),
|
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
) : null
|
) : null
|
||||||
|
|
@ -418,7 +460,9 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
checked={field.value === true}
|
checked={field.value === true}
|
||||||
onChange={(checked) => form.setFieldValue(field.name, checked)}
|
onChange={(checked) => form.setFieldValue(field.name, checked)}
|
||||||
>
|
>
|
||||||
{translate(`::App.ListFormEdit.${name[0].toUpperCase()}${name.slice(1)}`)}
|
{translate(
|
||||||
|
`::App.ListFormEdit.${name[0].toUpperCase()}${name.slice(1)}`,
|
||||||
|
)}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
@ -428,12 +472,29 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Button block size="sm" variant="solid" loading={isSubmitting}>
|
<Button block size="sm" variant="solid" loading={isSubmitting}>
|
||||||
{isSubmitting ? translate('::App.Platform.Saving') : translate('::App.Platform.Save')}
|
{isSubmitting
|
||||||
|
? translate('::App.Platform.Saving')
|
||||||
|
: translate('::App.Platform.Save')}
|
||||||
</Button>
|
</Button>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
||||||
|
{designTable && (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<SqlTableDesignerDialog
|
||||||
|
isOpen
|
||||||
|
dataSource={currentDataSource}
|
||||||
|
initialTableData={designTable}
|
||||||
|
onClose={() => setDesignTable(null)}
|
||||||
|
onDeployed={async (table) => {
|
||||||
|
await loadDbObjects(currentDataSource)
|
||||||
|
await loadColumns(currentDataSource, table.schemaName || 'dbo', table.tableName)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
import { Container } from '@/components/shared'
|
import { Container } from '@/components/shared'
|
||||||
import { Button, Card, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui'
|
import { Button, Card, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui'
|
||||||
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
|
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
|
||||||
|
import type { GridEditingDto, GridEditingPopupDto } from '@/proxy/form/models'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import type {
|
import { Field, FieldProps, Form, Formik, getIn } from 'formik'
|
||||||
GridsEditMode,
|
import type { ReactNode } from 'react'
|
||||||
GridsEditRefreshMode,
|
|
||||||
NewRowPosition,
|
|
||||||
StartEditAction,
|
|
||||||
} from 'devextreme/common/grids'
|
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
|
||||||
import { object } from 'yup'
|
|
||||||
import { FormEditProps } from './FormEdit'
|
import { FormEditProps } from './FormEdit'
|
||||||
import {
|
import {
|
||||||
editingOptionModeOptions,
|
editingOptionModeOptions,
|
||||||
|
|
@ -19,7 +14,11 @@ import {
|
||||||
editingOptionStartEditActionOptions,
|
editingOptionStartEditActionOptions,
|
||||||
} from './options'
|
} from './options'
|
||||||
|
|
||||||
const schema = object()
|
// `editingOptionDto` altındaki alan yolu; popup alanları `popup.` önekiyle.
|
||||||
|
type EditingField =
|
||||||
|
| Exclude<keyof GridEditingDto, 'popup'>
|
||||||
|
| `popup.${keyof GridEditingPopupDto}`
|
||||||
|
type SelectOption = { value: string; label: string }
|
||||||
|
|
||||||
function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -33,467 +32,163 @@ function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
||||||
<Container>
|
<Container>
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={listFormValues}
|
initialValues={listFormValues}
|
||||||
validationSchema={schema}
|
|
||||||
onSubmit={async (values, formikHelpers) => {
|
onSubmit={async (values, formikHelpers) => {
|
||||||
await props.onSubmit(ListFormEditTabs.Edit.EditForm, values, formikHelpers)
|
await props.onSubmit(ListFormEditTabs.Edit.EditForm, values, formikHelpers)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ touched, errors, values, isSubmitting }) => (
|
{({ touched, errors, values, isSubmitting }) => {
|
||||||
<Form>
|
const fieldItem = (
|
||||||
<FormContainer size="sm">
|
field: EditingField,
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
labelKey: string,
|
||||||
<Card className="my-2 col-span-2" header="General">
|
render: (name: string, label: string) => ReactNode,
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
) => {
|
||||||
<div>
|
const name = `editingOptionDto.${field}`
|
||||||
<FormItem
|
const label = translate(labelKey)
|
||||||
label={translate('::App.ListFormSchedulerOptions.AllowAdding')}
|
return (
|
||||||
invalid={
|
<FormItem
|
||||||
errors.editingOptionDto?.allowAdding &&
|
key={name}
|
||||||
touched.editingOptionDto?.allowAdding
|
label={label}
|
||||||
}
|
invalid={!!getIn(errors, name) && !!getIn(touched, name)}
|
||||||
errorMessage={errors.editingOptionDto?.allowAdding}
|
errorMessage={getIn(errors, name)}
|
||||||
>
|
>
|
||||||
<Field
|
{render(name, label)}
|
||||||
name="editingOptionDto.allowAdding"
|
</FormItem>
|
||||||
placeholder={translate('::App.ListFormSchedulerOptions.AllowAdding')}
|
)
|
||||||
component={Checkbox}
|
}
|
||||||
/>
|
|
||||||
</FormItem>
|
const checkbox = (field: EditingField, labelKey: string) =>
|
||||||
<FormItem
|
fieldItem(field, labelKey, (name, label) => (
|
||||||
label={translate('::App.ListFormEdit.EditingAddPageUrl')}
|
<Field name={name} placeholder={label} component={Checkbox} />
|
||||||
invalid={
|
))
|
||||||
errors.editingOptionDto?.addPageUrl &&
|
|
||||||
touched.editingOptionDto?.addPageUrl
|
const input = (field: EditingField, labelKey: string, type: 'text' | 'number') =>
|
||||||
}
|
fieldItem(field, labelKey, (name, label) => (
|
||||||
errorMessage={errors.editingOptionDto?.addPageUrl}
|
<Field
|
||||||
>
|
className={type === 'number' ? 'w-20' : undefined}
|
||||||
<Field
|
type={type}
|
||||||
type="text"
|
autoComplete="off"
|
||||||
autoComplete="off"
|
name={name}
|
||||||
name="editingOptionDto.addPageUrl"
|
placeholder={label}
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAddPageUrl')}
|
component={Input}
|
||||||
component={Input}
|
/>
|
||||||
/>
|
))
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
const text = (field: EditingField, labelKey: string) => input(field, labelKey, 'text')
|
||||||
label={translate('::App.ListFormEdit.EditingAddDialogName')}
|
const number = (field: EditingField, labelKey: string) =>
|
||||||
invalid={
|
input(field, labelKey, 'number')
|
||||||
errors.editingOptionDto?.addDialogName &&
|
|
||||||
touched.editingOptionDto?.addDialogName
|
const select = (field: EditingField, labelKey: string, options: SelectOption[]) =>
|
||||||
}
|
fieldItem(field, labelKey, (name, label) => (
|
||||||
errorMessage={errors.editingOptionDto?.addDialogName}
|
<Field name={name} placeholder={label}>
|
||||||
>
|
{({ field: formikField, form }: FieldProps) => (
|
||||||
<Field
|
<Select
|
||||||
type="text"
|
field={formikField}
|
||||||
autoComplete="off"
|
form={form}
|
||||||
name="editingOptionDto.addDialogName"
|
isClearable={true}
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAddDialogName')}
|
options={options}
|
||||||
component={Input}
|
value={options.filter(
|
||||||
/>
|
(option) => option.value === getIn(values, name),
|
||||||
</FormItem>
|
)}
|
||||||
<FormItem
|
onChange={(option) => form.setFieldValue(formikField.name, option?.value)}
|
||||||
label={translate('::App.ListFormEdit.EditingAddDialogParameters')}
|
/>
|
||||||
invalid={
|
)}
|
||||||
errors.editingOptionDto?.addDialogParameters &&
|
</Field>
|
||||||
touched.editingOptionDto?.addDialogParameters
|
))
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.addDialogParameters}
|
return (
|
||||||
>
|
<Form>
|
||||||
<Field
|
<FormContainer size="sm">
|
||||||
type="text"
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
autoComplete="off"
|
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-2 gap-3 content-start">
|
||||||
name="editingOptionDto.addDialogParameters"
|
<Card
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAddDialogParameters')}
|
className="my-2"
|
||||||
component={Input}
|
header={translate('::App.ListFormEdit.EditingGeneral')}
|
||||||
/>
|
>
|
||||||
</FormItem>
|
{select('mode', '::App.ListFormEdit.EditingMode', editingOptionModeOptions)}
|
||||||
<FormItem
|
{select(
|
||||||
label={translate('::App.ListFormEdit.AllowUpdating')}
|
'refreshMode',
|
||||||
invalid={
|
'::App.ListFormEdit.EditingRefreshMode',
|
||||||
errors.editingOptionDto?.allowUpdating &&
|
editingOptionRefreshModeOptions,
|
||||||
touched.editingOptionDto?.allowUpdating
|
)}
|
||||||
}
|
{select(
|
||||||
errorMessage={errors.editingOptionDto?.allowUpdating}
|
'startEditAction',
|
||||||
>
|
'::App.ListFormEdit.EditingStartEditAction',
|
||||||
<Field
|
editingOptionStartEditActionOptions,
|
||||||
name="editingOptionDto.allowUpdating"
|
)}
|
||||||
placeholder={translate('::App.ListFormEdit.AllowUpdating')}
|
{text('editColumnName', '::App.ListFormEdit.EditingEditColumnName')}
|
||||||
component={Checkbox}
|
<div className="grid grid-cols-2 gap-3">
|
||||||
/>
|
{checkbox('useIcons', '::App.ListFormEdit.EditingUseIcons')}
|
||||||
</FormItem>
|
{checkbox(
|
||||||
<FormItem
|
'selectTextOnEditStart',
|
||||||
label={translate(
|
'::App.ListFormEdit.EditingSelectTextOnEditStart',
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<Card className="my-2" header={translate('::App.Platform.New')}>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
{checkbox('allowAdding', '::App.ListFormSchedulerOptions.AllowAdding')}
|
||||||
|
{checkbox(
|
||||||
|
'openDetailAfterInsert',
|
||||||
|
'::App.ListFormEdit.EditingOpenDetailAfterInsert',
|
||||||
|
)}
|
||||||
|
{checkbox('allowDuplicate', '::App.ListFormEdit.EditingAllowDuplicate')}
|
||||||
|
</div>
|
||||||
|
{select(
|
||||||
|
'newRowPosition',
|
||||||
|
'::App.ListFormEdit.EditingNewRowPosition',
|
||||||
|
editingOptionNewRowPositionOptions,
|
||||||
|
)}
|
||||||
|
{text('addPageUrl', '::App.ListFormEdit.EditingAddPageUrl')}
|
||||||
|
{text('addDialogName', '::App.ListFormEdit.EditingAddDialogName')}
|
||||||
|
{text(
|
||||||
|
'addDialogParameters',
|
||||||
|
'::App.ListFormEdit.EditingAddDialogParameters',
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
<Card className="my-2" header={translate('::App.Platform.Edit')}>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
{checkbox('allowUpdating', '::App.ListFormEdit.AllowUpdating')}
|
||||||
|
{checkbox('allowDetail', '::App.ListFormEdit.EditingAllowDetail')}
|
||||||
|
{checkbox(
|
||||||
|
'sendOnlyChangedFormValuesUpdate',
|
||||||
'::App.ListFormEdit.SendOnlyChangedFormValuesUpdate',
|
'::App.ListFormEdit.SendOnlyChangedFormValuesUpdate',
|
||||||
)}
|
)}
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.sendOnlyChangedFormValuesUpdate &&
|
|
||||||
touched.editingOptionDto?.sendOnlyChangedFormValuesUpdate
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.sendOnlyChangedFormValuesUpdate}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.sendOnlyChangedFormValuesUpdate"
|
|
||||||
placeholder={translate(
|
|
||||||
'::App.ListFormEdit.SendOnlyChangedFormValuesUpdate',
|
|
||||||
)}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingAllowDetail')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.allowDetail &&
|
|
||||||
touched.editingOptionDto?.allowDetail
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.allowDetail}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.allowDetail"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAllowDetail')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingAllowDuplicate')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.allowDuplicate &&
|
|
||||||
touched.editingOptionDto?.allowDuplicate
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.allowDuplicate}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.allowDuplicate"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAllowDuplicate')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingConfirmDelete')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.confirmDelete &&
|
|
||||||
touched.editingOptionDto?.confirmDelete
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.confirmDelete}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.confirmDelete"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingConfirmDelete')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingAllowDeleting')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.allowDeleting &&
|
|
||||||
touched.editingOptionDto?.allowDeleting
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.allowDeleting}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.allowDeleting"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAllowDeleting')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingAllowDeleteAll')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.allowAllDeleting &&
|
|
||||||
touched.editingOptionDto?.allowAllDeleting
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.allowAllDeleting}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.allowAllDeleting"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingAllowDeleteAll')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
</Card>
|
||||||
<FormItem
|
<Card className="my-2" header={translate('::App.Platform.Delete')}>
|
||||||
label={translate('::App.ListFormEdit.EditingUseIcons')}
|
<div className="grid grid-cols-3 gap-3">
|
||||||
invalid={
|
{checkbox('allowDeleting', '::App.ListFormEdit.EditingAllowDeleting')}
|
||||||
errors.editingOptionDto?.useIcons && touched.editingOptionDto?.useIcons
|
{checkbox('confirmDelete', '::App.ListFormEdit.EditingConfirmDelete')}
|
||||||
}
|
{checkbox('allowAllDeleting', '::App.ListFormEdit.EditingAllowDeleteAll')}
|
||||||
errorMessage={errors.editingOptionDto?.useIcons}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.useIcons"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingUseIcons')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingSelectTextOnEditStart')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.selectTextOnEditStart &&
|
|
||||||
touched.editingOptionDto?.selectTextOnEditStart
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.selectTextOnEditStart}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.selectTextOnEditStart"
|
|
||||||
placeholder={translate(
|
|
||||||
'::App.ListFormEdit.EditingSelectTextOnEditStart',
|
|
||||||
)}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</div>
|
</div>
|
||||||
<FormItem
|
</Card>
|
||||||
label={translate('::App.ListFormEdit.EditingMode')}
|
|
||||||
invalid={errors.editingOptionDto?.mode && touched.editingOptionDto?.mode}
|
|
||||||
errorMessage={errors.editingOptionDto?.mode}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.mode"
|
|
||||||
placeholder={translate('::App.Platform.Mode')}
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<GridsEditMode>) => (
|
|
||||||
<Select
|
|
||||||
field={field}
|
|
||||||
form={form}
|
|
||||||
isClearable={true}
|
|
||||||
options={editingOptionModeOptions}
|
|
||||||
value={editingOptionModeOptions?.filter(
|
|
||||||
(option) => option.value === values.editingOptionDto.mode,
|
|
||||||
)}
|
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingRefreshMode')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.refreshMode &&
|
|
||||||
touched.editingOptionDto?.refreshMode
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.refreshMode}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.refreshMode"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingRefreshMode')}
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<GridsEditRefreshMode>) => (
|
|
||||||
<Select
|
|
||||||
field={field}
|
|
||||||
form={form}
|
|
||||||
isClearable={true}
|
|
||||||
options={editingOptionRefreshModeOptions}
|
|
||||||
value={editingOptionRefreshModeOptions?.filter(
|
|
||||||
(option) => option.value === values.editingOptionDto.refreshMode,
|
|
||||||
)}
|
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingNewRowPosition')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.newRowPosition &&
|
|
||||||
touched.editingOptionDto?.newRowPosition
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.newRowPosition}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.newRowPosition"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingNewRowPosition')}
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<NewRowPosition>) => (
|
|
||||||
<Select
|
|
||||||
field={field}
|
|
||||||
form={form}
|
|
||||||
isClearable={true}
|
|
||||||
options={editingOptionNewRowPositionOptions}
|
|
||||||
value={editingOptionNewRowPositionOptions?.filter(
|
|
||||||
(option) => option.value === values.editingOptionDto.newRowPosition,
|
|
||||||
)}
|
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingStartEditAction')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.startEditAction &&
|
|
||||||
touched.editingOptionDto?.startEditAction
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.startEditAction}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.startEditAction"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingStartEditAction')}
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<StartEditAction>) => (
|
|
||||||
<Select
|
|
||||||
field={field}
|
|
||||||
form={form}
|
|
||||||
isClearable={true}
|
|
||||||
options={editingOptionStartEditActionOptions}
|
|
||||||
value={editingOptionStartEditActionOptions?.filter(
|
|
||||||
(option) =>
|
|
||||||
option.value === values.editingOptionDto.startEditAction,
|
|
||||||
)}
|
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingEditColumnName')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.editColumnName &&
|
|
||||||
touched.editingOptionDto?.editColumnName
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.editColumnName}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.editColumnName"
|
|
||||||
placeholder={translate('::App.ListFormEdit.EditingEditColumnName')}
|
|
||||||
component={Input}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
<Card className="my-2" header="Popup">
|
||||||
<Card className="my-2" header="Popup">
|
{values.editingOptionDto.mode === 'popup' && (
|
||||||
{values.editingOptionDto.mode === 'popup' && (
|
<>
|
||||||
<>
|
{checkbox('popup.showTitle', '::App.ListFormEdit.EditingShowTitle')}
|
||||||
<FormItem
|
{text('popup.title', '::App.Listform.ListformField.Title')}
|
||||||
label={translate('::App.ListFormEdit.EditingShowTitle')}
|
{number('popup.width', '::App.ListFormEdit.DetailsWidth')}
|
||||||
invalid={
|
{number('popup.height', '::App.ListFormEdit.DetailsHeight')}
|
||||||
errors.editingOptionDto?.popup?.showTitle &&
|
{checkbox('popup.fullScreen', '::App.Platform.Fullscreen')}
|
||||||
touched.editingOptionDto?.popup?.showTitle
|
{checkbox('popup.resizeEnabled', '::App.ListFormEdit.ResizeEnabled')}
|
||||||
}
|
{checkbox(
|
||||||
errorMessage={errors.editingOptionDto?.popup?.showTitle}
|
'popup.hideOnOutsideClick',
|
||||||
>
|
'::App.ListFormEdit.EditingHideOnOutsideClick',
|
||||||
<Field
|
)}
|
||||||
name="editingOptionDto.popup.showTitle"
|
</>
|
||||||
placeholder={translate('::App.ListFormEdit.EditingShowTitle')}
|
)}
|
||||||
component={Checkbox}
|
</Card>
|
||||||
/>
|
</div>
|
||||||
</FormItem>
|
<Button block size="sm" variant="solid" loading={isSubmitting}>
|
||||||
<FormItem
|
{isSubmitting
|
||||||
label={translate('::App.Listform.ListformField.Title')}
|
? translate('::App.Platform.Saving')
|
||||||
invalid={
|
: translate('::App.Platform.Save')}
|
||||||
errors.editingOptionDto?.popup?.title &&
|
</Button>
|
||||||
touched.editingOptionDto?.popup?.title
|
</FormContainer>
|
||||||
}
|
</Form>
|
||||||
errorMessage={errors.editingOptionDto?.popup?.title}
|
)
|
||||||
>
|
}}
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.popup.title"
|
|
||||||
placeholder={translate('::App.Listform.ListformField.Title')}
|
|
||||||
component={Input}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.DetailsWidth')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.popup?.width &&
|
|
||||||
touched.editingOptionDto?.popup?.width
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.popup?.width}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
className="w-20"
|
|
||||||
type="number"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.popup.width"
|
|
||||||
placeholder={translate('::App.ListFormEdit.DetailsWidth')}
|
|
||||||
component={Input}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.DetailsHeight')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.popup?.height &&
|
|
||||||
touched.editingOptionDto?.popup?.height
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.popup?.height}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
className="w-20"
|
|
||||||
type="number"
|
|
||||||
autoComplete="off"
|
|
||||||
name="editingOptionDto.popup.height"
|
|
||||||
placeholder={translate('::App.ListFormEdit.DetailsHeight')}
|
|
||||||
component={Input}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.Platform.Fullscreen')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.popup?.fullScreen &&
|
|
||||||
touched.editingOptionDto?.popup?.fullScreen
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.popup?.fullScreen}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.popup.fullScreen"
|
|
||||||
placeholder={translate('::App.Platform.Fullscreen')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.ResizeEnabled')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.popup?.resizeEnabled &&
|
|
||||||
touched.editingOptionDto?.popup?.resizeEnabled
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.popup?.resizeEnabled}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.popup.resizeEnabled"
|
|
||||||
placeholder={translate('::App.ListFormEdit.ResizeEnabled')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.ListFormEdit.EditingHideOnOutsideClick')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.popup?.hideOnOutsideClick &&
|
|
||||||
touched.editingOptionDto?.popup?.hideOnOutsideClick
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.popup?.hideOnOutsideClick}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.popup.hideOnOutsideClick"
|
|
||||||
placeholder={translate(
|
|
||||||
'::App.ListFormEdit.EditingHideOnOutsideClick',
|
|
||||||
)}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<Button block size="sm" variant="solid" loading={isSubmitting}>
|
|
||||||
{isSubmitting ? translate('::App.Platform.Saving') : translate('::App.Platform.Save')}
|
|
||||||
</Button>
|
|
||||||
</FormContainer>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</Formik>
|
</Formik>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -513,6 +513,7 @@ export function FormTabWorkflow(
|
||||||
dragPreview={dragPreview}
|
dragPreview={dragPreview}
|
||||||
pendingLink={pendingLink}
|
pendingLink={pendingLink}
|
||||||
selectedCriteriaId={selectedId}
|
selectedCriteriaId={selectedId}
|
||||||
|
dataSourceCode={listValues.dataSourceCode ?? ''}
|
||||||
userList={props.userList}
|
userList={props.userList}
|
||||||
selectCommandColumns={props.selectCommandColumns}
|
selectCommandColumns={props.selectCommandColumns}
|
||||||
isLoadingSelectCommandColumns={props.isLoadingSelectCommandColumns}
|
isLoadingSelectCommandColumns={props.isLoadingSelectCommandColumns}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ const initialValues: ListFormWizardDto = {
|
||||||
allowDeleting: true,
|
allowDeleting: true,
|
||||||
confirmDelete: true,
|
confirmDelete: true,
|
||||||
allowDetail: false,
|
allowDetail: false,
|
||||||
|
openDetailAfterInsert: false,
|
||||||
seedData: false,
|
seedData: false,
|
||||||
seedSync: false,
|
seedSync: false,
|
||||||
defaultLayout: 'grid',
|
defaultLayout: 'grid',
|
||||||
|
|
@ -717,6 +718,7 @@ const Wizard = () => {
|
||||||
allowDeleting: w.allowDeleting ?? true,
|
allowDeleting: w.allowDeleting ?? true,
|
||||||
confirmDelete: w.confirmDelete ?? true,
|
confirmDelete: w.confirmDelete ?? true,
|
||||||
allowDetail: w.allowDetail ?? false,
|
allowDetail: w.allowDetail ?? false,
|
||||||
|
openDetailAfterInsert: w.openDetailAfterInsert ?? false,
|
||||||
seedData: w.seedData ?? false,
|
seedData: w.seedData ?? false,
|
||||||
seedSync: w.seedSync ?? false,
|
seedSync: w.seedSync ?? false,
|
||||||
defaultLayout: w.defaultLayout ?? 'grid',
|
defaultLayout: w.defaultLayout ?? 'grid',
|
||||||
|
|
@ -1455,6 +1457,7 @@ const Wizard = () => {
|
||||||
{resolvedStep === STEP.workflow && (
|
{resolvedStep === STEP.workflow && (
|
||||||
<WizardStepWorkflow
|
<WizardStepWorkflow
|
||||||
listFormCode={values.listFormCode}
|
listFormCode={values.listFormCode}
|
||||||
|
dataSourceCode={values.dataSourceCode}
|
||||||
workflow={workflow}
|
workflow={workflow}
|
||||||
criteria={criteria}
|
criteria={criteria}
|
||||||
selectCommandColumns={selectCommandColumns}
|
selectCommandColumns={selectCommandColumns}
|
||||||
|
|
|
||||||
|
|
@ -502,6 +502,19 @@ const WizardStepDataSettings = ({
|
||||||
>
|
>
|
||||||
<Field type="checkbox" autoComplete="off" name="allowDetail" component={Checkbox} />
|
<Field type="checkbox" autoComplete="off" name="allowDetail" component={Checkbox} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem
|
||||||
|
label={translate('::App.ListFormEdit.EditingOpenDetailAfterInsert')}
|
||||||
|
invalid={!!(errors.openDetailAfterInsert && touched.openDetailAfterInsert)}
|
||||||
|
errorMessage={errors.openDetailAfterInsert}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
type="checkbox"
|
||||||
|
autoComplete="off"
|
||||||
|
name="openDetailAfterInsert"
|
||||||
|
component={Checkbox}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { IdentityUserDto } from '@/proxy/admin/models'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
dataSourceCode: string
|
||||||
workflow: WorkflowDto
|
workflow: WorkflowDto
|
||||||
criteria: ListFormWorkflowCriteriaDto[]
|
criteria: ListFormWorkflowCriteriaDto[]
|
||||||
selectCommandColumns: DatabaseColumnDto[]
|
selectCommandColumns: DatabaseColumnDto[]
|
||||||
|
|
@ -106,6 +107,7 @@ const toWizardCriteria = (items: WorkflowCriteriaDto[]): ListFormWorkflowCriteri
|
||||||
*/
|
*/
|
||||||
function WizardStepWorkflow({
|
function WizardStepWorkflow({
|
||||||
listFormCode,
|
listFormCode,
|
||||||
|
dataSourceCode,
|
||||||
workflow,
|
workflow,
|
||||||
criteria,
|
criteria,
|
||||||
selectCommandColumns,
|
selectCommandColumns,
|
||||||
|
|
@ -521,6 +523,7 @@ function WizardStepWorkflow({
|
||||||
dragPreview={dragPreview}
|
dragPreview={dragPreview}
|
||||||
pendingLink={pendingLink}
|
pendingLink={pendingLink}
|
||||||
selectedCriteriaId={selectedId}
|
selectedCriteriaId={selectedId}
|
||||||
|
dataSourceCode={dataSourceCode}
|
||||||
userList={userList}
|
userList={userList}
|
||||||
selectCommandColumns={selectCommandColumns}
|
selectCommandColumns={selectCommandColumns}
|
||||||
isLoadingSelectCommandColumns={false}
|
isLoadingSelectCommandColumns={false}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
import React, { useMemo } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { FaEdit, FaTrash } from 'react-icons/fa'
|
import { FaEdit, FaTrash } from 'react-icons/fa'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { Button, Dialog, FormContainer, FormItem, Input, Select, Table } from '@/components/ui'
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Dialog,
|
||||||
|
FormContainer,
|
||||||
|
FormItem,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
} from '@/components/ui'
|
||||||
import TBody from '@/components/ui/Table/TBody'
|
import TBody from '@/components/ui/Table/TBody'
|
||||||
import THead from '@/components/ui/Table/THead'
|
import THead from '@/components/ui/Table/THead'
|
||||||
import Td from '@/components/ui/Table/Td'
|
import Td from '@/components/ui/Table/Td'
|
||||||
|
|
@ -15,19 +24,26 @@ import {
|
||||||
splitWorkflowApprovers,
|
splitWorkflowApprovers,
|
||||||
targetTitle,
|
targetTitle,
|
||||||
} from '@/utils/workflow/workflowHelpers'
|
} from '@/utils/workflow/workflowHelpers'
|
||||||
import type { CompareOutcomeDto, WorkflowCriteriaDto } from '@/services/workflow.service'
|
import type {
|
||||||
|
CompareOutcomeDto,
|
||||||
|
WorkflowCriteriaDto,
|
||||||
|
WorkflowRejectReasonDto,
|
||||||
|
} from '@/services/workflow.service'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
||||||
type TranslateFn = (key: string, params?: Record<string, string | number>) => string
|
import type { DatabaseColumnDto, DatabaseTableDto } from '@/proxy/sql-query-manager/models'
|
||||||
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
|
||||||
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||||
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
|
|
||||||
|
type TranslateFn = (key: string, params?: Record<string, string | number>) => string
|
||||||
|
|
||||||
type WorkflowCriteriaProps = {
|
type WorkflowCriteriaProps = {
|
||||||
criteria: WorkflowCriteriaDto[]
|
criteria: WorkflowCriteriaDto[]
|
||||||
selectedId: string
|
selectedId: string
|
||||||
formValues: any
|
formValues: any
|
||||||
busy: boolean
|
busy: boolean
|
||||||
|
dataSourceCode: string
|
||||||
userList: SelectBoxOption[]
|
userList: SelectBoxOption[]
|
||||||
selectCommandColumns: DatabaseColumnDto[]
|
selectCommandColumns: DatabaseColumnDto[]
|
||||||
isLoadingSelectCommandColumns: boolean
|
isLoadingSelectCommandColumns: boolean
|
||||||
|
|
@ -42,6 +58,7 @@ export function WorkflowCriteria({
|
||||||
selectedId,
|
selectedId,
|
||||||
formValues,
|
formValues,
|
||||||
busy,
|
busy,
|
||||||
|
dataSourceCode,
|
||||||
userList,
|
userList,
|
||||||
selectCommandColumns,
|
selectCommandColumns,
|
||||||
isLoadingSelectCommandColumns,
|
isLoadingSelectCommandColumns,
|
||||||
|
|
@ -331,6 +348,15 @@ export function WorkflowCriteria({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{formValues.kind === 'Approval' && (
|
||||||
|
<RejectReasonFields
|
||||||
|
value={formValues.rejectReason}
|
||||||
|
dataSourceCode={dataSourceCode}
|
||||||
|
recordColumns={compareColumnOptions}
|
||||||
|
onChange={(value) => setField('rejectReason', value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{formValues.kind === 'Compare' && (
|
{formValues.kind === 'Compare' && (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<div className="mb-3 flex items-center justify-between gap-2">
|
<div className="mb-3 flex items-center justify-between gap-2">
|
||||||
|
|
@ -548,6 +574,126 @@ function SelectField({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emptyRejectReason: WorkflowRejectReasonDto = {
|
||||||
|
tableName: '',
|
||||||
|
valueField: '',
|
||||||
|
textField: '',
|
||||||
|
targetField: '',
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reddin listeden seçilen nedeni: tablo ve Id/metin kolonları ekranın veri kaynağından seçilir.
|
||||||
|
* Tablo boşsa karar diyaloğu yalnızca açıklama sorar.
|
||||||
|
*/
|
||||||
|
function RejectReasonFields({
|
||||||
|
value,
|
||||||
|
dataSourceCode,
|
||||||
|
recordColumns,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value?: WorkflowRejectReasonDto | null
|
||||||
|
dataSourceCode: string
|
||||||
|
recordColumns: SelectBoxOption[]
|
||||||
|
onChange: (value: WorkflowRejectReasonDto | null) => void
|
||||||
|
}) {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
const reason = value ?? emptyRejectReason
|
||||||
|
const [tables, setTables] = useState<DatabaseTableDto[]>([])
|
||||||
|
const [columns, setColumns] = useState<DatabaseColumnDto[]>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dataSourceCode) return
|
||||||
|
sqlObjectManagerService
|
||||||
|
.getAllObjects(dataSourceCode)
|
||||||
|
.then((res) => setTables(res.data?.tables ?? []))
|
||||||
|
.catch(() => setTables([]))
|
||||||
|
}, [dataSourceCode])
|
||||||
|
|
||||||
|
const selectedTable = tables.find((table) => table.tableName === reason.tableName)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dataSourceCode || !selectedTable) {
|
||||||
|
setColumns([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sqlObjectManagerService
|
||||||
|
.getTableColumns(dataSourceCode, selectedTable.schemaName, selectedTable.tableName)
|
||||||
|
.then((res) => setColumns(res.data ?? []))
|
||||||
|
.catch(() => setColumns([]))
|
||||||
|
}, [dataSourceCode, selectedTable])
|
||||||
|
|
||||||
|
const tableOptions = tables.map((table) => ({ value: table.tableName, label: table.tableName }))
|
||||||
|
const columnOptions = columns.map((column) => ({
|
||||||
|
value: column.columnName,
|
||||||
|
label: `${column.columnName} (${column.dataType})`,
|
||||||
|
}))
|
||||||
|
const update = (patch: Partial<WorkflowRejectReasonDto>) => onChange({ ...reason, ...patch })
|
||||||
|
const hasTable = Boolean(reason.tableName)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-4 rounded border border-gray-200 p-3 dark:border-gray-700">
|
||||||
|
<h6 className="mb-1">{translate('::App.ListFormWorkflow.RejectReasonSection')}</h6>
|
||||||
|
<p className="mb-3 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{translate('::App.ListFormWorkflow.RejectReasonHint')}
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 gap-1">
|
||||||
|
<FormItem label={translate('::App.ListFormWorkflow.RejectReasonTable')}>
|
||||||
|
<Select
|
||||||
|
isClearable
|
||||||
|
options={tableOptions}
|
||||||
|
value={tableOptions.find((option) => option.value === reason.tableName) ?? null}
|
||||||
|
onChange={(option: any) =>
|
||||||
|
option?.value
|
||||||
|
? update({ tableName: option.value, valueField: '', textField: '' })
|
||||||
|
: onChange(null)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label={translate('::App.ListFormWorkflow.RejectReasonTargetField')}>
|
||||||
|
<Select
|
||||||
|
isClearable
|
||||||
|
isDisabled={!hasTable}
|
||||||
|
options={recordColumns}
|
||||||
|
value={recordColumns.find((option) => option.value === reason.targetField) ?? null}
|
||||||
|
onChange={(option: any) => update({ targetField: option?.value ?? '' })}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
asterisk={hasTable}
|
||||||
|
label={translate('::App.ListFormWorkflow.RejectReasonValueField')}
|
||||||
|
>
|
||||||
|
<SelectField
|
||||||
|
required={hasTable}
|
||||||
|
options={columnOptions}
|
||||||
|
value={reason.valueField}
|
||||||
|
onChange={(valueField) => update({ valueField })}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
asterisk={hasTable}
|
||||||
|
label={translate('::App.ListFormWorkflow.RejectReasonTextField')}
|
||||||
|
>
|
||||||
|
<SelectField
|
||||||
|
required={hasTable}
|
||||||
|
options={columnOptions}
|
||||||
|
value={reason.textField}
|
||||||
|
onChange={(textField) => update({ textField })}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
|
<Checkbox
|
||||||
|
disabled={!hasTable}
|
||||||
|
checked={reason.required !== false}
|
||||||
|
onChange={(required: boolean) => update({ required })}
|
||||||
|
>
|
||||||
|
{translate('::App.ListFormWorkflow.RejectReasonIsRequired')}
|
||||||
|
</Checkbox>
|
||||||
|
</FormItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function MultiSelectField({
|
function MultiSelectField({
|
||||||
options,
|
options,
|
||||||
value,
|
value,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type WorkflowDesignerProps = {
|
||||||
dragPreview: any
|
dragPreview: any
|
||||||
pendingLink: any
|
pendingLink: any
|
||||||
selectedCriteriaId: string
|
selectedCriteriaId: string
|
||||||
|
dataSourceCode: string
|
||||||
userList: SelectBoxOption[]
|
userList: SelectBoxOption[]
|
||||||
selectCommandColumns: DatabaseColumnDto[]
|
selectCommandColumns: DatabaseColumnDto[]
|
||||||
isLoadingSelectCommandColumns: boolean
|
isLoadingSelectCommandColumns: boolean
|
||||||
|
|
@ -79,6 +80,7 @@ export function WorkflowDesigner({
|
||||||
dragPreview,
|
dragPreview,
|
||||||
pendingLink,
|
pendingLink,
|
||||||
selectedCriteriaId,
|
selectedCriteriaId,
|
||||||
|
dataSourceCode,
|
||||||
userList,
|
userList,
|
||||||
selectCommandColumns,
|
selectCommandColumns,
|
||||||
isLoadingSelectCommandColumns,
|
isLoadingSelectCommandColumns,
|
||||||
|
|
@ -153,6 +155,7 @@ export function WorkflowDesigner({
|
||||||
selectedId={selectedCriteriaId}
|
selectedId={selectedCriteriaId}
|
||||||
formValues={criteriaForm}
|
formValues={criteriaForm}
|
||||||
busy={busy}
|
busy={busy}
|
||||||
|
dataSourceCode={dataSourceCode}
|
||||||
userList={userList}
|
userList={userList}
|
||||||
selectCommandColumns={selectCommandColumns}
|
selectCommandColumns={selectCommandColumns}
|
||||||
isLoadingSelectCommandColumns={isLoadingSelectCommandColumns}
|
isLoadingSelectCommandColumns={isLoadingSelectCommandColumns}
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ const WORKFLOW_COLUMNS: ColumnDefinition[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '__UserId',
|
id: '__UserId',
|
||||||
columnName: 'UserName',
|
columnName: 'ApprovalUserName',
|
||||||
dataType: 'nvarchar',
|
dataType: 'nvarchar',
|
||||||
maxLength: '256',
|
maxLength: '256',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
|
|
@ -276,7 +276,7 @@ const WORKFLOW_COLUMNS: ColumnDefinition[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '__Status',
|
id: '__Status',
|
||||||
columnName: 'Status',
|
columnName: 'ApprovalStatus',
|
||||||
dataType: 'nvarchar',
|
dataType: 'nvarchar',
|
||||||
maxLength: '50',
|
maxLength: '50',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
|
|
@ -285,7 +285,7 @@ const WORKFLOW_COLUMNS: ColumnDefinition[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '__Date',
|
id: '__Date',
|
||||||
columnName: 'Date',
|
columnName: 'ApprovalDate',
|
||||||
dataType: 'datetime',
|
dataType: 'datetime',
|
||||||
maxLength: '',
|
maxLength: '',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
|
|
@ -294,7 +294,7 @@ const WORKFLOW_COLUMNS: ColumnDefinition[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '__Description',
|
id: '__Description',
|
||||||
columnName: 'Description',
|
columnName: 'ApprovalDescription',
|
||||||
dataType: 'nvarchar',
|
dataType: 'nvarchar',
|
||||||
maxLength: '200',
|
maxLength: '200',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { isWorkflowToolbarItemDisabled, useToolbar } from '../list/useToolbar'
|
||||||
import { orderListToolbarItems } from '../list/toolbarOrder'
|
import { orderListToolbarItems } from '../list/toolbarOrder'
|
||||||
import { PermissionResults, RowMode } from './types'
|
import { PermissionResults, RowMode } from './types'
|
||||||
import { GridDto } from '@/proxy/form/models'
|
import { GridDto } from '@/proxy/form/models'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { getFormRecordUrl, ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { usePWA } from '@/utils/hooks/usePWA'
|
import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
import { layoutTypes } from '../admin/listForm/edit/types'
|
import { layoutTypes } from '../admin/listForm/edit/types'
|
||||||
|
|
@ -268,11 +268,7 @@ const FormButtons = (props: {
|
||||||
if (onActionEdit) {
|
if (onActionEdit) {
|
||||||
onActionEdit()
|
onActionEdit()
|
||||||
} else {
|
} else {
|
||||||
navigate(
|
navigate(getFormRecordUrl(listFormCode, id, true))
|
||||||
ROUTES_ENUM.protected.admin.formEdit
|
|
||||||
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
|
||||||
.replace(':id', encodeURIComponent(id!)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
!permissions.u,
|
!permissions.u,
|
||||||
|
|
@ -289,11 +285,7 @@ const FormButtons = (props: {
|
||||||
if (onActionView) {
|
if (onActionView) {
|
||||||
onActionView()
|
onActionView()
|
||||||
} else {
|
} else {
|
||||||
navigate(
|
navigate(getFormRecordUrl(listFormCode, id))
|
||||||
ROUTES_ENUM.protected.admin.formView
|
|
||||||
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
|
||||||
.replace(':id', encodeURIComponent(id!)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
!permissions.r,
|
!permissions.r,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { GridColumnData } from '../list/GridColumnData'
|
||||||
import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
|
import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
|
||||||
import { GridDto } from '@/proxy/form/models'
|
import { GridDto } from '@/proxy/form/models'
|
||||||
import { getAccessDeniedPath } from '@/utils/routing'
|
import { getAccessDeniedPath } from '@/utils/routing'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { getFormRecordUrl } from '@/routes/route.constant'
|
||||||
import { useLookupDataSource } from './useLookupDataSource'
|
import { useLookupDataSource } from './useLookupDataSource'
|
||||||
import { layoutTypes } from '../admin/listForm/edit/types'
|
import { layoutTypes } from '../admin/listForm/edit/types'
|
||||||
import { useListFormCustomDataSource } from '../list/useListFormCustomDataSource'
|
import { useListFormCustomDataSource } from '../list/useListFormCustomDataSource'
|
||||||
|
|
@ -154,18 +154,10 @@ const useGridData = (props: {
|
||||||
if (newId === undefined || newId === null) {
|
if (newId === undefined || newId === null) {
|
||||||
throw new Error(translate('::App.ListForm.FormBilgileriKaydedilemedi'))
|
throw new Error(translate('::App.ListForm.FormBilgileriKaydedilemedi'))
|
||||||
}
|
}
|
||||||
finishSubmit(
|
finishSubmit(getFormRecordUrl(listFormCode, newId))
|
||||||
ROUTES_ENUM.protected.admin.formView
|
|
||||||
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
|
||||||
.replace(':id', encodeURIComponent(String(newId))),
|
|
||||||
)
|
|
||||||
} else if (mode === 'edit') {
|
} else if (mode === 'edit') {
|
||||||
await dataSource.update(id, buildUpdateData(formValues))
|
await dataSource.update(id, buildUpdateData(formValues))
|
||||||
finishSubmit(
|
finishSubmit(getFormRecordUrl(listFormCode, id))
|
||||||
ROUTES_ENUM.protected.admin.formView
|
|
||||||
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
|
||||||
.replace(':id', encodeURIComponent(id!)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(<Notification title={error.message} type="danger" />, {
|
toast.push(<Notification title={error.message} type="danger" />, {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,12 @@ import {
|
||||||
} from './shared/editingForm'
|
} from './shared/editingForm'
|
||||||
import { createEditingFormHandlers } from './shared/editingFormScripts'
|
import { createEditingFormHandlers } from './shared/editingFormScripts'
|
||||||
import { flattenColumns } from './shared/columns'
|
import { flattenColumns } from './shared/columns'
|
||||||
import { getInitialExtraFilters, mergeFilterTriplets, useListFormWidgets } from './shared/hooks'
|
import {
|
||||||
|
getInitialExtraFilters,
|
||||||
|
mergeFilterTriplets,
|
||||||
|
useListFormWidgets,
|
||||||
|
useOpenDetailAfterInsert,
|
||||||
|
} from './shared/hooks'
|
||||||
import { useFilters } from './useFilters'
|
import { useFilters } from './useFilters'
|
||||||
import { useListFormColumns } from './useListFormColumns'
|
import { useListFormColumns } from './useListFormColumns'
|
||||||
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
||||||
|
|
@ -162,7 +167,8 @@ const toCardColumn = (
|
||||||
showInColumnChooser: column.showInColumnChooser,
|
showInColumnChooser: column.showInColumnChooser,
|
||||||
sortIndex: column.sortIndex,
|
sortIndex: column.sortIndex,
|
||||||
sortOrder: column.sortOrder,
|
sortOrder: column.sortOrder,
|
||||||
validationRules: column.validationRules,
|
// CardView formu şablonlu editörlere de kural uygular; liste kolonundan düşürülen kural buradan gelir.
|
||||||
|
validationRules: column.colData?.validationRuleDto ?? column.validationRules,
|
||||||
visible: column.visible,
|
visible: column.visible,
|
||||||
visibleIndex: column.visibleIndex,
|
visibleIndex: column.visibleIndex,
|
||||||
}).filter(([, value]) => value !== undefined),
|
}).filter(([, value]) => value !== undefined),
|
||||||
|
|
@ -383,6 +389,7 @@ const CardView = (props: CardViewProps) => {
|
||||||
const [formData, setFormData] = useState<Record<string, any>>()
|
const [formData, setFormData] = useState<Record<string, any>>()
|
||||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||||
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
||||||
|
const openDetailAfterInsert = useOpenDetailAfterInsert(gridDto)
|
||||||
const formDataRef = useRef<Record<string, any> | undefined>(undefined)
|
const formDataRef = useRef<Record<string, any> | undefined>(undefined)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
formDataRef.current = formData
|
formDataRef.current = formData
|
||||||
|
|
@ -1215,6 +1222,7 @@ const CardView = (props: CardViewProps) => {
|
||||||
onCardInserted={(e) => {
|
onCardInserted={(e) => {
|
||||||
onCardChanged()
|
onCardChanged()
|
||||||
void refreshCardLookups(e.component, e.data)
|
void refreshCardLookups(e.component, e.data)
|
||||||
|
openDetailAfterInsert(e)
|
||||||
}}
|
}}
|
||||||
onCardUpdated={(e) => {
|
onCardUpdated={(e) => {
|
||||||
onCardChanged()
|
onCardChanged()
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,11 @@ import {
|
||||||
useListFormCustomSources,
|
useListFormCustomSources,
|
||||||
useListFormGridDto,
|
useListFormGridDto,
|
||||||
useListFormWidgets,
|
useListFormWidgets,
|
||||||
|
useOpenDetailAfterInsert,
|
||||||
useWidgetGroupHeight,
|
useWidgetGroupHeight,
|
||||||
} from './shared/hooks'
|
} from './shared/hooks'
|
||||||
import { startWorkflowForInsertedRow } from './shared/workflowRows'
|
import { startWorkflowForInsertedRow } from './shared/workflowRows'
|
||||||
|
import { cancelOnTemplateEditorErrors } from './shared/templateEditorValidation'
|
||||||
|
|
||||||
interface GridProps {
|
interface GridProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -130,6 +132,7 @@ const Grid = (props: GridProps) => {
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
||||||
|
const openDetailAfterInsert = useOpenDetailAfterInsert(gridDto)
|
||||||
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
||||||
useListFormCustomSources(gridDto)
|
useListFormCustomSources(gridDto)
|
||||||
|
|
||||||
|
|
@ -298,17 +301,20 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
const onRowInserting = useCallback(
|
const onRowInserting = useCallback(
|
||||||
(e: DataGridTypes.RowInsertingEvent<any, any>) => {
|
(e: DataGridTypes.RowInsertingEvent<any, any>) => {
|
||||||
|
if (cancelOnTemplateEditorErrors(e, gridDto, e.data, 'new', translate)) return
|
||||||
e.data = buildInsertPayload(gridDto, e.data)
|
e.data = buildInsertPayload(gridDto, e.data)
|
||||||
},
|
},
|
||||||
[gridDto],
|
[gridDto, translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onRowUpdating = useCallback(
|
const onRowUpdating = useCallback(
|
||||||
(e: DataGridTypes.RowUpdatingEvent<any, any>) => {
|
(e: DataGridTypes.RowUpdatingEvent<any, any>) => {
|
||||||
if (!gridDto?.columnFormats) return
|
if (!gridDto?.columnFormats) return
|
||||||
|
const data = { ...e.oldData, ...e.newData }
|
||||||
|
if (cancelOnTemplateEditorErrors(e, gridDto, data, 'edit', translate)) return
|
||||||
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
|
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
|
||||||
},
|
},
|
||||||
[gridDto],
|
[gridDto, translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onEditingStart = useCallback(
|
const onEditingStart = useCallback(
|
||||||
|
|
@ -596,6 +602,7 @@ const Grid = (props: GridProps) => {
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
void refreshWidgets()
|
void refreshWidgets()
|
||||||
void refreshLookupsForRow(e.component, e.data)
|
void refreshLookupsForRow(e.component, e.data)
|
||||||
|
openDetailAfterInsert(e)
|
||||||
}}
|
}}
|
||||||
onRowUpdated={(e) => {
|
onRowUpdated={(e) => {
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import {
|
||||||
useListFormCustomSources,
|
useListFormCustomSources,
|
||||||
useListFormGridDto,
|
useListFormGridDto,
|
||||||
useListFormWidgets,
|
useListFormWidgets,
|
||||||
|
useOpenDetailAfterInsert,
|
||||||
useWidgetGroupHeight,
|
useWidgetGroupHeight,
|
||||||
} from './shared/hooks'
|
} from './shared/hooks'
|
||||||
|
|
||||||
|
|
@ -79,6 +80,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
const layout = layoutTypes.scheduler || 'scheduler'
|
const layout = layoutTypes.scheduler || 'scheduler'
|
||||||
|
|
||||||
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
||||||
|
const openDetailAfterInsert = useOpenDetailAfterInsert(gridDto)
|
||||||
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
||||||
useListFormCustomSources(gridDto)
|
useListFormCustomSources(gridDto)
|
||||||
|
|
||||||
|
|
@ -832,7 +834,10 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
onAppointmentDeleting={() => {
|
onAppointmentDeleting={() => {
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
}}
|
}}
|
||||||
onAppointmentAdded={() => void refreshWidgets()}
|
onAppointmentAdded={(e) => {
|
||||||
|
void refreshWidgets()
|
||||||
|
openDetailAfterInsert({ data: e.appointmentData })
|
||||||
|
}}
|
||||||
onAppointmentUpdated={() => void refreshWidgets()}
|
onAppointmentUpdated={() => void refreshWidgets()}
|
||||||
onAppointmentDeleted={() => void refreshWidgets()}
|
onAppointmentDeleted={() => void refreshWidgets()}
|
||||||
height={
|
height={
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,11 @@ import {
|
||||||
useListFormCustomSources,
|
useListFormCustomSources,
|
||||||
useListFormGridDto,
|
useListFormGridDto,
|
||||||
useListFormWidgets,
|
useListFormWidgets,
|
||||||
|
useOpenDetailAfterInsert,
|
||||||
useWidgetGroupHeight,
|
useWidgetGroupHeight,
|
||||||
} from './shared/hooks'
|
} from './shared/hooks'
|
||||||
import { startWorkflowForInsertedRow } from './shared/workflowRows'
|
import { startWorkflowForInsertedRow } from './shared/workflowRows'
|
||||||
|
import { cancelOnTemplateEditorErrors } from './shared/templateEditorValidation'
|
||||||
|
|
||||||
interface TreeProps {
|
interface TreeProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -121,6 +123,7 @@ const Tree = (props: TreeProps) => {
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
const { widgets, refreshWidgets } = useListFormWidgets(listFormCode, gridDto, isSubForm)
|
||||||
|
const openDetailAfterInsert = useOpenDetailAfterInsert(gridDto)
|
||||||
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, widgets)
|
||||||
useListFormCustomSources(gridDto)
|
useListFormCustomSources(gridDto)
|
||||||
|
|
||||||
|
|
@ -309,17 +312,20 @@ const Tree = (props: TreeProps) => {
|
||||||
|
|
||||||
const onRowInserting = useCallback(
|
const onRowInserting = useCallback(
|
||||||
(e: TreeListTypes.RowInsertingEvent) => {
|
(e: TreeListTypes.RowInsertingEvent) => {
|
||||||
|
if (cancelOnTemplateEditorErrors(e, gridDto, e.data, 'new', translate)) return
|
||||||
e.data = buildInsertPayload(gridDto, e.data)
|
e.data = buildInsertPayload(gridDto, e.data)
|
||||||
},
|
},
|
||||||
[gridDto],
|
[gridDto, translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onRowUpdating = useCallback(
|
const onRowUpdating = useCallback(
|
||||||
(e: TreeListTypes.RowUpdatingEvent) => {
|
(e: TreeListTypes.RowUpdatingEvent) => {
|
||||||
if (!gridDto?.columnFormats) return
|
if (!gridDto?.columnFormats) return
|
||||||
|
const data = { ...e.oldData, ...e.newData }
|
||||||
|
if (cancelOnTemplateEditorErrors(e, gridDto, data, 'edit', translate)) return
|
||||||
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
|
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
|
||||||
},
|
},
|
||||||
[gridDto],
|
[gridDto, translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onEditingStart = useCallback(
|
const onEditingStart = useCallback(
|
||||||
|
|
@ -582,6 +588,7 @@ const Tree = (props: TreeProps) => {
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
void refreshWidgets()
|
void refreshWidgets()
|
||||||
void refreshLookupsForRow(e.component, e.data)
|
void refreshLookupsForRow(e.component, e.data)
|
||||||
|
openDetailAfterInsert(e)
|
||||||
}}
|
}}
|
||||||
onRowUpdated={(e) => {
|
onRowUpdated={(e) => {
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
import { captionize } from 'devextreme/core/utils/inflector'
|
import { captionize } from 'devextreme/core/utils/inflector'
|
||||||
import type { DataType } from 'devextreme/common'
|
import type { DataType } from 'devextreme/common'
|
||||||
|
import { formatMessage } from 'devextreme/localization'
|
||||||
import {
|
import {
|
||||||
ColumnFormatDto,
|
ColumnFormatDto,
|
||||||
DbTypeEnum,
|
DbTypeEnum,
|
||||||
|
|
@ -465,6 +466,55 @@ const getDateEditorDefaults = (sourceDbType?: DbTypeEnum) => {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grid/Tree'de `editCellTemplate` ile çizilen platform editörleri: lookup'lı TagBox/GridBox,
|
||||||
|
* lookup'sız ImageUpload/Rating. DevExtreme bu kolonlara `validationRules` uygulamaz (E1050).
|
||||||
|
*/
|
||||||
|
const hasCellEditTemplate = (colFormat: ColumnFormatDto) =>
|
||||||
|
colFormat.lookupDto?.dataSourceType
|
||||||
|
? colFormat.editorType2 === PlatformEditorTypes.dxTagBox ||
|
||||||
|
colFormat.editorType2 === PlatformEditorTypes.dxGridBox
|
||||||
|
: colFormat.editorType2 === PlatformEditorTypes.dxImageUpload ||
|
||||||
|
colFormat.editorType2 === PlatformEditorTypes.dxRating
|
||||||
|
|
||||||
|
const isEmptyValue = (value: unknown) =>
|
||||||
|
value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
(typeof value === 'string' && value.trim() === '') ||
|
||||||
|
(Array.isArray(value) && value.length === 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Şablonlu editörlerin `required` kuralı DevExtreme'de çalışmadığı için kayıttan önce burada
|
||||||
|
* denetlenir; formda görünen boş zorunlu alanların hata metinleri döner.
|
||||||
|
*/
|
||||||
|
export const getTemplateEditorRequiredErrors = (
|
||||||
|
gridDto: GridDto | undefined,
|
||||||
|
data: Record<string, any>,
|
||||||
|
mode: Extract<RowMode, 'new' | 'edit'>,
|
||||||
|
translate: (key: string) => string,
|
||||||
|
) =>
|
||||||
|
(gridDto?.columnFormats ?? [])
|
||||||
|
.filter(
|
||||||
|
(colFormat) =>
|
||||||
|
colFormat.editOrderNo != null &&
|
||||||
|
hasCellEditTemplate(colFormat) &&
|
||||||
|
(mode === 'new'
|
||||||
|
? colFormat.canCreate && colFormat.allowAdding !== false
|
||||||
|
: colFormat.canUpdate && colFormat.allowEditing !== false),
|
||||||
|
)
|
||||||
|
.flatMap((colFormat) => {
|
||||||
|
const rule = colFormat.validationRuleDto?.find((item) => item.type === 'required')
|
||||||
|
if (!rule || !isEmptyValue(getValueByField(data, colFormat.fieldName))) return []
|
||||||
|
const caption = colFormat.captionName
|
||||||
|
? translate('::' + colFormat.captionName)
|
||||||
|
: colFormat.fieldName
|
||||||
|
return [
|
||||||
|
rule.message
|
||||||
|
? translate('::' + rule.message)
|
||||||
|
: formatMessage('validation-required-formatted', caption),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
/** PlatformEditorTypes -> DevExtreme editorType eşlemesi. */
|
/** PlatformEditorTypes -> DevExtreme editorType eşlemesi. */
|
||||||
export const resolveEditorType = (editorType2?: EditorType2) => {
|
export const resolveEditorType = (editorType2?: EditorType2) => {
|
||||||
if (editorType2 === PlatformEditorTypes.dxGridBox) return 'dxDropDownBox'
|
if (editorType2 === PlatformEditorTypes.dxGridBox) return 'dxDropDownBox'
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@
|
||||||
*/
|
*/
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import type { RefObject } from 'react'
|
import type { RefObject } from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
import type { GridDto } from '@/proxy/form/models'
|
import type { GridDto } from '@/proxy/form/models'
|
||||||
|
import { getFormRecordUrl } from '@/routes/route.constant'
|
||||||
import { getList } from '@/services/form.service'
|
import { getList } from '@/services/form.service'
|
||||||
|
import { getPersistedInsertedKey } from './editingForm'
|
||||||
import {
|
import {
|
||||||
addCss,
|
addCss,
|
||||||
addJs,
|
addJs,
|
||||||
|
|
@ -89,6 +92,28 @@ export const useListFormWidgets = (
|
||||||
return { widgets, refreshWidgets }
|
return { widgets, refreshWidgets }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `EditingOptionDto.OpenDetailAfterInsert` açıksa popup'tan eklenen kaydın detay formuna geçer.
|
||||||
|
* Aynı sekmede açılır: kayıt sonrası asenkron `window.open` kullanıcı hareketi sayılmadığı için
|
||||||
|
* tarayıcının açılır pencere engeline takılırdı.
|
||||||
|
*/
|
||||||
|
export const useOpenDetailAfterInsert = (gridDto?: GridDto) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const enabled = !!gridDto?.gridOptions.editingOptionDto?.openDetailAfterInsert
|
||||||
|
const listFormCode = gridDto?.gridOptions.listFormCode
|
||||||
|
const keyFieldName = gridDto?.gridOptions.keyFieldName
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
(event: any) => {
|
||||||
|
if (!enabled || !listFormCode) return
|
||||||
|
const key = getPersistedInsertedKey(event, keyFieldName)
|
||||||
|
if (key === undefined) return
|
||||||
|
navigate(getFormRecordUrl(listFormCode, key))
|
||||||
|
},
|
||||||
|
[enabled, listFormCode, keyFieldName, navigate],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WidgetGroup alanının yüksekliğini izler; `fullHeight` grid'lerinin
|
* WidgetGroup alanının yüksekliğini izler; `fullHeight` grid'lerinin
|
||||||
* `calc(100vh - …)` hesabında kullanılır.
|
* `calc(100vh - …)` hesabında kullanılır.
|
||||||
|
|
|
||||||
29
ui/src/views/list/shared/templateEditorValidation.tsx
Normal file
29
ui/src/views/list/shared/templateEditorValidation.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Grid ve Tree `onRowInserting` / `onRowUpdating` içinde şablonlu editörlerin zorunlu alan kontrolü.
|
||||||
|
*/
|
||||||
|
import { Notification, toast } from '@/components/ui'
|
||||||
|
import type { GridDto } from '@/proxy/form/models'
|
||||||
|
import { getTemplateEditorRequiredErrors } from './editingForm'
|
||||||
|
|
||||||
|
/** Boş zorunlu şablon alanı varsa kaydı iptal eder (popup açık kalır) ve hataları gösterir. */
|
||||||
|
export const cancelOnTemplateEditorErrors = (
|
||||||
|
event: { cancel?: unknown },
|
||||||
|
gridDto: GridDto | undefined,
|
||||||
|
data: Record<string, any>,
|
||||||
|
mode: 'new' | 'edit',
|
||||||
|
translate: (key: string) => string,
|
||||||
|
) => {
|
||||||
|
const errors = getTemplateEditorRequiredErrors(gridDto, data, mode, translate)
|
||||||
|
if (!errors.length) return false
|
||||||
|
|
||||||
|
event.cancel = true
|
||||||
|
toast.push(
|
||||||
|
<Notification type="danger" duration={4000}>
|
||||||
|
{errors.map((error) => (
|
||||||
|
<div key={error}>{error}</div>
|
||||||
|
))}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'bottom-end' },
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import { useCallback, useEffect } from 'react'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { usePWA } from '@/utils/hooks/usePWA'
|
import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
|
import { getFormRecordUrl } from '@/routes/route.constant'
|
||||||
import { useDialogContext } from '../shared/DialogContext'
|
import { useDialogContext } from '../shared/DialogContext'
|
||||||
import { dynamicFetch } from '@/services/form.service'
|
import { dynamicFetch } from '@/services/form.service'
|
||||||
import { GridColumnData } from './GridColumnData'
|
import { GridColumnData } from './GridColumnData'
|
||||||
|
|
@ -454,10 +455,10 @@ const useListFormColumns = ({
|
||||||
if (!gridDto.gridOptions.keyFieldName) return
|
if (!gridDto.gridOptions.keyFieldName) return
|
||||||
if (!gridDto.gridOptions.listFormCode) return
|
if (!gridDto.gridOptions.listFormCode) return
|
||||||
|
|
||||||
const detailFormName = gridDto.gridOptions.listFormCode
|
const url = getFormRecordUrl(
|
||||||
const id = e.row.data[gridDto.gridOptions.keyFieldName]
|
gridDto.gridOptions.listFormCode,
|
||||||
|
e.row.data[gridDto.gridOptions.keyFieldName],
|
||||||
const url = `/admin/form/${encodeURIComponent(detailFormName)}/${encodeURIComponent(String(id))}`
|
)
|
||||||
|
|
||||||
window.open(url, isPwaMode ? '_self' : '_blank')
|
window.open(url, isPwaMode ? '_self' : '_blank')
|
||||||
},
|
},
|
||||||
|
|
@ -829,8 +830,9 @@ const useListFormColumns = ({
|
||||||
column.alignment = 'left'
|
column.alignment = 'left'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colData.validationRuleDto) {
|
// Şablonlu editörde DevExtreme kuralı uygulamaz (E1050); zorunluluk kayıttan önce
|
||||||
// for server side validation : https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/DataValidation/jQuery/Light/
|
// `cancelOnTemplateEditorErrors` ile denetlenir.
|
||||||
|
if (colData.validationRuleDto && !column.editCellTemplate) {
|
||||||
column.validationRules = colData.validationRuleDto as ValidationRule[]
|
column.validationRules = colData.validationRuleDto as ValidationRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button, Notification, toast } from '@/components/ui'
|
import { Button, Notification, Radio, Select, toast } from '@/components/ui'
|
||||||
import { GridDto, UiCommandButtonPositionTypeEnum, WorkflowDto } from '@/proxy/form/models'
|
import { GridDto, UiCommandButtonPositionTypeEnum, WorkflowDto } from '@/proxy/form/models'
|
||||||
import { dynamicFetch } from '@/services/form.service'
|
import { dynamicFetch } from '@/services/form.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -12,7 +12,11 @@ import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
import { layoutTypes, ListViewLayoutType } from '../admin/listForm/edit/types'
|
import { layoutTypes, ListViewLayoutType } from '../admin/listForm/edit/types'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import type { WorkflowRunResultDto } from '@/services/workflow.service'
|
import type {
|
||||||
|
WorkflowRejectReasonDto,
|
||||||
|
WorkflowRejectReasonItemDto,
|
||||||
|
WorkflowRunResultDto,
|
||||||
|
} from '@/services/workflow.service'
|
||||||
import { splitWorkflowApprovers, translateWorkflowLabel } from '@/utils/workflow/workflowHelpers'
|
import { splitWorkflowApprovers, translateWorkflowLabel } from '@/utils/workflow/workflowHelpers'
|
||||||
import { safeJsonParse } from './Utils'
|
import { safeJsonParse } from './Utils'
|
||||||
import { BarcodeScannerDialog } from '@/components/shared'
|
import { BarcodeScannerDialog } from '@/components/shared'
|
||||||
|
|
@ -232,7 +236,8 @@ const useToolbar = ({
|
||||||
// baslangic degeri de mevcut secimden hesaplanir.
|
// baslangic degeri de mevcut secimden hesaplanir.
|
||||||
const currentSelection = resolveSelectedRowsData(getSelectedRowsData)
|
const currentSelection = resolveSelectedRowsData(getSelectedRowsData)
|
||||||
const workflowDisabled = (itemName: string) =>
|
const workflowDisabled = (itemName: string) =>
|
||||||
isWorkflowToolbarItemDisabled(itemName, workflowOptions, currentSelection, currentUser) ?? true
|
isWorkflowToolbarItemDisabled(itemName, workflowOptions, currentSelection, currentUser) ??
|
||||||
|
true
|
||||||
if (
|
if (
|
||||||
workflowOptions?.approvalStatusFieldName &&
|
workflowOptions?.approvalStatusFieldName &&
|
||||||
approvalCriteria.length > 0 &&
|
approvalCriteria.length > 0 &&
|
||||||
|
|
@ -467,6 +472,7 @@ const useToolbar = ({
|
||||||
<>
|
<>
|
||||||
<WorkflowApprovalDecisionDialog
|
<WorkflowApprovalDecisionDialog
|
||||||
criteriaTitle={criteria.title}
|
criteriaTitle={criteria.title}
|
||||||
|
rejectReason={criteria.rejectReason}
|
||||||
keys={keys}
|
keys={keys}
|
||||||
listFormCode={listFormCode}
|
listFormCode={listFormCode}
|
||||||
criteriaId={criteria.id}
|
criteriaId={criteria.id}
|
||||||
|
|
@ -572,9 +578,7 @@ const useToolbar = ({
|
||||||
open: true,
|
open: true,
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<h5 className="mb-4">
|
<h5 className="mb-4">{translate('::App.ListForm.DeleteSelectedRecords')}</h5>
|
||||||
{translate('::App.ListForm.DeleteSelectedRecords')}
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{translate('::App.Platform.SeciliKayitlarSilmekIstiyormusunuz', {
|
{translate('::App.Platform.SeciliKayitlarSilmekIstiyormusunuz', {
|
||||||
|
|
@ -1040,9 +1044,7 @@ function WorkflowResetConfirmDialog({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5 className="mb-4">{translate('::App.ListForm.WorkflowReset')}</h5>
|
<h5 className="mb-4">{translate('::App.ListForm.WorkflowReset')}</h5>
|
||||||
<p className="mb-4">
|
<p className="mb-4">{translate('::App.ListForm.WorkflowResetMessage', { 0: keys.length })}</p>
|
||||||
{translate('::App.ListForm.WorkflowResetMessage', { 0: keys.length })}
|
|
||||||
</p>
|
|
||||||
<div className="text-right mt-6">
|
<div className="text-right mt-6">
|
||||||
<Button
|
<Button
|
||||||
className="ltr:mr-2 rtl:ml-2"
|
className="ltr:mr-2 rtl:ml-2"
|
||||||
|
|
@ -1062,6 +1064,7 @@ function WorkflowResetConfirmDialog({
|
||||||
|
|
||||||
function WorkflowApprovalDecisionDialog({
|
function WorkflowApprovalDecisionDialog({
|
||||||
criteriaTitle,
|
criteriaTitle,
|
||||||
|
rejectReason,
|
||||||
keys,
|
keys,
|
||||||
listFormCode,
|
listFormCode,
|
||||||
criteriaId,
|
criteriaId,
|
||||||
|
|
@ -1069,6 +1072,7 @@ function WorkflowApprovalDecisionDialog({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
}: {
|
}: {
|
||||||
criteriaTitle: string
|
criteriaTitle: string
|
||||||
|
rejectReason?: WorkflowRejectReasonDto | null
|
||||||
keys: unknown[]
|
keys: unknown[]
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
criteriaId: string
|
criteriaId: string
|
||||||
|
|
@ -1076,15 +1080,40 @@ function WorkflowApprovalDecisionDialog({
|
||||||
onCompleted: () => void
|
onCompleted: () => void
|
||||||
}) {
|
}) {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
const [decision, setDecision] = useState<'approve' | 'reject' | ''>('')
|
||||||
const [note, setNote] = useState('')
|
const [note, setNote] = useState('')
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
|
const [reasons, setReasons] = useState<WorkflowRejectReasonItemDto[]>([])
|
||||||
|
const [reasonId, setReasonId] = useState('')
|
||||||
|
const hasReasonList = Boolean(rejectReason?.tableName)
|
||||||
|
const reasonRequired = hasReasonList && rejectReason?.required !== false
|
||||||
|
const showReasons = decision === 'reject' && hasReasonList
|
||||||
|
const canSave = decision !== '' && !(decision === 'reject' && reasonRequired && !reasonId)
|
||||||
|
const reasonOptions = reasons.map((reason) => ({ value: reason.id, label: reason.text }))
|
||||||
|
|
||||||
const decide = async (approved: boolean) => {
|
useEffect(() => {
|
||||||
|
if (!hasReasonList) return
|
||||||
|
workflowService
|
||||||
|
.getRejectReasons(listFormCode, criteriaId)
|
||||||
|
.then(setReasons)
|
||||||
|
.catch(() => setReasons([]))
|
||||||
|
}, [criteriaId, hasReasonList, listFormCode])
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
if (!canSave) return
|
||||||
|
const approved = decision === 'approve'
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
try {
|
try {
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
keys.map((key) =>
|
keys.map((key) =>
|
||||||
workflowService.decideWorkflow(listFormCode, [key], approved, note, criteriaId),
|
workflowService.decideWorkflow(
|
||||||
|
listFormCode,
|
||||||
|
[key],
|
||||||
|
approved,
|
||||||
|
note,
|
||||||
|
criteriaId,
|
||||||
|
approved ? undefined : reasonId || undefined,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
showWorkflowToastMessages(results)
|
showWorkflowToastMessages(results)
|
||||||
|
|
@ -1112,11 +1141,30 @@ function WorkflowApprovalDecisionDialog({
|
||||||
0: keys.length,
|
0: keys.length,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
|
<Radio.Group className="mb-4" value={decision} onChange={(value) => setDecision(value)}>
|
||||||
|
<Radio value="approve">{translate('::App.Listform.ListformField.Approver')}</Radio>
|
||||||
|
<Radio value="reject">{translate('::App.Listform.ListformField.Rejecter')}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
{showReasons && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="mb-1 block text-sm font-semibold">
|
||||||
|
{translate('::App.WorkflowNote.RejectReason')}
|
||||||
|
{reasonRequired && <span className="ml-1 text-red-500">*</span>}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
isClearable
|
||||||
|
menuPortalTarget={document.body}
|
||||||
|
placeholder={translate('::App.ListFormWorkflow.RejectReasonPlaceholder')}
|
||||||
|
options={reasonOptions}
|
||||||
|
value={reasonOptions.find((option) => option.value === reasonId) ?? null}
|
||||||
|
onChange={(option: any) => setReasonId(option?.value ?? '')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<textarea
|
<textarea
|
||||||
className="input input-textarea mb-4 min-h-[96px] w-full resize-y"
|
className="input input-textarea mb-4 min-h-[96px] w-full resize-y"
|
||||||
rows={4}
|
rows={4}
|
||||||
value={note}
|
value={note}
|
||||||
autoFocus
|
|
||||||
placeholder={translate('::App.Listform.ListformField.ApprovalComment')}
|
placeholder={translate('::App.Listform.ListformField.ApprovalComment')}
|
||||||
onChange={(event) => setNote(event.target.value)}
|
onChange={(event) => setNote(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1129,11 +1177,18 @@ function WorkflowApprovalDecisionDialog({
|
||||||
>
|
>
|
||||||
{translate('::App.Platform.Cancel')}
|
{translate('::App.Platform.Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="ltr:mr-2 rtl:ml-2" disabled={submitting} onClick={() => decide(false)}>
|
<Button
|
||||||
{translate('::App.Listform.ListformField.Rejecter')}
|
variant="solid"
|
||||||
</Button>
|
loading={submitting}
|
||||||
<Button variant="solid" disabled={submitting} onClick={() => decide(true)}>
|
disabled={!canSave}
|
||||||
{translate('::App.Listform.ListformField.Approver')}
|
title={
|
||||||
|
decision === 'reject' && reasonRequired && !reasonId
|
||||||
|
? translate('::App.ListFormWorkflow.RejectReasonRequired')
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={save}
|
||||||
|
>
|
||||||
|
{translate('::App.Platform.Save')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue