Workflow ve WizardSeeder düzenlemeleri yapıldı.
This commit is contained in:
parent
d0cccde53f
commit
12f046f262
11 changed files with 416 additions and 84 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
public class WorkflowRunResultDto
|
public class WorkflowRunResultDto
|
||||||
|
|
@ -8,5 +10,6 @@ public class WorkflowRunResultDto
|
||||||
public string CurrentNodeKind { get; set; }
|
public string CurrentNodeKind { get; set; }
|
||||||
public bool WaitingApproval { get; set; }
|
public bool WaitingApproval { get; set; }
|
||||||
public bool Completed { get; set; }
|
public bool Completed { get; set; }
|
||||||
|
public List<string> ToastMessages { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
using Volo.Abp.PermissionManagement;
|
using Volo.Abp.PermissionManagement;
|
||||||
using Volo.Abp.Uow;
|
using Volo.Abp.Uow;
|
||||||
using static Sozsoft.Platform.PlatformConsts;
|
|
||||||
using System.Data;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Sozsoft.Languages;
|
using Sozsoft.Languages;
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
|
|
@ -97,11 +95,7 @@ public class ListFormWizardAppService(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permission'ları tek seferde kontrol et ve oluştur
|
// Permission'ları tek seferde kontrol et ve oluştur
|
||||||
var queryable = await repoPerm.GetQueryableAsync();
|
var existingPerms = await repoPerm.GetListAsync(a => a.GroupName == groupName);
|
||||||
var existingPerms = await AsyncExecuter.ToListAsync(
|
|
||||||
queryable.Where(a => a.GroupName == groupName)
|
|
||||||
);
|
|
||||||
|
|
||||||
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
|
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
|
||||||
if (permRead == null)
|
if (permRead == null)
|
||||||
{
|
{
|
||||||
|
|
@ -280,7 +274,7 @@ public class ListFormWizardAppService(
|
||||||
input.Workflow.Criteria = input.WorkflowCriteria;
|
input.Workflow.Criteria = input.WorkflowCriteria;
|
||||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
||||||
|
|
||||||
var listForm = await repoListForm.InsertAsync(new ListForm
|
await repoListForm.InsertAsync(new ListForm
|
||||||
{
|
{
|
||||||
ListFormType = ListFormTypeEnum.List,
|
ListFormType = ListFormTypeEnum.List,
|
||||||
PageSize = 10,
|
PageSize = 10,
|
||||||
|
|
@ -302,12 +296,12 @@ public class ListFormWizardAppService(
|
||||||
KeyFieldName = input.KeyFieldName,
|
KeyFieldName = input.KeyFieldName,
|
||||||
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
||||||
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
||||||
SortMode = GridOptions.SortModeSingle,
|
SortMode = PlatformConsts.GridOptions.SortModeSingle,
|
||||||
FilterRowJson = WizardConsts.DefaultFilterRowJson,
|
FilterRowJson = WizardConsts.DefaultFilterRowJson,
|
||||||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
|
||||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
|
||||||
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
||||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
|
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? PlatformConsts.GridOptions.SelectionModeSingle : PlatformConsts.GridOptions.SelectionModeNone),
|
||||||
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
||||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
||||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
||||||
|
|
@ -417,9 +411,7 @@ public class ListFormWizardAppService(
|
||||||
{
|
{
|
||||||
return workflow != null && (
|
return workflow != null && (
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalDateFieldName) ||
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalDescriptionFieldName) ||
|
|
||||||
criteria.Count > 0
|
criteria.Count > 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@ using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Sozsoft.Platform.Data.Seeds;
|
using Sozsoft.Platform.Data.Seeds;
|
||||||
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;
|
||||||
|
using Sozsoft.Platform.Localization;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
using Sozsoft.Sender.Mail;
|
using Sozsoft.Sender.Mail;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
|
|
@ -37,6 +39,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
private readonly IdentityUserManager identityUserManager;
|
private readonly IdentityUserManager identityUserManager;
|
||||||
private readonly ISozsoftEmailSender erpEmailSender;
|
private readonly ISozsoftEmailSender erpEmailSender;
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
private readonly IStringLocalizer<PlatformResource> localizer;
|
||||||
|
|
||||||
public ListFormWorkflowAppService(
|
public ListFormWorkflowAppService(
|
||||||
IRepository<ListFormWorkflow, string> criteriaRepository,
|
IRepository<ListFormWorkflow, string> criteriaRepository,
|
||||||
|
|
@ -47,7 +50,8 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
IQueryManager queryManager,
|
IQueryManager queryManager,
|
||||||
IdentityUserManager identityUserManager,
|
IdentityUserManager identityUserManager,
|
||||||
ISozsoftEmailSender erpEmailSender,
|
ISozsoftEmailSender erpEmailSender,
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider,
|
||||||
|
IStringLocalizer<PlatformResource> localizer)
|
||||||
{
|
{
|
||||||
this.criteriaRepository = criteriaRepository;
|
this.criteriaRepository = criteriaRepository;
|
||||||
this.noteRepository = noteRepository;
|
this.noteRepository = noteRepository;
|
||||||
|
|
@ -58,6 +62,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
this.identityUserManager = identityUserManager;
|
this.identityUserManager = identityUserManager;
|
||||||
this.erpEmailSender = erpEmailSender;
|
this.erpEmailSender = erpEmailSender;
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
|
this.localizer = localizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("criteria")]
|
[HttpGet("criteria")]
|
||||||
|
|
@ -303,6 +308,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
|
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
|
||||||
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
|
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
|
||||||
|
|
||||||
|
context.WorkflowNoteRows.Add(("Started By: ", ResolveCurrentUserDisplayName()));
|
||||||
var result = await RunUntilWaitAsync(context, start);
|
var result = await RunUntilWaitAsync(context, start);
|
||||||
await InsertWorkflowNoteAsync(
|
await InsertWorkflowNoteAsync(
|
||||||
context,
|
context,
|
||||||
|
|
@ -374,7 +380,8 @@ 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);
|
||||||
AddWorkflowDecisionRows(context, current, next, input.Approved, input.Note ?? string.Empty);
|
context.WorkflowNoteRows.Add(("Decision By: ", ResolveCurrentUserDisplayName()));
|
||||||
|
AddWorkflowDecisionRows(context, current, input.Approved, input.Note ?? string.Empty);
|
||||||
var result = await RunUntilWaitAsync(context, next);
|
var result = await RunUntilWaitAsync(context, next);
|
||||||
await InsertWorkflowNoteAsync(
|
await InsertWorkflowNoteAsync(
|
||||||
context,
|
context,
|
||||||
|
|
@ -403,7 +410,10 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
CurrentNodeTitle = last?.CurrentNodeTitle,
|
CurrentNodeTitle = last?.CurrentNodeTitle,
|
||||||
CurrentNodeKind = last?.CurrentNodeKind,
|
CurrentNodeKind = last?.CurrentNodeKind,
|
||||||
WaitingApproval = results.Any(x => x.WaitingApproval),
|
WaitingApproval = results.Any(x => x.WaitingApproval),
|
||||||
Completed = results.All(x => x.Completed)
|
Completed = results.All(x => x.Completed),
|
||||||
|
ToastMessages = results
|
||||||
|
.SelectMany(result => result.ToastMessages ?? [])
|
||||||
|
.ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -435,7 +445,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
.ToList();
|
.ToList();
|
||||||
var row = await GetRowAsync(code, listForm.KeyFieldName, keys[0]);
|
var row = await GetRowAsync(code, listForm.KeyFieldName, keys[0]);
|
||||||
|
|
||||||
return new WorkflowRunContext(code, listForm.KeyFieldName, keys, workflow, criteria, row);
|
return new WorkflowRunContext(code, keys, workflow, criteria, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IDictionary<string, object>> GetRowAsync(string listFormCode, string keyFieldName, object key)
|
private async Task<IDictionary<string, object>> GetRowAsync(string listFormCode, string keyFieldName, object key)
|
||||||
|
|
@ -527,17 +537,16 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
await UpdateRowAsync(context, update);
|
await UpdateRowAsync(context, update);
|
||||||
MergeRowValues(context.Row, update);
|
MergeRowValues(context.Row, update);
|
||||||
|
AddWorkflowNodeRows(context, node);
|
||||||
|
AddWorkflowToastMessage(context, node);
|
||||||
|
|
||||||
string informedRecipient = null;
|
|
||||||
if (node.Kind == "Inform")
|
if (node.Kind == "Inform")
|
||||||
{
|
{
|
||||||
informedRecipient = await SendInformEmailAsync(context, node);
|
await SendInformEmailAsync(context, node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddWorkflowNodeRows(context, node, informedRecipient ?? string.Empty);
|
private async Task SendInformEmailAsync(WorkflowRunContext context, ListFormWorkflow node)
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> SendInformEmailAsync(WorkflowRunContext context, ListFormWorkflow node)
|
|
||||||
{
|
{
|
||||||
var recipientEmail = await ResolveApproverEmailAsync(node.Approver);
|
var recipientEmail = await ResolveApproverEmailAsync(node.Approver);
|
||||||
var senderName = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromDisplayName);
|
var senderName = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromDisplayName);
|
||||||
|
|
@ -552,7 +561,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
recipientEmail,
|
recipientEmail,
|
||||||
sender,
|
sender,
|
||||||
new { },
|
new { },
|
||||||
BuildInformEmailBody(context, node),
|
BuildInformEmailBody(context, node, await BuildPreviousWorkflowNotesHtmlAsync(context)),
|
||||||
$"Workflow Bilgilendirme: {node.Title}",
|
$"Workflow Bilgilendirme: {node.Title}",
|
||||||
null,
|
null,
|
||||||
true);
|
true);
|
||||||
|
|
@ -562,7 +571,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return recipientEmail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> ResolveApproverEmailAsync(string approver)
|
private async Task<string> ResolveApproverEmailAsync(string approver)
|
||||||
|
|
@ -586,26 +594,83 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return user.Email;
|
return user.Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildInformEmailBody(WorkflowRunContext context, ListFormWorkflow node)
|
private async Task<string> BuildPreviousWorkflowNotesHtmlAsync(WorkflowRunContext context)
|
||||||
|
{
|
||||||
|
var key = context.Keys?.FirstOrDefault()?.ToString();
|
||||||
|
if (key.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var notes = (await noteRepository.GetListAsync(note =>
|
||||||
|
note.EntityName == context.ListFormCode &&
|
||||||
|
note.EntityId == key &&
|
||||||
|
note.Type == "workflow"))
|
||||||
|
.OrderBy(note => note.CreationTime)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (notes.Count == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var noteItems = notes.Select(note =>
|
||||||
|
$"""
|
||||||
|
<div style="margin: 0 0 12px 0; padding: 10px 12px; border: 1px solid #e5e7eb; border-radius: 6px;">
|
||||||
|
<div style="font-weight: 600; margin-bottom: 6px;">{Encode(note.Subject)}</div>
|
||||||
|
<div>{note.Content}</div>
|
||||||
|
</div>
|
||||||
|
""");
|
||||||
|
|
||||||
|
return string.Join(string.Empty, noteItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildInformEmailBody(
|
||||||
|
WorkflowRunContext context,
|
||||||
|
ListFormWorkflow node,
|
||||||
|
string previousWorkflowNotesHtml)
|
||||||
{
|
{
|
||||||
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
||||||
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
||||||
var nodeTitle = WebUtility.HtmlEncode(node.Title ?? string.Empty);
|
var nodeTitle = WebUtility.HtmlEncode(node.Title ?? string.Empty);
|
||||||
|
var recipient = WebUtility.HtmlEncode(node.Approver ?? string.Empty);
|
||||||
|
var processRows = BuildWorkflowNoteContent(context.WorkflowNoteRows);
|
||||||
|
var previousNotesSection = previousWorkflowNotesHtml.IsNullOrWhiteSpace()
|
||||||
|
? string.Empty
|
||||||
|
: $"""
|
||||||
|
<h3 style="margin: 18px 0 8px 0;">Önceki Workflow Notları</h3>
|
||||||
|
{previousWorkflowNotesHtml}
|
||||||
|
""";
|
||||||
|
|
||||||
return $"""
|
return $"""
|
||||||
<p>Workflow bilgilendirme adimina ulasildi.</p>
|
<div style="font-family: Arial, sans-serif; color: #111827; line-height: 1.45;">
|
||||||
<table>
|
<p>Workflow sürecinde bilgilendirme adımına ulaşıldı.</p>
|
||||||
<tr><td><strong>Liste Formu</strong></td><td>{listFormCode}</td></tr>
|
<table style="border-collapse: collapse; width: 100%; margin-bottom: 16px;">
|
||||||
<tr><td><strong>Adim</strong></td><td>{nodeTitle}</td></tr>
|
<tr><td style="padding: 6px 8px;"><strong>Liste Formu</strong></td><td style="padding: 6px 8px;">{listFormCode}</td></tr>
|
||||||
<tr><td><strong>Kayit</strong></td><td>{keyText}</td></tr>
|
<tr><td style="padding: 6px 8px;"><strong>Kayıt</strong></td><td style="padding: 6px 8px;">{keyText}</td></tr>
|
||||||
|
<tr><td style="padding: 6px 8px;"><strong>Bilgilendirme Adımı</strong></td><td style="padding: 6px 8px;">{nodeTitle}</td></tr>
|
||||||
|
<tr><td style="padding: 6px 8px;"><strong>Alıcı</strong></td><td style="padding: 6px 8px;">{recipient}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<h3 style="margin: 18px 0 8px 0;">Bu İşlemdeki Süreç Özeti</h3>
|
||||||
|
{processRows}
|
||||||
|
|
||||||
|
{previousNotesSection}
|
||||||
|
</div>
|
||||||
""";
|
""";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ResolveCurrentUserDisplayName()
|
||||||
|
{
|
||||||
|
return CurrentUser.UserName
|
||||||
|
?? CurrentUser.Name
|
||||||
|
?? CurrentUser.Id?.ToString()
|
||||||
|
?? "System";
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddWorkflowDecisionRows(
|
private static void AddWorkflowDecisionRows(
|
||||||
WorkflowRunContext context,
|
WorkflowRunContext context,
|
||||||
ListFormWorkflow current,
|
ListFormWorkflow current,
|
||||||
ListFormWorkflow next,
|
|
||||||
bool approved,
|
bool approved,
|
||||||
string description)
|
string description)
|
||||||
{
|
{
|
||||||
|
|
@ -616,8 +681,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
private static void AddWorkflowNodeRows(
|
private static void AddWorkflowNodeRows(
|
||||||
WorkflowRunContext context,
|
WorkflowRunContext context,
|
||||||
ListFormWorkflow node,
|
ListFormWorkflow node)
|
||||||
string informedRecipient)
|
|
||||||
{
|
{
|
||||||
var action = node.Kind switch
|
var action = node.Kind switch
|
||||||
{
|
{
|
||||||
|
|
@ -637,6 +701,98 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddWorkflowToastMessage(
|
||||||
|
WorkflowRunContext context,
|
||||||
|
ListFormWorkflow node)
|
||||||
|
{
|
||||||
|
var userName = ResolveCurrentUserDisplayName();
|
||||||
|
var next = FindNextToastNode(context, node);
|
||||||
|
var messageLines = node.Kind switch
|
||||||
|
{
|
||||||
|
"Start" => new[]
|
||||||
|
{
|
||||||
|
localizer["ListForms.ListForm.Workflow.WorkflowStarted"].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.PerformedBy", userName].Value
|
||||||
|
}.Concat(FormatNextToastNode(next)),
|
||||||
|
"Inform" => new[]
|
||||||
|
{
|
||||||
|
localizer["ListForms.ListForm.Workflow.InformReached"].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.InformUser", FormatToastUser(node.Approver)].Value
|
||||||
|
}.Concat(FormatNextToastNode(next)),
|
||||||
|
"End" => new[]
|
||||||
|
{
|
||||||
|
localizer["ListForms.ListForm.Workflow.WorkflowCompleted"].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.Step", node.Title].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.PerformedBy", userName].Value
|
||||||
|
},
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (messageLines != null)
|
||||||
|
{
|
||||||
|
context.ToastMessages.Add(string.Join(Environment.NewLine, messageLines));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListFormWorkflow FindNextToastNode(
|
||||||
|
WorkflowRunContext context,
|
||||||
|
ListFormWorkflow node)
|
||||||
|
{
|
||||||
|
var current = FindNextCriteria(context.Criteria, ResolveNextNodeId(context, node));
|
||||||
|
var visited = new HashSet<string>();
|
||||||
|
|
||||||
|
while (current != null && visited.Add(current.Id))
|
||||||
|
{
|
||||||
|
if (current.Kind is "Approval" or "Inform" or "End")
|
||||||
|
{
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = FindNextCriteria(context.Criteria, ResolveNextNodeId(context, current));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> FormatNextToastNode(ListFormWorkflow node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.Kind switch
|
||||||
|
{
|
||||||
|
"Approval" =>
|
||||||
|
[
|
||||||
|
localizer["ListForms.ListForm.Workflow.NextStep.Approval"].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.ApproverUser", FormatToastUser(node.Approver)].Value
|
||||||
|
],
|
||||||
|
"Inform" =>
|
||||||
|
[
|
||||||
|
localizer["ListForms.ListForm.Workflow.NextStep.Inform"].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.InformUser", FormatToastUser(node.Approver)].Value
|
||||||
|
],
|
||||||
|
"End" =>
|
||||||
|
[
|
||||||
|
localizer["ListForms.ListForm.Workflow.NextStep.End"].Value,
|
||||||
|
localizer["ListForms.ListForm.Workflow.NextStepName", node.Title].Value
|
||||||
|
],
|
||||||
|
_ => [localizer["ListForms.ListForm.Workflow.NextStep", node.Title].Value]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatToastUser(string userName)
|
||||||
|
{
|
||||||
|
return userName.IsNullOrWhiteSpace()
|
||||||
|
? localizer["ListForms.ListForm.Workflow.UndefinedUser"].Value
|
||||||
|
: userName;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task InsertWorkflowNoteAsync(
|
private async Task InsertWorkflowNoteAsync(
|
||||||
WorkflowRunContext context,
|
WorkflowRunContext context,
|
||||||
string subject,
|
string subject,
|
||||||
|
|
@ -662,20 +818,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
await noteRepository.InsertAsync(note, autoSave: true);
|
await noteRepository.InsertAsync(note, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddWorkflowFieldRow(
|
|
||||||
List<(string Label, string Value)> rows,
|
|
||||||
WorkflowRunContext context,
|
|
||||||
string label,
|
|
||||||
string fieldName)
|
|
||||||
{
|
|
||||||
if (fieldName.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.Add((label, FormatRowValue(GetRowValue(context.Row, fieldName))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildWorkflowNoteContent(List<(string Label, string Value)> rows)
|
private static string BuildWorkflowNoteContent(List<(string Label, string Value)> rows)
|
||||||
{
|
{
|
||||||
var tableRows = rows
|
var tableRows = rows
|
||||||
|
|
@ -686,14 +828,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return $"<table class=\"workflow-note-log\">{string.Join(string.Empty, tableRows)}</table>";
|
return $"<table class=\"workflow-note-log\">{string.Join(string.Empty, tableRows)}</table>";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveCurrentUserDisplayName()
|
|
||||||
{
|
|
||||||
return CurrentUser.UserName
|
|
||||||
?? CurrentUser.Name
|
|
||||||
?? CurrentUser.Id?.ToString()
|
|
||||||
?? "System";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatNode(ListFormWorkflow node)
|
private static string FormatNode(ListFormWorkflow node)
|
||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null)
|
||||||
|
|
@ -706,33 +840,11 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return $"{title} ({kind} - {node.Id})";
|
return $"{title} ({kind} - {node.Id})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatRowValue(object value)
|
|
||||||
{
|
|
||||||
if (value == null || value == DBNull.Value)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value is DateTime dateTime
|
|
||||||
? dateTime.ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
: value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Encode(string value)
|
private static string Encode(string value)
|
||||||
{
|
{
|
||||||
return WebUtility.HtmlEncode(value ?? string.Empty);
|
return WebUtility.HtmlEncode(value ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string Truncate(string value, int maxLength)
|
|
||||||
{
|
|
||||||
if (value == null || value.Length <= maxLength)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value[..maxLength];
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateRowAsync(WorkflowRunContext context, Dictionary<string, object> data)
|
private async Task UpdateRowAsync(WorkflowRunContext context, Dictionary<string, object> data)
|
||||||
{
|
{
|
||||||
await queryManager.GenerateAndRunQueryAsync<int>(
|
await queryManager.GenerateAndRunQueryAsync<int>(
|
||||||
|
|
@ -914,7 +1026,8 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
CurrentNodeTitle = node?.Title,
|
CurrentNodeTitle = node?.Title,
|
||||||
CurrentNodeKind = node?.Kind,
|
CurrentNodeKind = node?.Kind,
|
||||||
WaitingApproval = waitingApproval,
|
WaitingApproval = waitingApproval,
|
||||||
Completed = completed
|
Completed = completed,
|
||||||
|
ToastMessages = context.ToastMessages.ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1129,7 +1242,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
private sealed record WorkflowRunContext(
|
private sealed record WorkflowRunContext(
|
||||||
string ListFormCode,
|
string ListFormCode,
|
||||||
string KeyFieldName,
|
|
||||||
object[] Keys,
|
object[] Keys,
|
||||||
WorkflowDto Workflow,
|
WorkflowDto Workflow,
|
||||||
List<ListFormWorkflow> Criteria,
|
List<ListFormWorkflow> Criteria,
|
||||||
|
|
@ -1137,5 +1250,6 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
{
|
{
|
||||||
public HashSet<string> UserUpdatedFields { get; } = [];
|
public HashSet<string> UserUpdatedFields { get; } = [];
|
||||||
public List<(string Label, string Value)> WorkflowNoteRows { get; } = [];
|
public List<(string Label, string Value)> WorkflowNoteRows { get; } = [];
|
||||||
|
public List<string> ToastMessages { get; } = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19321,6 +19321,84 @@
|
||||||
"key": "FileManager.SortByModifiedDesc",
|
"key": "FileManager.SortByModifiedDesc",
|
||||||
"en": "Modified (Newest)",
|
"en": "Modified (Newest)",
|
||||||
"tr": "Değiştirilme (En Yeni)"
|
"tr": "Değiştirilme (En Yeni)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.WorkflowStarted",
|
||||||
|
"en": "Operation: Workflow started",
|
||||||
|
"tr": "İşlem: Workflow başlatıldı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.InformReached",
|
||||||
|
"en": "Operation: Workflow inform step reached",
|
||||||
|
"tr": "İşlem: Workflow bilgilendirme adımına ulaştı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.WorkflowCompleted",
|
||||||
|
"en": "Operation: Workflow completed",
|
||||||
|
"tr": "İşlem: Workflow tamamlandı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.Step",
|
||||||
|
"en": "Step: {0}",
|
||||||
|
"tr": "Adım: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.PerformedBy",
|
||||||
|
"en": "Performed by: {0}",
|
||||||
|
"tr": "İşlemi yapan: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.InformUser",
|
||||||
|
"en": "User to inform: {0}",
|
||||||
|
"tr": "Bilgilendirilecek kullanıcı: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.ApproverUser",
|
||||||
|
"en": "Approver user: {0}",
|
||||||
|
"tr": "Onaylayacak kullanıcı: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.NextStep.Approval",
|
||||||
|
"en": "Next step: Approval",
|
||||||
|
"tr": "Sonraki adım: Onay"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.NextStep.Inform",
|
||||||
|
"en": "Next step: Inform",
|
||||||
|
"tr": "Sonraki adım: Bilgilendirme"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.NextStep.End",
|
||||||
|
"en": "Next step: Workflow end",
|
||||||
|
"tr": "Sonraki adım: Workflow bitiş"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.NextStepName",
|
||||||
|
"en": "Next step name: {0}",
|
||||||
|
"tr": "Sonraki adım adı: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.NextStep",
|
||||||
|
"en": "Next step: {0}",
|
||||||
|
"tr": "Sonraki adım: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "ListForms.ListForm.Workflow.UndefinedUser",
|
||||||
|
"en": "Undefined",
|
||||||
|
"tr": "Tanımsız"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
private readonly IRepository<DataSource, Guid> _repoDataSource;
|
private readonly IRepository<DataSource, Guid> _repoDataSource;
|
||||||
private readonly IRepository<ListForm, Guid> _repoListForm;
|
private readonly IRepository<ListForm, Guid> _repoListForm;
|
||||||
private readonly IRepository<ListFormField, Guid> _repoListFormField;
|
private readonly IRepository<ListFormField, Guid> _repoListFormField;
|
||||||
|
private readonly IRepository<ListFormWorkflow, string> _repoListFormWorkflow;
|
||||||
private readonly ILogger<WizardDataSeeder> _logger;
|
private readonly ILogger<WizardDataSeeder> _logger;
|
||||||
|
|
||||||
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
|
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
|
||||||
|
|
@ -51,6 +52,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
IRepository<DataSource, Guid> repoDataSource,
|
IRepository<DataSource, Guid> repoDataSource,
|
||||||
IRepository<ListForm, Guid> repoListForm,
|
IRepository<ListForm, Guid> repoListForm,
|
||||||
IRepository<ListFormField, Guid> repoListFormField,
|
IRepository<ListFormField, Guid> repoListFormField,
|
||||||
|
IRepository<ListFormWorkflow, string> repoListFormWorkflow,
|
||||||
ILogger<WizardDataSeeder> logger)
|
ILogger<WizardDataSeeder> logger)
|
||||||
{
|
{
|
||||||
_repoLangKey = repoLangKey;
|
_repoLangKey = repoLangKey;
|
||||||
|
|
@ -62,6 +64,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
_repoDataSource = repoDataSource;
|
_repoDataSource = repoDataSource;
|
||||||
_repoListForm = repoListForm;
|
_repoListForm = repoListForm;
|
||||||
_repoListFormField = repoListFormField;
|
_repoListFormField = repoListFormField;
|
||||||
|
_repoListFormWorkflow = repoListFormWorkflow;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +137,10 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
input.Widgets ??= new List<WidgetEditDto>();
|
input.Widgets ??= new List<WidgetEditDto>();
|
||||||
input.Workflow ??= new WorkflowDto();
|
input.Workflow ??= new WorkflowDto();
|
||||||
input.WorkflowCriteria ??= new List<ListFormWorkflowCriteriaDto>();
|
input.WorkflowCriteria ??= new List<ListFormWorkflowCriteriaDto>();
|
||||||
|
if (input.WorkflowCriteria.Count == 0 && input.Workflow.Criteria?.Count > 0)
|
||||||
|
{
|
||||||
|
input.WorkflowCriteria = input.Workflow.Criteria;
|
||||||
|
}
|
||||||
input.Workflow.Criteria = input.WorkflowCriteria;
|
input.Workflow.Criteria = input.WorkflowCriteria;
|
||||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
|
||||||
|
|
||||||
|
|
@ -326,6 +333,12 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
await _repoListFormField.DeleteManyAsync(existingListFormFields, autoSave: true);
|
await _repoListFormField.DeleteManyAsync(existingListFormFields, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var existingWorkflowCriteria = await _repoListFormWorkflow.GetListAsync(a => a.ListFormCode == input.ListFormCode);
|
||||||
|
if (existingWorkflowCriteria.Count > 0)
|
||||||
|
{
|
||||||
|
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
|
||||||
|
}
|
||||||
|
|
||||||
// ListForm
|
// ListForm
|
||||||
await _repoListForm.InsertAsync(new ListForm
|
await _repoListForm.InsertAsync(new ListForm
|
||||||
{
|
{
|
||||||
|
|
@ -403,6 +416,34 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var criteria in input.WorkflowCriteria)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(criteria.Id))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Workflow criteria skipped because Id is empty. ListFormCode: {ListFormCode}, Title: {Title}", input.ListFormCode, criteria.Title);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _repoListFormWorkflow.InsertAsync(new ListFormWorkflow(criteria.Id)
|
||||||
|
{
|
||||||
|
ListFormCode = string.IsNullOrWhiteSpace(criteria.ListFormCode) ? input.ListFormCode : criteria.ListFormCode,
|
||||||
|
Kind = criteria.Kind,
|
||||||
|
Title = criteria.Title,
|
||||||
|
CompareColumn = criteria.CompareColumn,
|
||||||
|
CompareOperator = criteria.CompareOperator,
|
||||||
|
CompareValue = criteria.CompareValue,
|
||||||
|
Approver = criteria.Approver,
|
||||||
|
NextOnStart = criteria.NextOnStart,
|
||||||
|
NextOnTrue = criteria.NextOnTrue,
|
||||||
|
NextOnFalse = criteria.NextOnFalse,
|
||||||
|
NextOnApprove = criteria.NextOnApprove,
|
||||||
|
NextOnReject = criteria.NextOnReject,
|
||||||
|
PositionX = criteria.PositionX,
|
||||||
|
PositionY = criteria.PositionY,
|
||||||
|
CompareOutcomesJson = JsonSerializer.Serialize(criteria.CompareOutcomes ?? []),
|
||||||
|
}, autoSave: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasWorkflow(WorkflowDto workflow, List<ListFormWorkflowCriteriaDto> criteria)
|
private static bool HasWorkflow(WorkflowDto workflow, List<ListFormWorkflowCriteriaDto> criteria)
|
||||||
|
|
|
||||||
|
|
@ -475,7 +475,15 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
whereParts.Add("AND");
|
whereParts.Add("AND");
|
||||||
}
|
}
|
||||||
|
|
||||||
whereParts.Add($"\"{workflow.ApprovalUserFieldName}\" = '{CurrentUser.UserName}'");
|
// Hem CurrentUserName alanı boş olan kayıtları
|
||||||
|
// hem de ApprovalUserFieldName alanı CurrentUserName'e eşit olan kayıtları getirmek istiyoruz,
|
||||||
|
// Boş olanları getirmemizin sebebi workflow start edebilmektir.
|
||||||
|
// İlk kayıt eklenince onaylayacak kişi atanmaz, böylece o kayıt onaysız olarak kalmaz ve workflow başlatılabilir olur.
|
||||||
|
whereParts.Add(
|
||||||
|
$"(\"{workflow.ApprovalUserFieldName}\" = '{CurrentUser.UserName}' " +
|
||||||
|
$"OR \"{workflow.ApprovalUserFieldName}\" IS NULL " +
|
||||||
|
$"OR \"{workflow.ApprovalUserFieldName}\" = '')"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1456,6 +1456,36 @@
|
||||||
"DepartmentName": "Muhasebe",
|
"DepartmentName": "Muhasebe",
|
||||||
"Name": "Muhasebe Şefi",
|
"Name": "Muhasebe Şefi",
|
||||||
"ParentName": "Muhasebe Müdürü"
|
"ParentName": "Muhasebe Müdürü"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "b7c8d9e0-f1a2-4b3c-8d9e-0f1a2b3c4d2e",
|
||||||
|
"DepartmentName": "Bilgi İşlem",
|
||||||
|
"Name": "Bilgi İşlem Müdürü",
|
||||||
|
"ParentName": "Genel Müdür"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "b7c8d9e0-f1a2-4b3c-1d9e-0f1a2b3c4d2e",
|
||||||
|
"DepartmentName": "Finans",
|
||||||
|
"Name": "Finans Müdürü",
|
||||||
|
"ParentName": "Genel Müdür"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "b7c8d9e0-f1b2-4b3c-1d9e-0f1a2b3c4d2e",
|
||||||
|
"DepartmentName": "Satış",
|
||||||
|
"Name": "İhracat Müdürü",
|
||||||
|
"ParentName": "Genel Müdür"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "b2c8d9e0-f1b2-4b3c-1d9e-0f1a2b3c4d2e",
|
||||||
|
"DepartmentName": "Satış",
|
||||||
|
"Name": "İç Piyasa Müdürü",
|
||||||
|
"ParentName": "Genel Müdür"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "b2c8d9e0-f1b2-2b3c-1d9e-0f1a2b3c4d2e",
|
||||||
|
"DepartmentName": "Üretim",
|
||||||
|
"Name": "Üretim Müdürü",
|
||||||
|
"ParentName": "Genel Müdür"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Announcements": [
|
"Announcements": [
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export interface WorkflowRunResultDto {
|
||||||
currentNodeKind?: string | null
|
currentNodeKind?: string | null
|
||||||
waitingApproval: boolean
|
waitingApproval: boolean
|
||||||
completed: boolean
|
completed: boolean
|
||||||
|
toastMessages?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SaveCriteriaInput = Omit<Partial<WorkflowCriteriaDto>, 'id'> & {
|
export type SaveCriteriaInput = Omit<Partial<WorkflowCriteriaDto>, 'id'> & {
|
||||||
|
|
|
||||||
|
|
@ -1474,7 +1474,27 @@ const Grid = (props: GridProps) => {
|
||||||
) {
|
) {
|
||||||
workflowService
|
workflowService
|
||||||
.startWorkflow(listFormCode, [insertedKey])
|
.startWorkflow(listFormCode, [insertedKey])
|
||||||
.then(() => gridRef.current?.instance()?.refresh())
|
.then((result) => {
|
||||||
|
const messages = result.toastMessages ?? []
|
||||||
|
if (messages.length > 0) {
|
||||||
|
toast.push(
|
||||||
|
<Notification type="info" duration={7000}>
|
||||||
|
{messages.map((message, messageIndex) => (
|
||||||
|
<div
|
||||||
|
key={messageIndex}
|
||||||
|
className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}
|
||||||
|
>
|
||||||
|
{message.split('\n').map((line, lineIndex) => (
|
||||||
|
<div key={lineIndex}>{line}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
gridRef.current?.instance()?.refresh()
|
||||||
|
})
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
|
|
|
||||||
|
|
@ -1133,7 +1133,27 @@ const Tree = (props: TreeProps) => {
|
||||||
) {
|
) {
|
||||||
workflowService
|
workflowService
|
||||||
.startWorkflow(listFormCode, [insertedKey])
|
.startWorkflow(listFormCode, [insertedKey])
|
||||||
.then(() => gridRef.current?.instance()?.refresh())
|
.then((result) => {
|
||||||
|
const messages = result.toastMessages ?? []
|
||||||
|
if (messages.length > 0) {
|
||||||
|
toast.push(
|
||||||
|
<Notification type="info" duration={7000}>
|
||||||
|
{messages.map((message, messageIndex) => (
|
||||||
|
<div
|
||||||
|
key={messageIndex}
|
||||||
|
className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}
|
||||||
|
>
|
||||||
|
{message.split('\n').map((line, lineIndex) => (
|
||||||
|
<div key={lineIndex}>{line}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
gridRef.current?.instance()?.refresh()
|
||||||
|
})
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
props.refreshData?.()
|
props.refreshData?.()
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,35 @@ 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'
|
||||||
|
|
||||||
type ToolbarModalData = {
|
type ToolbarModalData = {
|
||||||
open: boolean
|
open: boolean
|
||||||
content?: JSX.Element
|
content?: JSX.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showWorkflowToastMessages = (results: WorkflowRunResultDto | WorkflowRunResultDto[]) => {
|
||||||
|
const list = Array.isArray(results) ? results : [results]
|
||||||
|
const messages = list.flatMap((result) => result.toastMessages ?? [])
|
||||||
|
|
||||||
|
if (!messages.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.push(
|
||||||
|
<Notification type="info" duration={7000}>
|
||||||
|
{messages.map((message, messageIndex) => (
|
||||||
|
<div key={messageIndex} className={messageIndex > 0 ? 'mt-2 border-t pt-2' : undefined}>
|
||||||
|
{message.split('\n').map((line, lineIndex) => (
|
||||||
|
<div key={lineIndex}>{line}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/
|
// https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/
|
||||||
// item.name > Accepted Values: 'addRowButton', 'applyFilterButton', 'columnChooserButton', 'exportButton', 'groupPanel', 'revertButton', 'saveButton', 'searchPanel'
|
// item.name > Accepted Values: 'addRowButton', 'applyFilterButton', 'columnChooserButton', 'exportButton', 'groupPanel', 'revertButton', 'saveButton', 'searchPanel'
|
||||||
const useToolbar = ({
|
const useToolbar = ({
|
||||||
|
|
@ -158,7 +181,8 @@ const useToolbar = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await workflowService.startWorkflow(listFormCode, keys)
|
const result = await workflowService.startWorkflow(listFormCode, keys)
|
||||||
|
showWorkflowToastMessages(result)
|
||||||
refreshData()
|
refreshData()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -690,11 +714,12 @@ function WorkflowApprovalDecisionDialog({
|
||||||
const decide = async (approved: boolean) => {
|
const decide = async (approved: boolean) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
try {
|
try {
|
||||||
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),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
showWorkflowToastMessages(results)
|
||||||
onCompleted()
|
onCompleted()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue