AiVoiceMode ("direct" und "push") eingefügt
This commit is contained in:
284
BeWo/View/Detail/Report/DatenbereinigungView.xaml
Normal file
284
BeWo/View/Detail/Report/DatenbereinigungView.xaml
Normal file
@@ -0,0 +1,284 @@
|
||||
<localView:BeWoView
|
||||
x:Class="BeWo.View.Detail.Report.DatenbereinigungView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic"
|
||||
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
|
||||
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
|
||||
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
|
||||
xmlns:localView="clr-namespace:BeWo.View"
|
||||
xmlns:localViewDetail="clr-namespace:BeWo.View.Detail.Accounting"
|
||||
xmlns:localViewModel="clr-namespace:BeWo.ViewModel"
|
||||
xmlns:markup="clr-namespace:BeWo.MultiLanguage.Markup"
|
||||
xmlns:proxy="clr-namespace:BeWo.ServiceProxy"
|
||||
xmlns:r="clr-namespace:BeWo.Security"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:uc="clr-namespace:BeWo.Controls;assembly=BeWo.Controls"
|
||||
xmlns:val="clr-namespace:BeWo.Validation"
|
||||
Width="Auto"
|
||||
Height="Auto"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Focusable="True">
|
||||
|
||||
<Grid>
|
||||
|
||||
<GroupBox
|
||||
x:Name="groupbox_editArea"
|
||||
Grid.Row="0"
|
||||
Header="{markup:Translate Datenbereinigung}"
|
||||
Style="{DynamicResource ObjectEditGroupBox}">
|
||||
|
||||
<Grid Margin="0,3,0,0" Grid.IsSharedSizeScope="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Label Margin="3">Anzahl Jahre ungenutzt:</Label>
|
||||
<dxe:SpinEdit
|
||||
x:Name="editJahre"
|
||||
Grid.Row="1"
|
||||
Height="20"
|
||||
Width="60"
|
||||
Margin="3"
|
||||
MinValue="1"
|
||||
MaxValue="99"
|
||||
IsFloatValue="False"
|
||||
VerticalContentAlignment="Center"/>
|
||||
<RadioButton
|
||||
x:Name="radioCustomer"
|
||||
Margin="5,7,5,0"
|
||||
Content="{markup:Translate Klienten}"
|
||||
IsChecked="True" />
|
||||
<RadioButton
|
||||
x:Name="radioEmployee"
|
||||
Margin="5,7,5,0"
|
||||
Content="{markup:Translate Mitarbeiter}"
|
||||
IsChecked="False" />
|
||||
<RadioButton
|
||||
x:Name="radioPersonen"
|
||||
Margin="5,7,5,0"
|
||||
Content="{markup:Translate Personen}"
|
||||
IsChecked="False" />
|
||||
<Button
|
||||
x:Name="button_reload"
|
||||
Grid.Column="1"
|
||||
Width="80"
|
||||
Height="24"
|
||||
Margin="3"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="ReloadButton_OnClick"
|
||||
Content="Aktualisieren" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
|
||||
|
||||
<dxg:GridControl
|
||||
x:Name="datagrid_inactiveData"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="3"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<dxg:GridControl.Columns>
|
||||
<dxg:GridColumn
|
||||
x:Name="ColumnChecked"
|
||||
MinWidth="30"
|
||||
FieldName="IsChecked"
|
||||
Header=" " />
|
||||
<dxg:GridColumn
|
||||
x:Name="ColumnButtons"
|
||||
Width="50"
|
||||
AllowEditing="False"
|
||||
FieldName="ReportLink"
|
||||
Header=" ">
|
||||
<dxg:GridColumn.DisplayTemplate>
|
||||
<ControlTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
<Button
|
||||
x:Name="btn_delete"
|
||||
Width="22"
|
||||
Height="22"
|
||||
Margin="1"
|
||||
Padding="0"
|
||||
VerticalAlignment="Center"
|
||||
Click="btn_delete_Click"
|
||||
Content="r"
|
||||
FontFamily="Webdings"
|
||||
FontSize="13"
|
||||
Foreground="#FF0000"
|
||||
ToolTip="ToolTipDelete" />
|
||||
<Button
|
||||
x:Name="btn_anonymisieren"
|
||||
Width="22"
|
||||
Height="22"
|
||||
Margin="1"
|
||||
Padding="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="?"
|
||||
FontSize="14"
|
||||
Foreground="#FFFFFF"
|
||||
Click="btn_anonymisieren_Click"
|
||||
ToolTip="{markup:Translate ToolTipAnonymisieren}" />
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</dxg:GridColumn.DisplayTemplate>
|
||||
</dxg:GridColumn>
|
||||
<dxg:GridColumn
|
||||
Width="150"
|
||||
AllowEditing="False"
|
||||
FieldName="LastName"
|
||||
Header="Nachname"
|
||||
ReadOnly="True" />
|
||||
<dxg:GridColumn
|
||||
Width="100"
|
||||
AllowEditing="False"
|
||||
FieldName="FirstName"
|
||||
Header="Vorname"
|
||||
ReadOnly="True" />
|
||||
<dxg:GridColumn
|
||||
Width="100"
|
||||
AllowEditing="False"
|
||||
FieldName="DateOfBirth"
|
||||
EditSettings="{dxe:DateSettings DisplayFormat=d}"
|
||||
Header="Geburtsdatum"
|
||||
ReadOnly="True"
|
||||
SortFieldName="AccountingPeriodStart" />
|
||||
<dxg:GridColumn
|
||||
AllowEditing="False"
|
||||
FieldName="Street"
|
||||
Header="Straße"
|
||||
ReadOnly="True"
|
||||
SortOrder="Ascending" />
|
||||
<dxg:GridColumn
|
||||
MinWidth="70"
|
||||
AllowEditing="False"
|
||||
FieldName="PostalCode"
|
||||
Header="PLZ"
|
||||
ReadOnly="True" />
|
||||
<dxg:GridColumn
|
||||
AllowEditing="False"
|
||||
FieldName="Town"
|
||||
Header="Ort"
|
||||
ReadOnly="True" />
|
||||
<!--<dxg:GridColumn FieldName="AccountingPeriodString" Header="Abrechnungszeitraum" />-->
|
||||
<dxg:GridColumn
|
||||
MinWidth="70"
|
||||
AllowEditing="False"
|
||||
FieldName="LastUsedOn"
|
||||
Header="Zuletzt verwendet am"
|
||||
ReadOnly="True">
|
||||
</dxg:GridColumn>
|
||||
<dxg:GridColumn
|
||||
AllowEditing="False"
|
||||
FieldName="Notice"
|
||||
Header="Bemerkung"
|
||||
ReadOnly="True" />
|
||||
</dxg:GridControl.Columns>
|
||||
<dxg:GridControl.View>
|
||||
<dxg:TableView
|
||||
x:Name="gridView"
|
||||
AllowBestFit="True"
|
||||
BestFitMode="Smart"
|
||||
Loaded="gridView_Loaded"
|
||||
NavigationStyle="Cell"
|
||||
ScrollingMode="Smart"
|
||||
ShowGroupPanel="False"
|
||||
SnapsToDevicePixels="True" />
|
||||
</dxg:GridControl.View>
|
||||
</dxg:GridControl>
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="5,2,5,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding FilterResultAmountSum, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}Summe aller Rechnungen: {0:c}'}"
|
||||
FontSize="14"
|
||||
Foreground="{StaticResource GridPrintMenuLightBrush}" />
|
||||
<Label
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content=" | "
|
||||
FontSize="14"
|
||||
Foreground="{StaticResource GridPrintMenuLightBrush}" />
|
||||
<Label
|
||||
x:Name="lblAmoutNotPaid"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding FilterResultAmountNotPaidSum, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}Summe der noch offenen Rechnungen: {0:c}'}"
|
||||
FontSize="14"
|
||||
Foreground="{StaticResource GridPrintMenuLightBrush}" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal">
|
||||
<Button
|
||||
Name="button_checkAllLeistungen"
|
||||
Margin="10,3,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Click="button_checkAll_Click">
|
||||
Alle auswählen
|
||||
</Button>
|
||||
<Button
|
||||
Name="button_uncheckAllLeistungen"
|
||||
Margin="10,3,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Click="button_uncheckAll_Click">
|
||||
Alle abwählen
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Name="button_deleteAll"
|
||||
Margin="20,3,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Click="button_deleteAll_Click">
|
||||
Ausgewählte löschen
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Name="button_anonymisierenAll"
|
||||
Margin="10,3,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Click="button_anonymisierenAll_Click">
|
||||
Ausgewählte anonymisieren
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
</Grid>
|
||||
|
||||
</localView:BeWoView>
|
||||
173
BeWo/View/Detail/Report/DatenbereinigungView.xaml.cs
Normal file
173
BeWo/View/Detail/Report/DatenbereinigungView.xaml.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Policy;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Threading;
|
||||
using BeWo.Core;
|
||||
using BeWo.ServiceProxy;
|
||||
using BeWo.View.Windows;
|
||||
using BeWo.ViewModel;
|
||||
using BeWo.ViewModel.ListViewModel;
|
||||
|
||||
using BS.Shared;
|
||||
using BS.Shared.Core;
|
||||
using BS.Shared.DataContracts;
|
||||
using BS.Shared.DataContracts.Compact;
|
||||
using BS.Shared.Extensions;
|
||||
using BS.Shared.Translation;
|
||||
using DevExpress.Xpf.Editors;
|
||||
using DevExpress.Xpf.Grid;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BeWo.View.Detail.Report
|
||||
{
|
||||
public partial class DatenbereinigungView : BeWoView
|
||||
{
|
||||
|
||||
public DatenbereinigungView()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
editJahre.Value = 10;
|
||||
button_reload.Focus();
|
||||
CheckRights();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void CheckRights()
|
||||
{
|
||||
if (!BeWoApp.AppSettings.AllowStorno)
|
||||
{
|
||||
ColumnButtons.MinWidth = 67;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void RefreshList()
|
||||
{
|
||||
DateTime? start = null;
|
||||
DateTime? end = null;
|
||||
|
||||
//bool filterByInvoiceDate = radioFilterByInvoiceDate.IsChecked.Value;
|
||||
var tid = TableID.Customer;
|
||||
if (radioEmployee.IsChecked == true) {
|
||||
tid = TableID.Employee;
|
||||
} else if (radioPersonen.IsChecked == true) {
|
||||
tid = TableID.Person;
|
||||
}
|
||||
|
||||
int anzahlJahre = (int)editJahre.Value;
|
||||
|
||||
ServiceFacade.DoOperationsServiceAsync(s => s.GetInactivePersons(anzahlJahre, tid), UpdateList);
|
||||
}
|
||||
|
||||
private void UpdateList(IList<InactivePersonDC> dcList)
|
||||
{
|
||||
this.Dispatcher.BeginInvoke(
|
||||
DispatcherPriority.Normal,
|
||||
(Action)delegate
|
||||
{
|
||||
//ViewModel.UpdateList(dcList);
|
||||
this.datagrid_inactiveData.ItemsSource = dcList;
|
||||
this.gridView.BestFitColumns();
|
||||
});
|
||||
}
|
||||
|
||||
private void ReloadButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private void btn_delete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = this.datagrid_inactiveData.GetCurrentValue<InactivePersonDC>();
|
||||
|
||||
if (MessageBox.Show("Sind Sie sicher, dass Sie die gewählte Rechnung löschen möchten?", "Rechnung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void btn_anonymisieren_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = this.datagrid_inactiveData.GetCurrentValue<InactivePersonDC>();
|
||||
|
||||
if (MessageBox.Show("Sind Sie sicher, dass Sie die gewählte Rechnung löschen möchten?", "Rechnung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void gridView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.gridView.BestFitColumns();
|
||||
}
|
||||
|
||||
private void button_checkAll_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var visible = GetVisibleVMs();
|
||||
foreach (var item in visible)
|
||||
{
|
||||
item.IsChecked = true;
|
||||
}
|
||||
|
||||
datagrid_inactiveData.RefreshData();
|
||||
}
|
||||
|
||||
private void button_uncheckAll_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var visible = GetVisibleVMs();
|
||||
foreach (var item in visible)
|
||||
{
|
||||
item.IsChecked = false;
|
||||
}
|
||||
|
||||
datagrid_inactiveData.RefreshData();
|
||||
}
|
||||
|
||||
private void button_deleteAll_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void button_anonymisierenAll_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private IList<InactivePersonDC> GetVisibleVMs()
|
||||
{
|
||||
var list = new List<InactivePersonDC>();
|
||||
|
||||
var items = datagrid_inactiveData.ItemsSource as IList<InactivePersonDC>;
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (datagrid_inactiveData.IsRowVisible(i))
|
||||
{
|
||||
var vm = datagrid_inactiveData.GetRow(i) as InactivePersonDC;
|
||||
if (vm != null)
|
||||
{
|
||||
list.Add(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -383,6 +383,8 @@ namespace BeWoPlanerMobil.Controllers
|
||||
Model.SelectedZeitraum = new NullableDateTimeSpan(Model.LastSelectedServiceRecordMonth.Date.GetFirstOfMonth(), Model.LastSelectedServiceRecordMonth.Date.GetLastOfMonth());
|
||||
}
|
||||
|
||||
Model.AiVoiceMode = UserSettingsUtils.GetSettingValueAsInteger(userSettings, SettingsKeys.AiVoiceMode);
|
||||
|
||||
CalculateSelectedDayCount(Model.LastSelectedServiceRecordTimeIntervalDays, Model.LastSelectedServiceRecordMonth, Model.SelectedZeitraum?.StartDate, Model.SelectedZeitraum?.EndDate, null);
|
||||
|
||||
var customerFilter = MobileUserSettingsUtils.GetSettingValueAsEnum(userSettings, SettingsKeys.CustomerFilterInZeiterfassung, CustomerFilterEnum.MyCustomer);
|
||||
@@ -3563,35 +3565,65 @@ namespace BeWoPlanerMobil.Controllers
|
||||
return View("Main", Model);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public ActionResult SetAiVoiceMode(string mode)
|
||||
{
|
||||
SkipModelInitialization = true;
|
||||
Model.AiVoiceMode = mode == "1" ? 1 : 0;
|
||||
|
||||
UpdateUserSettingsWithoutReload(SettingsKeys.AiVoiceMode, Model.AiVoiceMode.ToString());
|
||||
|
||||
return new HttpStatusCodeResult(200);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ermittelt die externe URL, die beim Klick auf den Test-Button aufgerufen werden soll.
|
||||
/// Die callbackUrl wird als Parameter mitgegeben, damit das externe System sie verwenden kann.
|
||||
/// Ermittelt die externe URL, die beim Klick auf den AI-Voice-Button aufgerufen werden soll.
|
||||
/// Die callId wird server-seitig als GUID erzeugt (kryptographisch sicher) und zusammen
|
||||
/// mit der URL an den Client zurückgegeben.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public JsonResult GetExternalCallUrl(string callId, string callbackUrl)
|
||||
public JsonResult GetExternalCallUrl()
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
SkipModelInitialization = true;
|
||||
// CallId server-seitig erzeugen – Guid.NewGuid() ist kryptographisch sicher (128-Bit)
|
||||
// In der Session speichern – der Browser bekommt sie nie zu sehen
|
||||
var callId = Guid.NewGuid().ToString("N");
|
||||
Session["ExternalCallId"] = callId;
|
||||
|
||||
// CallbackUrl server-seitig zusammenbauen
|
||||
var callbackUrl = Url.Action("ExternalCallCallback", "Main", new { callId }, Request.Url.Scheme);
|
||||
|
||||
var externalUrl = "";
|
||||
|
||||
if (Mandator?.OwnChatCode != null)
|
||||
{
|
||||
var ts = new TranscriptionService();
|
||||
var avd = ts.InitTranskription(MobileSessionFacade.Tenant, false);
|
||||
var sendOwnChatMessage = Model.AiVoiceMode == 0 ? false : true;
|
||||
|
||||
externalUrl = avd.OwnChatUrl;
|
||||
var avd = ts.InitTranskription(MobileSessionFacade.Tenant, sendOwnChatMessage);
|
||||
|
||||
if (!string.IsNullOrEmpty(callId) && !string.IsNullOrEmpty(avd.Token))
|
||||
if (!sendOwnChatMessage)
|
||||
{
|
||||
externalUrl = avd.OwnChatUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
avd.OwnChatUrl = null;
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(avd.Token))
|
||||
{
|
||||
_externalCallTokens[callId] = ts.MapAiVoiceDataDC(avd);
|
||||
}
|
||||
|
||||
Log.Info($"GetExternalCallUrl: {callId}. Url = {externalUrl}, Transkription startet!");
|
||||
//var externalBaseUrl = "https://example.com/external-service";
|
||||
//var externalUrl = externalBaseUrl
|
||||
// + "?callbackUrl=" + Uri.EscapeDataString(callbackUrl)
|
||||
// + "&callId=" + Uri.EscapeDataString(callId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3646,12 +3678,15 @@ namespace BeWoPlanerMobil.Controllers
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public JsonResult CheckExternalCallStatus(string callId)
|
||||
public JsonResult CheckExternalCallStatus()
|
||||
{
|
||||
var callId = Session["ExternalCallId"] as string;
|
||||
|
||||
if(!string.IsNullOrEmpty(callId) && _externalCallResults.TryRemove(callId, out var data))
|
||||
{
|
||||
_externalCallStatusMessages.TryRemove(callId, out _);
|
||||
_externalCallTokens.TryRemove(callId, out _);
|
||||
Session.Remove("ExternalCallId");
|
||||
return Json(new { completed = true, data }, JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
|
||||
@@ -3668,10 +3703,14 @@ namespace BeWoPlanerMobil.Controllers
|
||||
|
||||
statusMessage = avd.StatusMessage;
|
||||
|
||||
if (!String.IsNullOrEmpty(statusMessage) && statusMessage.Contains("klicken Sie auf den Link"))
|
||||
if (!String.IsNullOrEmpty(avd.OwnChatUrl)) // Wenn nicht null, dann wurde die Spracheingabe über direkt Link gestartet
|
||||
{
|
||||
statusMessage = "Sollte die Aufnahme nicht starten, prüfen Sie bitte, ob ownChat korrekt installiert ist.";
|
||||
if (!String.IsNullOrEmpty(statusMessage) && statusMessage.Contains("klicken Sie auf den Link"))
|
||||
{
|
||||
statusMessage = "Warte auf Beendigung der Aufzeichnung in ownChat...\r\nHINWEIS: Wenn ownChat auf diesem Gerät nicht installiert ist, ist die Spracheingabe nicht möglich. In diesem Fall brechen Sie den Vorgang bitte ab. Wenn Sie ownChat auf einem anderen Gerät nutzen, können Sie in den Einstellungen auch in den Push-Modus wechseln.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(avd.TranskriptionText))
|
||||
{
|
||||
statusMessage = "Transkription erfolgreich durchgeführt";
|
||||
@@ -3708,13 +3747,15 @@ namespace BeWoPlanerMobil.Controllers
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public ActionResult CancelExternalCall(string callId)
|
||||
public ActionResult CancelExternalCall()
|
||||
{
|
||||
var callId = Session["ExternalCallId"] as string;
|
||||
if(!string.IsNullOrEmpty(callId))
|
||||
{
|
||||
_externalCallResults.TryRemove(callId, out _);
|
||||
_externalCallStatusMessages.TryRemove(callId, out _);
|
||||
_externalCallTokens.TryRemove(callId, out _);
|
||||
Session.Remove("ExternalCallId");
|
||||
}
|
||||
|
||||
return new HttpStatusCodeResult(200);
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace BeWoPlanerMobil.Models
|
||||
[Serializable]
|
||||
public abstract class BaseSessionModel
|
||||
{
|
||||
public int AiVoiceMode { get; set; }
|
||||
|
||||
public Dictionary<string, bool> Collapsibles2IsShown { get; set; } = new Dictionary<string, bool>();
|
||||
|
||||
public virtual void ResetValues()
|
||||
|
||||
@@ -820,6 +820,8 @@ namespace BeWoPlanerMobil.Models
|
||||
|
||||
public bool IsShowingDurationInHours { get; set; }
|
||||
|
||||
public int AiVoiceMode { get { return MainSessionModel.AiVoiceMode; } set { MainSessionModel.AiVoiceMode = value; } } // 0 = direct, 1 = Link senden
|
||||
|
||||
public string SelectedDurationUnit => IsShowingDurationInHours ? "Stunden" : "Minuten";
|
||||
|
||||
public DateTime LastSelectedServiceRecordMonth { get; set; }
|
||||
|
||||
@@ -170,37 +170,35 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var _externalCallPollingTimer = null;
|
||||
var _externalCallId = null;
|
||||
|
||||
// Zuletzt verwendeter Modus – wird aus der Session initialisiert
|
||||
var _aiVoiceMode = '@Model.AiVoiceMode';
|
||||
|
||||
function startExternalCall() {
|
||||
// Eindeutige ID für diesen Aufruf generieren
|
||||
_externalCallId = 'ext-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
|
||||
// Direkt starten mit dem gespeicherten Modus, keine Moduswahl anzeigen
|
||||
if (_aiVoiceMode === '1') {
|
||||
$('#external-call-status-text').text('Um die Aufnahme zu starten, klicken Sie auf den Link, den wir an Ihren ownChat Account gesendet haben.');
|
||||
}
|
||||
else {
|
||||
$('#external-call-status-text').text('Bitte wechseln Sie zu ownChat um die Aufnahme zu starten.\nHINWEIS: Wenn ownChat auf diesem Gerät nicht installiert ist, ist die Spracheingabe nicht möglich.');
|
||||
}
|
||||
document.getElementById('external-call-overlay').style.display = 'flex';
|
||||
|
||||
// Callback-URL für das externe System
|
||||
var callbackUrl = window.location.origin + '@Url.Action("ExternalCallCallback", "Main")' + '?callId=' + encodeURIComponent(_externalCallId);
|
||||
|
||||
// Externe URL vom Server ermitteln lassen
|
||||
$.ajax({
|
||||
url: '@Url.Action("GetExternalCallUrl", "Main")',
|
||||
data: { callId: _externalCallId, callbackUrl: callbackUrl },
|
||||
type: 'GET',
|
||||
success: function(result) {
|
||||
if (result && result.success && result.url) {
|
||||
// Overlay anzeigen und Statustext zurücksetzen
|
||||
$('#external-call-status-text').text('Um die Aufnahme zu starten, klicken Sie auf den Link, den wir an Ihren ownChat Account gesendet haben.');
|
||||
document.getElementById('external-call-overlay').style.display = 'flex';
|
||||
|
||||
if (result && result.success) {
|
||||
// Custom URL Scheme (ownchat://) per temporärem Anchor-Element öffnen –
|
||||
// zuverlässiger als window.open() für alle Browser inkl. Safari
|
||||
var $a = $('<a>').attr('href', result.url).css('display', 'none').appendTo('body');
|
||||
$a[0].click();
|
||||
$a.remove();
|
||||
|
||||
if (result.url && result.url.length > 0) {
|
||||
var $a = $('<a>').attr('href', result.url).css('display', 'none').appendTo('body');
|
||||
$a[0].click();
|
||||
$a.remove();
|
||||
}
|
||||
// Polling starten - prüft regelmäßig ob der Callback aufgerufen wurde
|
||||
_externalCallPollingTimer = setInterval(function() {
|
||||
$.ajax({
|
||||
url: '@Url.Action("CheckExternalCallStatus", "Main")',
|
||||
data: { callId: _externalCallId },
|
||||
type: 'GET',
|
||||
success: function(pollResult) {
|
||||
if (pollResult && pollResult.completed) {
|
||||
@@ -216,31 +214,53 @@
|
||||
});
|
||||
}, 3000);
|
||||
} else {
|
||||
// Fehler beim Ermitteln der URL
|
||||
document.getElementById('external-call-overlay').style.display = 'none';
|
||||
var errorMsg = (result && result.message) ? result.message : 'Die externe URL konnte nicht ermittelt werden.';
|
||||
showAlertMessageBox(errorMsg);
|
||||
_externalCallId = null;
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
document.getElementById('external-call-overlay').style.display = 'none';
|
||||
showAlertMessageBox('Fehler bei der Kommunikation mit dem Server.');
|
||||
_externalCallId = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openAiVoiceSettings() {
|
||||
// Aktuellen Modus im Einstellungs-Overlay vorauswählen
|
||||
$('input[name="aiVoiceMode"][value="' + _aiVoiceMode + '"]').prop('checked', true);
|
||||
document.getElementById('aivoice-settings-overlay').style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeAiVoiceSettings() {
|
||||
document.getElementById('aivoice-settings-overlay').style.display = 'none';
|
||||
}
|
||||
|
||||
function saveAiVoiceMode() {
|
||||
var selectedMode = $('input[name="aiVoiceMode"]:checked').val();
|
||||
$.ajax({
|
||||
url: '@Url.Action("SetAiVoiceMode", "Main")',
|
||||
data: { mode: selectedMode },
|
||||
type: 'POST',
|
||||
success: function() {
|
||||
_aiVoiceMode = selectedMode;
|
||||
closeAiVoiceSettings();
|
||||
cancelExternalCall();
|
||||
showMessagePopup('Einstellungen gespeichert', 'Die Änderungen wurden übernommen. Bitte starten Sie die Aufnahme erneut.');
|
||||
},
|
||||
error: function() {
|
||||
showAlertMessageBox('Fehler beim Speichern der Einstellung.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cancelExternalCall() {
|
||||
// callId merken BEVOR stopExternalCallPolling() sie auf null setzt
|
||||
var callIdToCancel = _externalCallId;
|
||||
stopExternalCallPolling();
|
||||
// Server informieren, dass abgebrochen wurde
|
||||
if (callIdToCancel) {
|
||||
$.ajax({
|
||||
url: '@Url.Action("CancelExternalCall", "Main")',
|
||||
data: { callId: callIdToCancel },
|
||||
type: 'POST'
|
||||
});
|
||||
}
|
||||
// Server informieren, dass abgebrochen wurde – callId liegt serverseitig in der Session
|
||||
$.ajax({
|
||||
url: '@Url.Action("CancelExternalCall", "Main")',
|
||||
type: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
function stopExternalCallPolling() {
|
||||
@@ -248,7 +268,6 @@
|
||||
clearInterval(_externalCallPollingTimer);
|
||||
_externalCallPollingTimer = null;
|
||||
}
|
||||
_externalCallId = null;
|
||||
document.getElementById('external-call-overlay').style.display = 'none';
|
||||
}
|
||||
|
||||
@@ -719,16 +738,46 @@
|
||||
@Html.Partial("RestoreServiceRecordInputsPartial", Model)
|
||||
</div>
|
||||
|
||||
@* Waiting-Overlay für externen Aufruf *@
|
||||
@* Warte-Overlay für externen Aufruf *@
|
||||
<div id="external-call-overlay" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.6); z-index:9999; justify-content:center; align-items:center;">
|
||||
<div style="background:white; border-radius:8px; padding:30px; text-align:center; box-shadow:0 4px 20px rgba(0,0,0,0.3);">
|
||||
<div style="background:white; border-radius:8px; padding:30px; min-width:300px; text-align:center; box-shadow:0 4px 20px rgba(0,0,0,0.3);">
|
||||
<div class="mb-3">
|
||||
<i class="fa fa-spinner fa-spin fa-2x"></i>
|
||||
</div>
|
||||
<p class="mb-3" id="external-call-status-text">Warte auf Rückmeldung vom externen System...</p>
|
||||
<p class="mb-3" id="external-call-status-text" style="white-space: pre-line">Bitte warten...</p>
|
||||
<button type="button" class="btn btn-secondary mr-2" onclick="openAiVoiceSettings();">
|
||||
<i class="fa fa-cog mr-1"></i>Einstellungen
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" onclick="cancelExternalCall();">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Einstellungs-Overlay für Moduswahl *@
|
||||
<div id="aivoice-settings-overlay" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.7); z-index:10000; justify-content:center; align-items:center;">
|
||||
<div style="background:white; border-radius:8px; padding:30px; max-width:480px; box-shadow:0 4px 20px rgba(0,0,0,0.3);">
|
||||
<h6 class="mb-3">Modus für Spracheingabe</h6>
|
||||
<div class="form-group text-left mb-4">
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="radio" name="aiVoiceMode" id="aiVoiceModeDirect" value="0" />
|
||||
<label class="form-check-label font-weight-bold" for="aiVoiceModeDirect">
|
||||
Spracheingabe direkt aus Mobilklient starten
|
||||
</label>
|
||||
<small class="form-text text-muted">Diese Option setzt voraus, dass Mobilklient und ownChat auf gleichem Endgerät genutzt werden</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="aiVoiceMode" id="aiVoiceModePush" value="1" />
|
||||
<label class="form-check-label font-weight-bold" for="aiVoiceModePush">
|
||||
Spracheingabe per Push-Nachricht starten
|
||||
</label>
|
||||
<small class="form-text text-muted">Diese Option ermöglicht die Nutzung von Mobilklient und ownChat auf getrennten Endgeräten wie bei der Vollversion</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button type="button" class="btn btn-secondary mr-2" onclick="closeAiVoiceSettings();">Schließen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveAiVoiceMode();">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@* /Einzelbuchungsformular *@
|
||||
|
||||
@@ -28,24 +28,22 @@ namespace BeWo.Service.AI
|
||||
avd = new AiVoiceData();
|
||||
avd.Token = "Test";
|
||||
avd.StatusMessage = "1";
|
||||
avd.ResultUrl = "https://example.com/";
|
||||
avd.OwnChatUrl = null; // "ownchat://voice/?token=test";
|
||||
|
||||
return avd;
|
||||
#endif
|
||||
|
||||
avd = GetTranskriptionToken(tenant, sprache);
|
||||
|
||||
|
||||
if (!String.IsNullOrEmpty(avd.Token))
|
||||
{
|
||||
avd.OwnChatUrl = String.Format("ownchat://voice/?token={0}", avd.Token);
|
||||
|
||||
|
||||
|
||||
if (sendOwnChatMessage)
|
||||
{
|
||||
var msg = String.Format("Bitte klicken Sie auf folgenden Link um die Aufnahme zu starten:\n\n{0}", avd.OwnChatUrl);
|
||||
OwnChatHelper.SendOwnChatMessage(msg, UserRightHelper.GetLoggedInUser().Employee);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return avd;
|
||||
|
||||
@@ -144,5 +144,6 @@ namespace BS.Shared.Core
|
||||
/// Aktiviert AI Einstellungen (Modell, ...)
|
||||
/// </summary>
|
||||
public const string ModuleAiSettingsEnabled = "ModuleAiSettingsEnabled";
|
||||
public const string AiVoiceMode = "AiVoiceMode"; // 0 = direct; 1 = Link zu ownChat senden
|
||||
}
|
||||
}
|
||||
|
||||
28
Shared/DataContracts/InactivePersonDC.cs
Normal file
28
Shared/DataContracts/InactivePersonDC.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
using BS.Shared.Core;
|
||||
using BS.Shared.Extensions;
|
||||
using BS.Shared.Interface.Abstract;
|
||||
|
||||
namespace BS.Shared.DataContracts
|
||||
{
|
||||
[DataContract]
|
||||
public class InactivePersonDC : IDataContract
|
||||
{
|
||||
[DataMember] public string FirstName { get; set; }
|
||||
[DataMember] public string LastName { get; set; }
|
||||
[DataMember] public DateTime? DateOfBirth { get; set; }
|
||||
[DataMember] public int PersonType { get; set; }
|
||||
[DataMember] public string Street { get; set; }
|
||||
[DataMember] public string PostalCode { get; set; }
|
||||
[DataMember] public string Town { get; set; }
|
||||
[DataMember] public DateTime? LastUsedOn { get; set; }
|
||||
[DataMember] public string Notice { get; set; }
|
||||
[DataMember] public long? PersonOid { get; set; }
|
||||
[DataMember] public long? ObjectOid { get; set; }
|
||||
[DataMember] public long? ObjectTid { get; set; }
|
||||
[DataMember] public bool IsChecked { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user