Add simplified ticket completion flow

This commit is contained in:
Drechsler, Meik
2026-07-20 10:48:40 +02:00
parent 1a3bef9eeb
commit fb45f1c42b
5 changed files with 203 additions and 5 deletions

View File

@@ -90,6 +90,29 @@ namespace C4IT.FASD.Cockpit.Communication
public abstract Task<bool> UpdateHealthcardTableData(cF4SDWriteParameters dataParameter); public abstract Task<bool> UpdateHealthcardTableData(cF4SDWriteParameters dataParameter);
public abstract Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData); public abstract Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData);
public virtual async Task<cTicketFinalizationResult> FinalizeTicketAsync(cApiM42Ticket ticketData)
{
var success = await Matrix42TicketFinalization(ticketData);
var ticketId = ticketData?.Ticket;
if (ticketId == Guid.Empty)
ticketId = null;
return new cTicketFinalizationResult
{
Success = success,
TicketId = ticketId
};
}
public virtual Task<cTicketExternalLinkResult> GetTicketExternalLinkAsync(Guid ticketId, enumTicketExternalOpenMode mode)
{
return Task.FromResult(new cTicketExternalLinkResult
{
Success = false,
Mode = enumTicketExternalOpenMode.None
});
}
public abstract Task<List<List<object>>> GetQuickActionHistory(string QuickActionName, int OrgId, int DeviceId, int? UserId); public abstract Task<List<List<object>>> GetQuickActionHistory(string QuickActionName, int OrgId, int DeviceId, int? UserId);
public abstract Task<bool> GetAgentApiAccessInfo(); public abstract Task<bool> GetAgentApiAccessInfo();

View File

@@ -1515,6 +1515,12 @@ namespace C4IT.FASD.Cockpit.Communication
return output; return output;
} }
public override async Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData) public override async Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData)
{
var result = await FinalizeTicketAsync(ticketData);
return result?.Success == true;
}
public override async Task<cTicketFinalizationResult> FinalizeTicketAsync(cApiM42Ticket ticketData)
{ {
var CM = MethodBase.GetCurrentMethod(); var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM); LogMethodBegin(CM);
@@ -1522,7 +1528,6 @@ namespace C4IT.FASD.Cockpit.Communication
var apiError = 0; var apiError = 0;
var timeStart = DateTime.UtcNow; var timeStart = DateTime.UtcNow;
bool output = false;
try try
{ {
var http = GetHttpHelper(true); var http = GetHttpHelper(true);
@@ -1532,7 +1537,7 @@ namespace C4IT.FASD.Cockpit.Communication
var result = await http.PostJsonAsync(url, payload, 20000, CancellationToken.None); var result = await http.PostJsonAsync(url, payload, 20000, CancellationToken.None);
if (result.IsOk) if (result.IsOk)
return true; return ParseTicketFinalizationResult(result.Result, ticketData?.Ticket);
else else
apiError = (int)result.Status; apiError = (int)result.Status;
} }
@@ -1547,7 +1552,102 @@ namespace C4IT.FASD.Cockpit.Communication
LogMethodEnd(CM); LogMethodEnd(CM);
} }
return output; return new cTicketFinalizationResult { Success = false };
}
internal static cTicketFinalizationResult ParseTicketFinalizationResult(string response, Guid? fallbackTicketId)
{
if (fallbackTicketId == Guid.Empty)
fallbackTicketId = null;
if (!string.IsNullOrWhiteSpace(response))
{
try
{
var token = JToken.Parse(response);
if (token.Type == JTokenType.Object)
{
var result = token.ToObject<cTicketFinalizationResult>();
if (result != null)
{
if (result.Success && (!result.TicketId.HasValue || result.TicketId.Value == Guid.Empty))
result.TicketId = fallbackTicketId;
return result;
}
}
else if (token.Type == JTokenType.String &&
Guid.TryParse(token.Value<string>(), out var createdTicketId))
{
return new cTicketFinalizationResult
{
Success = true,
TicketId = createdTicketId
};
}
}
catch (JsonException)
{
if (Guid.TryParse(response.Trim().Trim('"'), out var createdTicketId))
{
return new cTicketFinalizationResult
{
Success = true,
TicketId = createdTicketId
};
}
}
}
// Older providers returned an empty JSON value for successful updates.
return new cTicketFinalizationResult
{
Success = true,
TicketId = fallbackTicketId
};
}
public override async Task<cTicketExternalLinkResult> GetTicketExternalLinkAsync(Guid ticketId, enumTicketExternalOpenMode mode)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var apiError = 0;
var timeStart = DateTime.UtcNow;
try
{
if (ticketId == Guid.Empty || mode == enumTicketExternalOpenMode.None)
return new cTicketExternalLinkResult { Success = false, Mode = enumTicketExternalOpenMode.None };
var http = GetHttpHelper(true);
var url = $"api/Ticketing/GetExternalLink?ticketId={ticketId:D}&mode={Uri.EscapeDataString(mode.ToString())}";
var result = await http.GetHttpJson(url, 20000, CancellationToken.None);
if (result.IsOk)
{
return JsonConvert.DeserializeObject<cTicketExternalLinkResult>(result.Result)
?? new cTicketExternalLinkResult { Success = false, Mode = enumTicketExternalOpenMode.None };
}
apiError = (int)result.Status;
}
catch (Exception E)
{
apiError = E.HResult;
LogException(E);
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("Ticketing-GetExternalLink", timeStart, apiError);
LogMethodEnd(CM);
}
return new cTicketExternalLinkResult
{
Success = false,
Mode = enumTicketExternalOpenMode.None
};
} }
public override async Task<bool> GetAgentApiAccessInfo() public override async Task<bool> GetAgentApiAccessInfo()

