Ergebnisse 1-3 + Typs Debug
This commit is contained in:
997
Ergebnisse/Ergebnisse 03/USE_CASES_NEW_IMPLEMENTATION_GUIDE.md
Normal file
997
Ergebnisse/Ergebnisse 03/USE_CASES_NEW_IMPLEMENTATION_GUIDE.md
Normal file
@@ -0,0 +1,997 @@
|
||||
# Implementierungs-Leitfaden: Asset Management & Scheduling Modules
|
||||
|
||||
> **Zielgruppe**: C# Entwickler, WPF/MVVM Architekten
|
||||
> **Schwerpunkt**: Praktische Umsetzung der neuen Module
|
||||
> **Basis**: USE_CASES_NEW.md + USE_CASES_NEW_GUI_MAPPING.md
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Schnelleinstieg
|
||||
|
||||
### Verzeichnis-Struktur nach Fertigstellung
|
||||
|
||||
```
|
||||
src/
|
||||
├── centron/Centron.WPF.UI/Modules/
|
||||
│ ├── Administration/
|
||||
│ │ ├── AssetManagement/ [NEU]
|
||||
│ │ │ ├── AssetManagementAppModuleController.cs
|
||||
│ │ │ ├── AssetManagementView.xaml
|
||||
│ │ │ ├── ViewModels/
|
||||
│ │ │ │ ├── AssetInventoryViewModel.cs
|
||||
│ │ │ │ ├── AssetDetailsViewModel.cs
|
||||
│ │ │ │ ├── AssetLifecycleViewModel.cs
|
||||
│ │ │ │ ├── AssetBatchImportViewModel.cs
|
||||
│ │ │ │ ├── PatchManagementViewModel.cs
|
||||
│ │ │ │ ├── SnmpMonitoringViewModel.cs
|
||||
│ │ │ │ └── LicenseManagementViewModel.cs
|
||||
│ │ │ └── Views/
|
||||
│ │ │ ├── AssetInventoryView.xaml
|
||||
│ │ │ ├── AssetDetailsView.xaml
|
||||
│ │ │ ├── AssetLifecycleView.xaml
|
||||
│ │ │ ├── PatchManagementView.xaml
|
||||
│ │ │ ├── SnmpMonitoringView.xaml
|
||||
│ │ │ └── LicenseManagementView.xaml
|
||||
│ │
|
||||
│ └── Calendar/
|
||||
│ ├── AppointmentScheduling/ [NEU/ERWEITERT]
|
||||
│ │ ├── AppointmentSchedulingAppModuleController.cs
|
||||
│ │ ├── AppointmentSchedulingView.xaml
|
||||
│ │ ├── ViewModels/
|
||||
│ │ │ ├── AppointmentRequestViewModel.cs
|
||||
│ │ │ ├── AppointmentProposalViewModel.cs
|
||||
│ │ │ ├── AppointmentConfirmationViewModel.cs
|
||||
│ │ │ ├── TechnicianRoutePlannerViewModel.cs
|
||||
│ │ │ ├── CapacityPlanningViewModel.cs
|
||||
│ │ │ ├── SLAMonitoringViewModel.cs
|
||||
│ │ │ └── WizardPages/
|
||||
│ │ │ ├── AppointmentRequestWizardPageViewModel.cs
|
||||
│ │ │ ├── AppointmentProposalWizardPageViewModel.cs
|
||||
│ │ │ ├── AppointmentConfirmationWizardPageViewModel.cs
|
||||
│ │ └── Views/
|
||||
│ │ ├── AppointmentRequestView.xaml
|
||||
│ │ ├── AppointmentSlotSelectionView.xaml
|
||||
│ │ ├── RouteOptimizationView.xaml
|
||||
│ │ ├── CapacityPlanningView.xaml
|
||||
│ │ ├── SLAMonitoringView.xaml
|
||||
│ │ └── Wizard/
|
||||
│ │ ├── AppointmentBookingWizardView.xaml
|
||||
│ │
|
||||
│ └── RouteOptimization/ [NEU]
|
||||
│ ├── RouteOptimizationAppModuleController.cs
|
||||
│ ├── RouteOptimizationView.xaml
|
||||
│ └── ViewModels/
|
||||
│ ├── TechnicianRoutePlannerViewModel.cs
|
||||
│ ├── ZoneManagementViewModel.cs
|
||||
│ └── RouteMapViewModel.cs
|
||||
│
|
||||
└── backend/
|
||||
├── Centron.BL/
|
||||
│ ├── Sales/CustomerAssets/ [ERWEITERN]
|
||||
│ │ ├── AssetBL.cs (erweitern um 1.1-1.5)
|
||||
│ │ ├── AssetArticleBL.cs
|
||||
│ │ ├── AssetPatchBL.cs [NEU]
|
||||
│ │ ├── AssetSnmpMonitoringBL.cs [NEU]
|
||||
│ │ ├── AssetLicenseBL.cs [NEU]
|
||||
│ │ └── AssetDepreciationBL.cs [NEU]
|
||||
│ │
|
||||
│ ├── AppointmentRequests/ [ERWEITERN]
|
||||
│ │ ├── AppointmentRequestBL.cs (erweitern um 17.1)
|
||||
│ │ ├── AppointmentProposalBL.cs [NEU]
|
||||
│ │ ├── TechnicianRouteBL.cs [NEU]
|
||||
│ │ ├── CapacityPlanningBL.cs [NEU]
|
||||
│ │ └── SLAAgreementBL.cs [NEU]
|
||||
│ │
|
||||
│ └── MyCentron/Schedulings/ [ERWEITERN]
|
||||
│ ├── SchedulingBL.cs (erweitern um Kapazität)
|
||||
│ ├── ScheduleOptimizationBL.cs [NEU]
|
||||
│ └── ScheduleAlertingBL.cs [NEU]
|
||||
│
|
||||
├── Centron.Interfaces/
|
||||
│ ├── Administration/
|
||||
│ │ ├── AssetManagement/ [NEU]
|
||||
│ │ │ ├── IAssetManagementLogic.cs
|
||||
│ │ │ ├── IAssetPatchLogic.cs
|
||||
│ │ │ ├── IAssetSnmpMonitoringLogic.cs
|
||||
│ │ │ └── IAssetLicenseLogic.cs
|
||||
│ │ │
|
||||
│ │ └── Scheduling/ [NEU]
|
||||
│ │ ├── IAppointmentSchedulingLogic.cs
|
||||
│ │ ├── ITechnicianRoutingLogic.cs
|
||||
│ │ ├── ICapacityPlanningLogic.cs
|
||||
│ │ └── ISLAAgreementLogic.cs
|
||||
│ │
|
||||
│ └── WebServiceBL/ [ERWEITERN]
|
||||
│ ├── AssetManagementWebServiceBL.cs [NEU]
|
||||
│ └── SchedulingWebServiceBL.cs [NEU]
|
||||
│
|
||||
├── Centron.Entities/Entities/
|
||||
│ ├── Administration/MasterData/
|
||||
│ │ ├── AssetCondition.cs (existiert)
|
||||
│ │ ├── AssetManagementDevice.cs [NEU - optional]
|
||||
│ │ └── AssetManagementPatch.cs [NEU - optional]
|
||||
│ │
|
||||
│ ├── AppointmentRequests/ [EXISTIERT]
|
||||
│ │ ├── AppointmentRequest.cs
|
||||
│ │ ├── AppointmentProposal.cs
|
||||
│ │ └── AppointmentRequestFilter.cs
|
||||
│ │
|
||||
│ ├── Sales/CustomerAssets/ [EXISTIERT]
|
||||
│ │ ├── Asset.cs
|
||||
│ │ └── AssetBase.cs
|
||||
│ │
|
||||
│ └── MyCentron/Schedulings/ [EXISTIERT]
|
||||
│ ├── Scheduling.cs
|
||||
│ └── SchedulingFilter.cs
|
||||
│
|
||||
└── Centron.DAO/
|
||||
└── Repositories/
|
||||
├── Sales/Customers/Assets/ [EXISTIERT]
|
||||
└── AppointmentRequests/ [EXISTIERT]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ ViewModel-Template für Asset Management
|
||||
|
||||
```csharp
|
||||
// File: src/centron/Centron.WPF.UI/Modules/Administration/AssetManagement/ViewModels/AssetInventoryViewModel.cs
|
||||
// UTF-8 with BOM encoding required!
|
||||
|
||||
using Centron.Interfaces.BL;
|
||||
using Centron.Common;
|
||||
using Prism.Mvvm;
|
||||
using Prism.Commands;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using DevExpress.Mvvm;
|
||||
using Centron.Data.Entities.Sales.CustomerAssets;
|
||||
using Centron.WPF.UI.Common;
|
||||
using Centron.WPF.UI.Services.Logics;
|
||||
|
||||
namespace Centron.WPF.UI.Modules.Administration.AssetManagement.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel für Asset-Inventarverwaltung (UC 1.1 - 1.5)
|
||||
///
|
||||
/// Use Cases:
|
||||
/// - UC 1.1: Neue IT-Geräte erfassen
|
||||
/// - UC 1.2: Geräte-Details anzeigen und bearbeiten
|
||||
/// - UC 1.3: Geräte-Bestände nach Abteilung verwalten
|
||||
/// - UC 1.4: Geräte-Lebenszyklusmanagement
|
||||
/// - UC 1.5: Batch-Import von Geräteinventaren
|
||||
///
|
||||
/// ViewModel Properties → Use Cases:
|
||||
/// DeviceName → UC 1.1 TextEdit
|
||||
/// SelectedDeviceType → UC 1.1 ComboBoxEdit
|
||||
/// SelectedDepartmentI3D → UC 1.3 Filter
|
||||
/// SelectedStatus → UC 1.4 Lifecycle
|
||||
/// </summary>
|
||||
public class AssetInventoryViewModel : BindableBase
|
||||
{
|
||||
#region Dependencies
|
||||
private readonly IAssetManagementLogic _assetLogic;
|
||||
private readonly IMessageDialogService _messageService;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private string _deviceName;
|
||||
private int _selectedDeviceType;
|
||||
private string _manufacturer;
|
||||
private string _modelNumber;
|
||||
private string _serialNumber;
|
||||
private string _assetTag;
|
||||
private int _selectedDepartmentI3D;
|
||||
private int _selectedLocationI3D;
|
||||
private int _selectedStatus;
|
||||
private bool _isLoading;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Gerätname eingeben
|
||||
/// </summary>
|
||||
public string DeviceName
|
||||
{
|
||||
get => _deviceName;
|
||||
set => this.SetProperty(ref _deviceName, value, nameof(DeviceName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Gerättyp auswählen
|
||||
/// </summary>
|
||||
public int SelectedDeviceType
|
||||
{
|
||||
get => _selectedDeviceType;
|
||||
set => this.SetProperty(ref _selectedDeviceType, value, nameof(SelectedDeviceType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Hersteller angeben
|
||||
/// </summary>
|
||||
public string Manufacturer
|
||||
{
|
||||
get => _manufacturer;
|
||||
set => this.SetProperty(ref _manufacturer, value, nameof(Manufacturer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Modellnummer eingeben
|
||||
/// </summary>
|
||||
public string ModelNumber
|
||||
{
|
||||
get => _modelNumber;
|
||||
set => this.SetProperty(ref _modelNumber, value, nameof(ModelNumber));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Seriennummer (eindeutig!)
|
||||
/// </summary>
|
||||
public string SerialNumber
|
||||
{
|
||||
get => _serialNumber;
|
||||
set => this.SetProperty(ref _serialNumber, value, nameof(SerialNumber));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Asset-Tag (auto-generated)
|
||||
/// </summary>
|
||||
public string AssetTag
|
||||
{
|
||||
get => _assetTag;
|
||||
set => this.SetProperty(ref _assetTag, value, nameof(AssetTag));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.3: Abteilung filtern
|
||||
/// </summary>
|
||||
public int SelectedDepartmentI3D
|
||||
{
|
||||
get => _selectedDepartmentI3D;
|
||||
set => this.SetProperty(ref _selectedDepartmentI3D, value, nameof(SelectedDepartmentI3D));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Standort zuordnen
|
||||
/// </summary>
|
||||
public int SelectedLocationI3D
|
||||
{
|
||||
get => _selectedLocationI3D;
|
||||
set => this.SetProperty(ref _selectedLocationI3D, value, nameof(SelectedLocationI3D));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.4: Status / Lifecycle Stage
|
||||
/// </summary>
|
||||
public int SelectedStatus
|
||||
{
|
||||
get => _selectedStatus;
|
||||
set => this.SetProperty(ref _selectedStatus, value, nameof(SelectedStatus));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI-Status: Loading indicator
|
||||
/// </summary>
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
set => this.SetProperty(ref _isLoading, value, nameof(IsLoading));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.3: Assets-Grid Datenquelle
|
||||
/// </summary>
|
||||
public ObservableCollection<AssetListItemViewModel> Assets { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.3: Abteilungs-Dropdown Datenquelle
|
||||
/// </summary>
|
||||
public ObservableCollection<DepartmentViewModel> Departments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Locations-Dropdown
|
||||
/// </summary>
|
||||
public ObservableCollection<LocationViewModel> Locations { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Neues Gerät erstellen
|
||||
/// </summary>
|
||||
public ICommand CreateNewAssetCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Asset-Tag auto-generieren
|
||||
/// </summary>
|
||||
public ICommand GenerateAssetTagCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Gerät speichern
|
||||
/// </summary>
|
||||
public ICommand SaveAssetCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.5: Batch-Import starten
|
||||
/// </summary>
|
||||
public ICommand OpenBatchImportCommand { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.3: Filter anwenden
|
||||
/// </summary>
|
||||
public ICommand FilterCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public AssetInventoryViewModel()
|
||||
{
|
||||
// Dependency Injection (from ClassContainer or direct DI)
|
||||
_assetLogic = ClassContainer.Instance
|
||||
.GetInstance<IAssetManagementLogic>();
|
||||
|
||||
_messageService = ClassContainer.Instance
|
||||
.GetInstance<IMessageDialogService>();
|
||||
|
||||
// Collections
|
||||
this.Assets = new ObservableCollection<AssetListItemViewModel>();
|
||||
this.Departments = new ObservableCollection<DepartmentViewModel>();
|
||||
this.Locations = new ObservableCollection<LocationViewModel>();
|
||||
|
||||
// Initialize Commands
|
||||
this.CreateNewAssetCommand = new DelegateCommand(this.CreateNewAsset);
|
||||
this.GenerateAssetTagCommand = new DelegateCommand(this.GenerateAssetTag);
|
||||
this.SaveAssetCommand = new DelegateCommand(this.SaveAsset, this.CanSaveAsset);
|
||||
this.OpenBatchImportCommand = new DelegateCommand(this.OpenBatchImport);
|
||||
this.FilterCommand = new DelegateCommand(this.ApplyFilter);
|
||||
|
||||
// Load initial data
|
||||
this.LoadInitialData();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Neues Asset-Objekt initialisieren
|
||||
/// </summary>
|
||||
private void CreateNewAsset()
|
||||
{
|
||||
// Clear form for new entry
|
||||
this.DeviceName = string.Empty;
|
||||
this.Manufacturer = string.Empty;
|
||||
this.ModelNumber = string.Empty;
|
||||
this.SerialNumber = string.Empty;
|
||||
this.SelectedDeviceType = 0;
|
||||
this.SelectedLocationI3D = 0;
|
||||
this.SelectedStatus = 1; // Active
|
||||
|
||||
this._messageService.ShowMessage("Neues Gerät bereit zur Eingabe", "Info");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Asset-Tag auto-generieren
|
||||
/// </summary>
|
||||
private void GenerateAssetTag()
|
||||
{
|
||||
// Pattern: AST-YYYYMMDD-XXX
|
||||
string prefix = "AST-" + DateTime.Now.ToString("yyyyMMdd");
|
||||
string randomPart = new Random().Next(100, 999).ToString();
|
||||
this.AssetTag = $"{prefix}-{randomPart}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Gerät speichern
|
||||
/// </summary>
|
||||
private void SaveAsset()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.IsLoading = true;
|
||||
|
||||
// Validation
|
||||
if (string.IsNullOrWhiteSpace(this.DeviceName))
|
||||
{
|
||||
this._messageService.ShowError("Gerätname erforderlich", "Validierung");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(this.SerialNumber))
|
||||
{
|
||||
this._messageService.ShowError("Seriennummer erforderlich", "Validierung");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call BL
|
||||
var result = this._assetLogic.CreateNewAsset(new CreateAssetRequest
|
||||
{
|
||||
DeviceName = this.DeviceName,
|
||||
DeviceType = (AssetDeviceType)this.SelectedDeviceType,
|
||||
Manufacturer = this.Manufacturer,
|
||||
ModelNumber = this.ModelNumber,
|
||||
SerialNumber = this.SerialNumber,
|
||||
AssetTag = this.AssetTag,
|
||||
DepartmentI3D = this.SelectedDepartmentI3D,
|
||||
LocationI3D = this.SelectedLocationI3D,
|
||||
Status = (AssetStatus)this.SelectedStatus
|
||||
});
|
||||
|
||||
if (result.Status == ResultStatus.Success)
|
||||
{
|
||||
this._messageService.ShowMessage($"Gerät gespeichert: {result.Data.I3D}", "Erfolg");
|
||||
this.CreateNewAsset(); // Reset form
|
||||
this.ApplyFilter(); // Refresh list
|
||||
}
|
||||
else
|
||||
{
|
||||
this._messageService.ShowError(result.Error, "Fehler");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this._messageService.ShowError($"Fehler beim Speichern: {ex.Message}", "Fehler");
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: CanExecute für SaveCommand
|
||||
/// </summary>
|
||||
private bool CanSaveAsset()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(this.DeviceName)
|
||||
&& !string.IsNullOrWhiteSpace(this.SerialNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.5: Batch-Import öffnen
|
||||
/// </summary>
|
||||
private void OpenBatchImport()
|
||||
{
|
||||
// Open wizard view
|
||||
var wizard = new AssetBatchImportWizardView();
|
||||
// ... present wizard dialog
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.3: Filter anwenden
|
||||
/// </summary>
|
||||
private async void ApplyFilter()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.IsLoading = true;
|
||||
|
||||
var result = await this._assetLogic.GetAssetsByDepartmentAsync(
|
||||
this.SelectedDepartmentI3D,
|
||||
(AssetStatus?)this.SelectedStatus
|
||||
);
|
||||
|
||||
if (result.Status == ResultStatus.Success)
|
||||
{
|
||||
this.Assets.Clear();
|
||||
foreach (var asset in result.Data)
|
||||
{
|
||||
this.Assets.Add(new AssetListItemViewModel
|
||||
{
|
||||
I3D = asset.I3D,
|
||||
DeviceName = asset.DeviceName,
|
||||
AssetTag = asset.AssetTag,
|
||||
Status = asset.Status.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load initial data (departments, locations, etc.)
|
||||
/// </summary>
|
||||
private async void LoadInitialData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Load departments
|
||||
var deptResult = await this._assetLogic.GetDepartmentsAsync();
|
||||
if (deptResult.Status == ResultStatus.Success)
|
||||
{
|
||||
this.Departments.Clear();
|
||||
foreach (var dept in deptResult.Data)
|
||||
{
|
||||
this.Departments.Add(new DepartmentViewModel { I3D = dept.I3D, Name = dept.Name });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this._messageService.ShowError($"Fehler beim Laden: {ex.Message}", "Fehler");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ ViewModel-Template für Scheduling
|
||||
|
||||
```csharp
|
||||
// File: src/centron/Centron.WPF.UI/Modules/Calendar/AppointmentScheduling/ViewModels/AppointmentRequestViewModel.cs
|
||||
|
||||
using Prism.Mvvm;
|
||||
using Prism.Commands;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Centron.Interfaces.BL;
|
||||
using Centron.Common;
|
||||
|
||||
namespace Centron.WPF.UI.Modules.Calendar.AppointmentScheduling.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel für Terminanfragen (UC 17.1)
|
||||
/// Use Cases:
|
||||
/// - UC 17.1.1: Kunde fordert Termin an
|
||||
/// - UC 17.1.2: System schlägt verfügbare Slots vor
|
||||
/// - UC 17.1.3: Kunde wählt Termin-Slot
|
||||
/// - UC 17.1.4: Terminbestätigung an Kunde
|
||||
/// - UC 17.1.5: Termine verschieben/absagen
|
||||
/// </summary>
|
||||
public class AppointmentRequestViewModel : BindableBase
|
||||
{
|
||||
#region Dependencies
|
||||
private readonly IAppointmentSchedulingLogic _appointmentLogic;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private int _ticketI3D;
|
||||
private int _selectedServiceType;
|
||||
private DateTime _preferredDateFrom;
|
||||
private DateTime _preferredDateTo;
|
||||
private int _estimatedDurationInHours;
|
||||
private string _locationAddress;
|
||||
private string _customerNotes;
|
||||
private string _appointmentStatus;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Hidden - aus Ticket Context
|
||||
/// </summary>
|
||||
public int TicketI3D
|
||||
{
|
||||
get => _ticketI3D;
|
||||
set => this.SetProperty(ref _ticketI3D, value, nameof(TicketI3D));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Service-Typ (Installation/Wartung/Reparatur)
|
||||
/// </summary>
|
||||
public int SelectedServiceType
|
||||
{
|
||||
get => _selectedServiceType;
|
||||
set => this.SetProperty(ref _selectedServiceType, value, nameof(SelectedServiceType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Gewünschtes Startdatum
|
||||
/// </summary>
|
||||
public DateTime PreferredDateFrom
|
||||
{
|
||||
get => _preferredDateFrom;
|
||||
set => this.SetProperty(ref _preferredDateFrom, value, nameof(PreferredDateFrom));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Gewünschtes Enddatum
|
||||
/// </summary>
|
||||
public DateTime PreferredDateTo
|
||||
{
|
||||
get => _preferredDateTo;
|
||||
set => this.SetProperty(ref _preferredDateTo, value, nameof(PreferredDateTo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Geschätzte Dauer in Stunden
|
||||
/// </summary>
|
||||
public int EstimatedDurationInHours
|
||||
{
|
||||
get => _estimatedDurationInHours;
|
||||
set => this.SetProperty(ref _estimatedDurationInHours, value, nameof(EstimatedDurationInHours));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Ort des Termins
|
||||
/// </summary>
|
||||
public string LocationAddress
|
||||
{
|
||||
get => _locationAddress;
|
||||
set => this.SetProperty(ref _locationAddress, value, nameof(LocationAddress));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Kundennotizen
|
||||
/// </summary>
|
||||
public string CustomerNotes
|
||||
{
|
||||
get => _customerNotes;
|
||||
set => this.SetProperty(ref _customerNotes, value, nameof(CustomerNotes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.2: Verfügbare Termine
|
||||
/// </summary>
|
||||
public ObservableCollection<AppointmentSlotViewModel> ProposedSlots { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Aktueller Status der Anfrage
|
||||
/// </summary>
|
||||
public string AppointmentStatus
|
||||
{
|
||||
get => _appointmentStatus;
|
||||
set => this.SetProperty(ref _appointmentStatus, value, nameof(AppointmentStatus));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
public ICommand RequestAppointmentCommand { get; private set; }
|
||||
public ICommand SelectSlotCommand { get; private set; }
|
||||
public ICommand ConfirmAppointmentCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public AppointmentRequestViewModel()
|
||||
{
|
||||
this._appointmentLogic = ClassContainer.Instance
|
||||
.GetInstance<IAppointmentSchedulingLogic>();
|
||||
|
||||
this.ProposedSlots = new ObservableCollection<AppointmentSlotViewModel>();
|
||||
|
||||
// Initialize Commands
|
||||
this.RequestAppointmentCommand = new DelegateCommand(this.RequestAppointment);
|
||||
this.SelectSlotCommand = new DelegateCommand<AppointmentSlotViewModel>(this.SelectSlot);
|
||||
this.ConfirmAppointmentCommand = new DelegateCommand(this.ConfirmAppointment);
|
||||
|
||||
// Default values
|
||||
this.PreferredDateFrom = DateTime.Now;
|
||||
this.PreferredDateTo = DateTime.Now.AddDays(7);
|
||||
this.EstimatedDurationInHours = 2;
|
||||
this.AppointmentStatus = "Bereit für Anfrage";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.1: Anfrage einreichen und Slots vorschlagen
|
||||
/// </summary>
|
||||
private async void RequestAppointment()
|
||||
{
|
||||
try
|
||||
{
|
||||
// UC 17.1.2: Get available slots
|
||||
var result = await this._appointmentLogic.GetAvailableAppointmentSlotsAsync(
|
||||
this.TicketI3D,
|
||||
this.PreferredDateFrom,
|
||||
this.PreferredDateTo,
|
||||
this.EstimatedDurationInHours
|
||||
);
|
||||
|
||||
if (result.Status == ResultStatus.Success)
|
||||
{
|
||||
this.ProposedSlots.Clear();
|
||||
foreach (var slot in result.Data)
|
||||
{
|
||||
this.ProposedSlots.Add(new AppointmentSlotViewModel
|
||||
{
|
||||
SlotDateTime = slot.StartTime,
|
||||
TechnicianName = slot.TechnicianName,
|
||||
TravelTime = $"{slot.TravelTimeMinutes} Min",
|
||||
ProposalI3D = slot.I3D
|
||||
});
|
||||
}
|
||||
|
||||
this.AppointmentStatus = $"✓ {this.ProposedSlots.Count} Slots verfügbar";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.AppointmentStatus = $"✗ Fehler: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.3: Slot auswählen
|
||||
/// </summary>
|
||||
private void SelectSlot(AppointmentSlotViewModel slot)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
this.AppointmentStatus = $"Slot ausgewählt: {slot.SlotDateTime:dd.MM.yyyy HH:mm}";
|
||||
// Store selected slot for confirmation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 17.1.4: Termin bestätigen
|
||||
/// </summary>
|
||||
private async void ConfirmAppointment()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Call BookAppointment logic
|
||||
this.AppointmentStatus = "✓ Termin bestätigt!";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.AppointmentStatus = $"✗ Fehler: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 REST API Endpoints Template
|
||||
|
||||
```csharp
|
||||
// File: src/webservice/Centron.WebServices.Core/RestService/CentronRestService.cs
|
||||
// Add these methods to existing service:
|
||||
|
||||
[WebInvoke(Method = "POST", UriTemplate = "AssetManagement/GetDeviceDetails")]
|
||||
[Authenticate]
|
||||
public Response<AssetDetailsDTO> GetAssetDeviceDetails(Request<int> request)
|
||||
{
|
||||
// UC 1.2: Get asset details
|
||||
try
|
||||
{
|
||||
var result = this.classContainer
|
||||
.WithInstance((IAssetManagementLogic logic) => logic.GetAssetDetails(request.Data))
|
||||
.ThrowIfError();
|
||||
|
||||
return Response.Success(result.Data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Response.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[WebInvoke(Method = "POST", UriTemplate = "AssetManagement/ImportBatch")]
|
||||
[Authenticate]
|
||||
public Response<BatchImportResultDTO> ImportAssetBatch(Request<AssetBatchImportRequest> request)
|
||||
{
|
||||
// UC 1.5: Batch import
|
||||
try
|
||||
{
|
||||
var result = this.classContainer
|
||||
.WithInstance((IAssetManagementLogic logic) => logic.ImportAssetsBatch(
|
||||
request.Data.FileContent,
|
||||
request.Data.Format))
|
||||
.ThrowIfError();
|
||||
|
||||
return Response.Success(result.Data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Response.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[WebInvoke(Method = "POST", UriTemplate = "Scheduling/GetAvailableAppointments")]
|
||||
[Authenticate]
|
||||
public Response<List<AppointmentSlotDTO>> GetAvailableAppointments(Request<AppointmentRequestFilter> request)
|
||||
{
|
||||
// UC 17.1.2: Get available slots
|
||||
try
|
||||
{
|
||||
var result = this.classContainer
|
||||
.WithInstance((IAppointmentSchedulingLogic logic) => logic.GetAvailableAppointmentSlots(
|
||||
request.Data.TicketI3D,
|
||||
request.Data.PreferredDateFrom,
|
||||
request.Data.PreferredDateTo,
|
||||
request.Data.DurationHours))
|
||||
.ThrowIfError();
|
||||
|
||||
return Response.Success(result.Data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Response.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[WebInvoke(Method = "POST", UriTemplate = "Scheduling/BookAppointment")]
|
||||
[Authenticate]
|
||||
public Response<AppointmentConfirmationDTO> BookAppointment(Request<int> request)
|
||||
{
|
||||
// UC 17.1.3: Book appointment
|
||||
try
|
||||
{
|
||||
var result = this.classContainer
|
||||
.WithInstance((IAppointmentSchedulingLogic logic) => logic.BookAppointment(request.Data))
|
||||
.ThrowIfError();
|
||||
|
||||
return Response.Success(result.Data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Response.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[WebInvoke(Method = "POST", UriTemplate = "Scheduling/OptimizeRoute")]
|
||||
[Authenticate]
|
||||
public Response<OptimizedRouteDTO> OptimizeRoute(Request<RouteOptimizationRequest> request)
|
||||
{
|
||||
// UC 17.2: Route optimization
|
||||
try
|
||||
{
|
||||
var result = this.classContainer
|
||||
.WithInstance((ITechnicianRoutingLogic logic) => logic.OptimizeRoute(
|
||||
request.Data.TechnicianI3D,
|
||||
request.Data.Date))
|
||||
.ThrowIfError();
|
||||
|
||||
return Response.Success(result.Data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Response.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[WebInvoke(Method = "POST", UriTemplate = "Scheduling/GetSLAStatus")]
|
||||
[Authenticate]
|
||||
public Response<SLAStatusDTO> GetSLAStatus(Request<int> request)
|
||||
{
|
||||
// UC 17.4: Get SLA status
|
||||
try
|
||||
{
|
||||
var result = this.classContainer
|
||||
.WithInstance((ISLAAgreementLogic logic) => logic.GetSLAStatus(request.Data))
|
||||
.ThrowIfError();
|
||||
|
||||
return Response.Success(result.Data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Response.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Unit Test Template
|
||||
|
||||
```csharp
|
||||
// File: tests/Centron.Tests/BL/AssetManagementLogicTests.cs
|
||||
|
||||
[TestFixture]
|
||||
public class AssetManagementLogicTests
|
||||
{
|
||||
private AssetManagementLogic _logic;
|
||||
private Mock<IAssetBL> _assetBLMock;
|
||||
private Mock<DAOSession> _sessionMock;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_sessionMock = new Mock<DAOSession>();
|
||||
_assetBLMock = new Mock<IAssetBL>();
|
||||
|
||||
_logic = new AssetManagementLogic(_sessionMock.Object, _assetBLMock.Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Test creating new asset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CreateNewAsset_WithValidData_ReturnsSuccess()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CreateAssetRequest
|
||||
{
|
||||
DeviceName = "Test-Laptop",
|
||||
SerialNumber = "SN-123456",
|
||||
Manufacturer = "Dell"
|
||||
};
|
||||
|
||||
var expectedAsset = new Asset
|
||||
{
|
||||
I3D = 1,
|
||||
DeviceName = "Test-Laptop",
|
||||
SerialNumber = "SN-123456"
|
||||
};
|
||||
|
||||
_assetBLMock
|
||||
.Setup(bl => bl.CreateNewAsset(It.IsAny<CreateAssetRequest>()))
|
||||
.Returns(expectedAsset);
|
||||
|
||||
// Act
|
||||
var result = _logic.CreateNewAsset(request);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.Status, Is.EqualTo(ResultStatus.Success));
|
||||
Assert.That(result.Data.I3D, Is.EqualTo(1));
|
||||
Assert.That(result.Data.DeviceName, Is.EqualTo("Test-Laptop"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UC 1.1: Test validation - missing serial number
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CreateNewAsset_WithoutSerialNumber_ReturnsError()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CreateAssetRequest
|
||||
{
|
||||
DeviceName = "Test-Laptop",
|
||||
SerialNumber = "" // Missing!
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = _logic.CreateNewAsset(request);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.Status, Is.EqualTo(ResultStatus.Error));
|
||||
Assert.That(result.Error, Contains.Substring("Seriennummer"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Deployment Checklist
|
||||
|
||||
- [ ] **Code-Review** durchführen
|
||||
- [ ] **Unit Tests** schreiben und ausführen
|
||||
- [ ] **Integration Tests** mit echtem DB durchführen
|
||||
- [ ] **Code Analysis** (SonarQube/StyleCop) ausführen
|
||||
- [ ] **Performance Tests** durchführen
|
||||
- [ ] **UI-Tests** durchführen (manual)
|
||||
- [ ] **Ribbon-Integration** testen
|
||||
- [ ] **Localization** überprüfen (Deutsche Strings)
|
||||
- [ ] **Documentation** finalisieren
|
||||
- [ ] **Release Notes** schreiben
|
||||
- [ ] **Module Registration** in main app durchführen
|
||||
- [ ] **Build Pipeline** anpassen
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Nächste Schritte
|
||||
|
||||
1. **Code-Struktur erstellen** (Verzeichnisse, leere Dateien)
|
||||
2. **Templates ausfüllen** (Controller, ViewModel, View, BL)
|
||||
3. **Database-Access** via DAO implementieren
|
||||
4. **UI-Binding** in XAML durchführen
|
||||
5. **Commands** und Event-Handler implementieren
|
||||
6. **Unit Tests** schreiben
|
||||
7. **Integration Tests** durchführen
|
||||
8. **User Acceptance Testing** organisieren
|
||||
9. **Deployment** durchführen
|
||||
|
||||
---
|
||||
|
||||
**Status**: ✅ Implementierungs-Leitfaden abgeschlossen | Templates & Checklisten bereit
|
||||
|
||||
Reference in New Issue
Block a user