View File

@@ -12,6 +12,8 @@
<TicketConfiguration> <TicketConfiguration>
<DisableAutomaticTimeTracking Policy="Mandatory" Value="true" /> <DisableAutomaticTimeTracking Policy="Mandatory" Value="true" />
<CompletitionPolicy Policy="Mandatory" Value="IfRequired" /> <CompletitionPolicy Policy="Mandatory" Value="IfRequired" />
<UseSimplifiedCaseCompletionDialog Policy="Hidden" Value="false" />
<SimplifiedCaseCompletionTicketOpenMode Policy="Hidden" Value="Preview" />
<NotesMandatory Policy="Mandatory" Value="true" /> <NotesMandatory Policy="Mandatory" Value="true" />
<ShowOverview Policy="Mandatory" Value="true" /> <ShowOverview Policy="Mandatory" Value="true" />
<OpenActivitiesExternally Policy="Mandatory" Value="false"> <OpenActivitiesExternally Policy="Mandatory" Value="false">

View File

@@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using C4IT.FASD.Base; using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging; using C4IT.Logging;
using FasdDesktopUi.Basics; using FasdDesktopUi.Basics;
@@ -15,6 +17,33 @@ namespace FasdDesktopUi.Basics.Helper
{ {
private const string DefaultTicketActivityType = "SPSActivityTypeTicket"; private const string DefaultTicketActivityType = "SPSActivityTypeTicket";
internal static async Task<bool> TryOpenFinalizedTicketExternallyAsync(Guid ticketId, enumTicketExternalOpenMode mode)
{
try
{
if (ticketId == Guid.Empty || mode == enumTicketExternalOpenMode.None)
return false;
var linkResult = await cFasdCockpitCommunicationBase.Instance.GetTicketExternalLinkAsync(ticketId, mode);
if (linkResult?.Success != true ||
!Uri.TryCreate(linkResult.Url, UriKind.Absolute, out var ticketUri) ||
(ticketUri.Scheme != Uri.UriSchemeHttp && ticketUri.Scheme != Uri.UriSchemeHttps))
{
LogEntry($"Could not resolve an external link for ticket '{ticketId:D}'.", LogLevels.Warning);
return false;
}
new cBrowsers().Start("default", ticketUri.AbsoluteUri);
return true;
}
catch (Exception ex)
{
LogException(ex);
}
return false;
}
internal static bool TryOpenTicketRelationExternally(cF4sdApiSearchResultRelation relation, bool forceExternal = false) internal static bool TryOpenTicketRelationExternally(cF4sdApiSearchResultRelation relation, bool forceExternal = false)
{ {
try try

View File

@@ -104,6 +104,13 @@ namespace FasdDesktopUi.Basics.UserControls
public bool AreCaseNotesMandatory => public bool AreCaseNotesMandatory =>
cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.NotesMandatory ?? false; cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.NotesMandatory ?? false;
private bool UseSimplifiedCaseCompletionDialog =>
cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.UseSimplifiedCaseCompletionDialog == true;
private enumTicketExternalOpenMode SimplifiedCaseCompletionTicketOpenMode =>
cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.SimplifiedCaseCompletionTicketOpenMode
?? enumTicketExternalOpenMode.Preview;
private static class TicketInfoKeys private static class TicketInfoKeys
{ {
public const string ActivityType = "ActivityType"; public const string ActivityType = "ActivityType";
@@ -531,6 +538,8 @@ namespace FasdDesktopUi.Basics.UserControls
private void CloseCaseDialogWithTicket_Loaded(object sender, RoutedEventArgs e) private void CloseCaseDialogWithTicket_Loaded(object sender, RoutedEventArgs e)
{ {
UpdateTicketComponentVisibility();
Window _parentWindow = Window.GetWindow(this); Window _parentWindow = Window.GetWindow(this);
if (!(_parentWindow is TicketCompletion _msgBox)) if (!(_parentWindow is TicketCompletion _msgBox))
@@ -623,10 +632,17 @@ namespace FasdDesktopUi.Basics.UserControls
return; return;
var _isVisible = checkTicketActive() && !_skipTicketPillSelected; var _isVisible = checkTicketActive() && !_skipTicketPillSelected;
var useSimplifiedDialog = _isVisible && UseSimplifiedCaseCompletionDialog;
if (useSimplifiedDialog)
SelectSaveTicketAction();
TicketSelectionCategory.Visibility = _isVisible ? Visibility.Visible : Visibility.Collapsed; TicketSelectionCategory.Visibility = _isVisible ? Visibility.Visible : Visibility.Collapsed;
StatusSelectionBorder.Visibility = _isVisible ? Visibility.Visible : Visibility.Collapsed; StatusSelectionBorder.Visibility = _isVisible && !useSimplifiedDialog ? Visibility.Visible : Visibility.Collapsed;
CopyDisclaimerTextBox.Visibility = _isVisible ? Visibility.Collapsed : Visibility.Visible; CopyDisclaimerTextBox.Visibility = _isVisible ? Visibility.Collapsed : Visibility.Visible;
if (!_isVisible || useSimplifiedDialog)
DynamicStatusAdditionBorder.Visibility = Visibility.Collapsed;
} }
catch (Exception E) catch (Exception E)
{ {
@@ -634,6 +650,21 @@ namespace FasdDesktopUi.Basics.UserControls
} }
} }
private void SelectSaveTicketAction()
{
var saveItem = TicketStatusCombobox.Items
.OfType<ComboBoxItem>()
.FirstOrDefault(item =>
Enum.TryParse(item.Tag?.ToString(), out enumCloseCaseTicketStatus action) &&
action == enumCloseCaseTicketStatus.Save);
if (saveItem != null && !ReferenceEquals(TicketStatusCombobox.SelectedItem, saveItem))
TicketStatusCombobox.SelectedItem = saveItem;
DynamicStatusAdditionBorder.Child = null;
DynamicStatusAdditionBorder.Visibility = Visibility.Collapsed;
}
private UIElement GetTicketUiElement(cF4sdApiSearchResultRelation ticketRelation) private UIElement GetTicketUiElement(cF4sdApiSearchResultRelation ticketRelation)
{ {
try try
@@ -2011,7 +2042,20 @@ namespace FasdDesktopUi.Basics.UserControls
ticketData.AdditionalValues.Add("Dialog.CloseCase.AssetNotFoundInMatrix42", cMultiLanguageSupport.GetItem("Dialog.CloseCase.AssetNotFoundInMatrix42")); ticketData.AdditionalValues.Add("Dialog.CloseCase.AssetNotFoundInMatrix42", cMultiLanguageSupport.GetItem("Dialog.CloseCase.AssetNotFoundInMatrix42"));
ticketData.AdditionalValues.Add("Dialog.CloseCase.ReopenReasonText", cMultiLanguageSupport.GetItem("Dialog.CloseCase.ReopenReasonText")); ticketData.AdditionalValues.Add("Dialog.CloseCase.ReopenReasonText", cMultiLanguageSupport.GetItem("Dialog.CloseCase.ReopenReasonText"));
ticketData.AdditionalValues.Add("Dialog.CloseCase.ReopenReasonTextHtml", cMultiLanguageSupport.GetItem("Dialog.CloseCase.ReopenReasonTextHtml")); ticketData.AdditionalValues.Add("Dialog.CloseCase.ReopenReasonTextHtml", cMultiLanguageSupport.GetItem("Dialog.CloseCase.ReopenReasonTextHtml"));
var _res = await cFasdCockpitCommunicationBase.Instance.Matrix42TicketFinalization(ticketData); var finalizationResult = await cFasdCockpitCommunicationBase.Instance.FinalizeTicketAsync(ticketData);
if (finalizationResult?.Success != true)
return false;
if (UseSimplifiedCaseCompletionDialog &&
SimplifiedCaseCompletionTicketOpenMode != enumTicketExternalOpenMode.None &&
finalizationResult.TicketId is Guid finalizedTicketId &&
finalizedTicketId != Guid.Empty)
{
await TicketExternalLinkHelper.TryOpenFinalizedTicketExternallyAsync(
finalizedTicketId,
SimplifiedCaseCompletionTicketOpenMode);
}
return true; return true;
} }
catch (Exception e) catch (Exception e)