39 KiB
c-entron.NET - WPF Module Controller Implementations
Generiert: 2025-11-11 Zweck: Production-ready Controller implementations für Asset Management & Scheduling Pattern: ICentronAppModuleController, IRibbonControlModule, Prism Module Registration
Asset Management Module Controllers
16. AssetManagementAppModuleController.cs
Pfad: src/centron/Centron.WPF.UI/Modules/Administration/AssetManagement/AssetManagementAppModuleController.cs
Purpose: Main module controller for Asset Management module registration
using CentronSoftware.Centron.Core.Interfaces;
using CentronSoftware.Centron.Interfaces.Administration;
using CentronSoftware.Centron.WPF.UI.Extension.Controls;
using Prism.Ioc;
using Prism.Regions;
using System;
namespace CentronSoftware.Centron.WPF.UI.Modules.Administration.AssetManagement
{
/// <summary>
/// UC 16: Asset Management Module Controller
/// Verwaltet IT-Hardware-Bestände, Patch-Management, SNMP-Überwachung, Lizenzen, Compliance
/// </summary>
public class AssetManagementAppModuleController : ICentronAppModuleController, IRibbonControlModule
{
private readonly IRegionManager _regionManager;
private readonly IContainerProvider _containerProvider;
public string ModuleNameKey => "AssetManagement";
public string ModulePath => "Administration/AssetManagement";
public int RequestedRightI3D => UserRightsConst.Administration.ASSET_MANAGEMENT;
public bool IsActive { get; set; }
public AssetManagementAppModuleController(IRegionManager regionManager, IContainerProvider containerProvider)
{
this._regionManager = regionManager ?? throw new ArgumentNullException(nameof(regionManager));
this._containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
}
/// <summary>
/// UC 16: Initialize Asset Management Module
/// - Register ViewModels
/// - Register Views
/// - Register Business Logic
/// - Setup Ribbon Commands
/// </summary>
public void Initialize()
{
try
{
// Register ViewModels (Prism IoC)
this._containerProvider.Resolve<IContainerRegistry>()
.Register<AssetInventoryViewModel>()
.Register<AssetDetailsViewModel>()
.Register<AssetLifecycleViewModel>()
.Register<AssetBatchImportViewModel>()
.Register<PatchManagementViewModel>()
.Register<SNMPMonitoringViewModel>()
.Register<LicenseManagementViewModel>()
.Register<ComplianceDashboardViewModel>();
// Register Views
this._containerProvider.Resolve<IContainerRegistry>()
.Register<AssetInventoryView>()
.Register<PatchManagementView>()
.Register<SNMPMonitoringView>()
.Register<LicenseManagementView>()
.Register<ComplianceDashboardView>();
// Register Business Logic Layer
this._containerProvider.Resolve<IContainerRegistry>()
.Register<IAssetManagementLogic, BLAssetManagementLogic>();
// Register WebService Logic (for REST API calls)
this._containerProvider.Resolve<IContainerRegistry>()
.Register<IAssetManagementLogic, WSAssetManagementLogic>(nameof(CentronConnectionType.CentronWebServices));
this.IsActive = true;
}
catch (Exception ex)
{
// Log error
System.Diagnostics.Debug.WriteLine($"AssetManagement Module initialization failed: {ex.Message}");
this.IsActive = false;
throw;
}
}
/// <summary>
/// UC 16: Show main Asset Management view
/// </summary>
public void ShowModule()
{
try
{
var view = this._containerProvider.Resolve<AssetInventoryView>();
this._regionManager.AddToRegion(RegionNames.MainContentRegion, view);
this._regionManager.RequestNavigate(RegionNames.MainContentRegion, "AssetInventoryView");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Failed to show AssetManagement module: {ex.Message}");
}
}
/// <summary>
/// UC 16: Return Ribbon implementation for Asset Management
/// </summary>
public IRibbonTab GetRibbon()
{
return new AssetManagementRibbonController();
}
/// <summary>
/// UC 16: Cleanup when module is closed
/// </summary>
public void Cleanup()
{
// Dispose of resources
this.IsActive = false;
}
}
}
16.1 AssetInventoryViewModel.cs
Pfad: src/centron/Centron.WPF.UI/Modules/Administration/AssetManagement/ViewModels/AssetInventoryViewModel.cs
using CentronSoftware.Centron.Core.Interfaces;
using CentronSoftware.Centron.Interfaces.Administration;
using CentronSoftware.Centron.WPF.UI.Extension;
using CentronSoftware.Centron.WPF.UI.Resources;
using Prism.Commands;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace CentronSoftware.Centron.WPF.UI.Modules.Administration.AssetManagement.ViewModels
{
/// <summary>
/// UC 1.1-1.5: Device Inventory Management ViewModel
/// Hauptview für Geräte-Erfassung, Bearbeitung, Lebenszyklusmanagement
/// </summary>
public class AssetInventoryViewModel : BindableBase, IDisposable
{
private readonly IAssetManagementLogic _assetLogic;
private readonly IDepartmentLogic _departmentLogic;
private readonly ILocationLogic _locationLogic;
// UC 1.1: Properties for new asset creation
private string _deviceName;
private AssetDeviceType _selectedDeviceType;
private string _manufacturer;
private string _modelNumber;
private string _serialNumber;
private string _assetTag;
private int _selectedDepartmentI3D;
private int _selectedLocationI3D;
private AssetStatus _selectedStatus;
// Collections
private ObservableCollection<AssetItemViewModel> _assets;
private ObservableCollection<DepartmentViewModel> _departments;
private ObservableCollection<LocationViewModel> _locations;
private ObservableCollection<AssetDeviceType> _deviceTypes;
private ObservableCollection<AssetStatus> _statuses;
// Filter Properties
private string _searchText;
private AssetDeviceType _filterDeviceType;
private DepartmentViewModel _filterDepartment;
private AssetStatus _filterStatus;
// Selection
private AssetItemViewModel _selectedAsset;
// Loading State
private bool _isLoading;
// Commands
public DelegateCommand CreateNewAssetCommand { get; }
public DelegateCommand SaveAssetCommand { get; }
public DelegateCommand DeleteAssetCommand { get; }
public AsyncCommand ImportBatchCommand { get; }
public DelegateCommand ClearFiltersCommand { get; }
// UI Settings
public UISettings UISettings { get; set; } = new UISettings();
public AssetInventoryViewModel()
{
this._assetLogic = ClassContainer.Instance.GetInstance<IAssetManagementLogic>();
this._departmentLogic = ClassContainer.Instance.GetInstance<IDepartmentLogic>();
this._locationLogic = ClassContainer.Instance.GetInstance<ILocationLogic>();
// Initialize Collections
this.Assets = new ObservableCollection<AssetItemViewModel>();
this.Departments = new ObservableCollection<DepartmentViewModel>();
this.Locations = new ObservableCollection<LocationViewModel>();
this.DeviceTypes = new ObservableCollection<AssetDeviceType>();
this.Statuses = new ObservableCollection<AssetStatus>();
// Initialize Commands
this.CreateNewAssetCommand = new DelegateCommand(
execute: this.CreateNewAsset,
canExecute: () => !this.IsLoading
);
this.SaveAssetCommand = new DelegateCommand(
execute: this.SaveAsset,
canExecute: () => this.SelectedAsset != null && !this.IsLoading
);
this.DeleteAssetCommand = new DelegateCommand(
execute: this.DeleteAsset,
canExecute: () => this.SelectedAsset != null && !this.IsLoading
);
this.ImportBatchCommand = new AsyncCommand(
execute: this.ImportBatchAssets,
canExecute: () => !this.IsLoading
);
this.ClearFiltersCommand = new DelegateCommand(
execute: this.ClearFilters,
canExecute: () => !this.IsLoading
);
// Load initial data
_ = this.LoadInitialData();
}
// Properties
public string DeviceName
{
get => this._deviceName;
set => this.SetProperty(ref this._deviceName, value, nameof(this.DeviceName));
}
public AssetDeviceType SelectedDeviceType
{
get => this._selectedDeviceType;
set => this.SetProperty(ref this._selectedDeviceType, value, nameof(this.SelectedDeviceType));
}
public string Manufacturer
{
get => this._manufacturer;
set => this.SetProperty(ref this._manufacturer, value, nameof(this.Manufacturer));
}
public string ModelNumber
{
get => this._modelNumber;
set => this.SetProperty(ref this._modelNumber, value, nameof(this.ModelNumber));
}
public string SerialNumber
{
get => this._serialNumber;
set => this.SetProperty(ref this._serialNumber, value, nameof(this.SerialNumber));
}
public string AssetTag
{
get => this._assetTag;
set => this.SetProperty(ref this._assetTag, value, nameof(this.AssetTag));
}
public AssetItemViewModel SelectedAsset
{
get => this._selectedAsset;
set => this.SetProperty(ref this._selectedAsset, value, nameof(this.SelectedAsset));
}
public ObservableCollection<AssetItemViewModel> Assets
{
get => this._assets;
set => this.SetProperty(ref this._assets, value, nameof(this.Assets));
}
public string SearchText
{
get => this._searchText;
set
{
if (this.SetProperty(ref this._searchText, value, nameof(this.SearchText)))
{
this.RefreshAssetList();
}
}
}
public bool IsLoading
{
get => this._isLoading;
set => this.SetProperty(ref this._isLoading, value, nameof(this.IsLoading));
}
/// <summary>
/// UC 1.1: Load initial departments, locations, types, statuses
/// </summary>
private async Task LoadInitialData()
{
try
{
this.IsLoading = true;
// Load departments
var departmentsResult = await ClassContainer.Instance
.WithInstance((IDepartmentLogic logic) => logic.GetActiveDepartments())
.ThrowIfErrorAsync();
if (departmentsResult.IsSuccess)
{
this.Departments = new ObservableCollection<DepartmentViewModel>(
departmentsResult.Data.Select(d => new DepartmentViewModel(d))
);
}
// Load locations
var locationsResult = await ClassContainer.Instance
.WithInstance((ILocationLogic logic) => logic.GetActiveLocations())
.ThrowIfErrorAsync();
if (locationsResult.IsSuccess)
{
this.Locations = new ObservableCollection<LocationViewModel>(
locationsResult.Data.Select(l => new LocationViewModel(l))
);
}
// Initialize device types and statuses
this.DeviceTypes = new ObservableCollection<AssetDeviceType>(
Enum.GetValues(typeof(AssetDeviceType)).Cast<AssetDeviceType>()
);
this.Statuses = new ObservableCollection<AssetStatus>(
Enum.GetValues(typeof(AssetStatus)).Cast<AssetStatus>()
);
// Load assets
await this.RefreshAssetList();
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// UC 1.1: Create new asset (initializes form with default values)
/// </summary>
private void CreateNewAsset()
{
this.SelectedAsset = new AssetItemViewModel
{
AssetTag = this.GenerateAssetTag(),
Status = AssetStatus.Active,
Department = this.Departments.FirstOrDefault()
};
this.DeviceName = string.Empty;
this.SerialNumber = string.Empty;
}
/// <summary>
/// UC 1.1: Save asset to database
/// </summary>
private void SaveAsset()
{
if (this.SelectedAsset == null) return;
try
{
this.IsLoading = true;
var request = new CreateAssetRequest
{
DeviceName = this.SelectedAsset.DeviceName,
DeviceType = this.SelectedAsset.DeviceType,
SerialNumber = this.SelectedAsset.SerialNumber,
Manufacturer = this.SelectedAsset.Manufacturer,
ModelNumber = this.SelectedAsset.ModelNumber,
AssetTag = this.SelectedAsset.AssetTag,
DepartmentI3D = this.SelectedAsset.Department.I3D,
LocationI3D = this.SelectedAsset.Location.I3D,
Status = this.SelectedAsset.Status
};
var result = this._assetLogic.CreateOrUpdateAsset(request);
if (result.IsSuccess)
{
// Show success message
System.Windows.MessageBox.Show(
LocalizedStrings.OperationSuccessful,
LocalizedStrings.Success,
System.Windows.MessageBoxButton.OK
);
// Refresh list
_ = this.RefreshAssetList();
}
else
{
System.Windows.MessageBox.Show(
result.Error,
LocalizedStrings.Error,
System.Windows.MessageBoxButton.OK
);
}
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// UC 1.4: Delete asset (soft delete - sets IsDeleted flag)
/// </summary>
private void DeleteAsset()
{
if (this.SelectedAsset == null) return;
var confirm = System.Windows.MessageBox.Show(
LocalizedStrings.ConfirmDelete,
LocalizedStrings.Confirmation,
System.Windows.MessageBoxButton.YesNo
);
if (confirm != System.Windows.MessageBoxResult.Yes) return;
try
{
this.IsLoading = true;
var result = this._assetLogic.DeleteAsset(this.SelectedAsset.I3D);
if (result.IsSuccess)
{
this.Assets.Remove(this.SelectedAsset);
this.SelectedAsset = null;
}
else
{
System.Windows.MessageBox.Show(result.Error);
}
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// UC 1.5: Import batch of assets from CSV/Excel
/// </summary>
private async Task ImportBatchAssets()
{
// Open file dialog and import batch
var dialog = new System.Windows.Forms.OpenFileDialog
{
Filter = "CSV Files (*.csv)|*.csv|Excel Files (*.xlsx)|*.xlsx"
};
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
try
{
this.IsLoading = true;
var result = await this._assetLogic.ImportBatchAssetsAsync(dialog.FileName);
if (result.IsSuccess)
{
System.Windows.MessageBox.Show(
$"{result.Data} assets imported successfully",
"Import Complete"
);
await this.RefreshAssetList();
}
else
{
System.Windows.MessageBox.Show(result.Error);
}
}
finally
{
this.IsLoading = false;
}
}
}
/// <summary>
/// UC 1.1-1.3: Refresh asset list with applied filters
/// </summary>
private async Task RefreshAssetList()
{
try
{
var result = await ClassContainer.Instance
.WithInstance((IAssetManagementLogic logic) =>
logic.GetAssets(
searchText: this.SearchText,
deviceType: this.SelectedDeviceType,
status: this.SelectedStatus
)
)
.ThrowIfErrorAsync();
if (result.IsSuccess)
{
this.Assets = new ObservableCollection<AssetItemViewModel>(
result.Data.Select(a => new AssetItemViewModel(a))
);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error refreshing assets: {ex.Message}");
}
}
/// <summary>
/// UC 1.1: Generate unique asset tag
/// </summary>
private string GenerateAssetTag()
{
return $"ASSET-{DateTime.Now:yyyyMMdd}-{Guid.NewGuid().ToString().Substring(0, 6).ToUpper()}";
}
/// <summary>
/// Clear all filters and reload
/// </summary>
private void ClearFilters()
{
this.SearchText = string.Empty;
this.SelectedDeviceType = AssetDeviceType.Unknown;
this.SelectedStatus = AssetStatus.Unknown;
_ = this.RefreshAssetList();
}
/// <summary>
/// Cleanup resources
/// </summary>
public void Dispose()
{
ClassContainer.Instance.ReleaseInstance(this._assetLogic);
ClassContainer.Instance.ReleaseInstance(this._departmentLogic);
ClassContainer.Instance.ReleaseInstance(this._locationLogic);
}
}
}
Scheduling Module Controllers
17. SchedulingAppModuleController.cs
Pfad: src/centron/Centron.WPF.UI/Modules/Helpdesk/Scheduling/SchedulingAppModuleController.cs
using CentronSoftware.Centron.Core.Interfaces;
using CentronSoftware.Centron.Interfaces.Helpdesk;
using CentronSoftware.Centron.WPF.UI.Extension.Controls;
using Prism.Ioc;
using Prism.Regions;
using System;
namespace CentronSoftware.Centron.WPF.UI.Modules.Helpdesk.Scheduling
{
/// <summary>
/// UC 17: Scheduling & Appointment Management Module Controller
/// Verwaltet Terminverwaltung, Route-Optimierung, Kapazitätsplanung, SLA-Management
/// </summary>
public class SchedulingAppModuleController : ICentronAppModuleController, IRibbonControlModule
{
private readonly IRegionManager _regionManager;
private readonly IContainerProvider _containerProvider;
public string ModuleNameKey => "Scheduling";
public string ModulePath => "Helpdesk/Scheduling";
public int RequestedRightI3D => UserRightsConst.Helpdesk.APPOINTMENT_MANAGEMENT;
public bool IsActive { get; set; }
public SchedulingAppModuleController(IRegionManager regionManager, IContainerProvider containerProvider)
{
this._regionManager = regionManager ?? throw new ArgumentNullException(nameof(regionManager));
this._containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
}
/// <summary>
/// UC 17: Initialize Scheduling Module
/// </summary>
public void Initialize()
{
try
{
// Register ViewModels
this._containerProvider.Resolve<IContainerRegistry>()
.Register<AppointmentManagementViewModel>()
.Register<RouteOptimizationViewModel>()
.Register<CapacityPlanningViewModel>()
.Register<SLAManagementViewModel>();
// Register Views
this._containerProvider.Resolve<IContainerRegistry>()
.Register<AppointmentManagementView>()
.Register<RouteOptimizationView>()
.Register<CapacityPlanningView>()
.Register<SLAManagementView>();
// Register Business Logic
this._containerProvider.Resolve<IContainerRegistry>()
.Register<ISchedulingLogic, BLSchedulingLogic>();
this.IsActive = true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Scheduling Module initialization failed: {ex.Message}");
this.IsActive = false;
throw;
}
}
/// <summary>
/// UC 17: Show Appointment Management view
/// </summary>
public void ShowModule()
{
try
{
var view = this._containerProvider.Resolve<AppointmentManagementView>();
this._regionManager.AddToRegion(RegionNames.MainContentRegion, view);
this._regionManager.RequestNavigate(RegionNames.MainContentRegion, "AppointmentManagementView");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Failed to show Scheduling module: {ex.Message}");
}
}
/// <summary>
/// UC 17: Return Ribbon implementation
/// </summary>
public IRibbonTab GetRibbon()
{
return new SchedulingRibbonController();
}
/// <summary>
/// Cleanup
/// </summary>
public void Cleanup()
{
this.IsActive = false;
}
}
}
17.1 AppointmentManagementViewModel.cs
Pfad: src/centron/Centron.WPF.UI/Modules/Helpdesk/Scheduling/ViewModels/AppointmentManagementViewModel.cs
using CentronSoftware.Centron.Core.Interfaces;
using CentronSoftware.Centron.Interfaces.Helpdesk;
using CentronSoftware.Centron.WPF.UI.Extension;
using Prism.Commands;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace CentronSoftware.Centron.WPF.UI.Modules.Helpdesk.Scheduling.ViewModels
{
/// <summary>
/// UC 17.1: Appointment Management ViewModel
/// Hauptview für Terminverwaltung und Buchungen
/// </summary>
public class AppointmentManagementViewModel : BindableBase, IDisposable
{
private readonly ISchedulingLogic _schedulingLogic;
// Appointment Properties
private DateTime? _selectedAppointmentDate;
private DateTime? _startDate;
private DateTime? _endDate;
private int _durationMinutes;
// Collections
private ObservableCollection<AppointmentItemViewModel> _appointments;
private ObservableCollection<TechnicianViewModel> _technicians;
private ObservableCollection<CustomerViewModel> _customers;
private ObservableCollection<AppointmentSlotViewModel> _availableSlots;
// Selections
private AppointmentItemViewModel _selectedAppointment;
private TechnicianViewModel _selectedTechnician;
private CustomerViewModel _selectedCustomer;
private DateTime _selectedCalendarDate;
// Loading State
private bool _isLoading;
// Commands
public DelegateCommand CreateAppointmentCommand { get; }
public DelegateCommand SaveAppointmentCommand { get; }
public DelegateCommand ConfirmAppointmentCommand { get; }
public DelegateCommand CancelAppointmentCommand { get; }
public DelegateCommand FilterCommand { get; }
public DelegateCommand ClearFiltersCommand { get; }
public AppointmentManagementViewModel()
{
this._schedulingLogic = ClassContainer.Instance.GetInstance<ISchedulingLogic>();
// Initialize Collections
this.Appointments = new ObservableCollection<AppointmentItemViewModel>();
this.Technicians = new ObservableCollection<TechnicianViewModel>();
this.Customers = new ObservableCollection<CustomerViewModel>();
this.AvailableSlots = new ObservableCollection<AppointmentSlotViewModel>();
this.SelectedCalendarDate = DateTime.Today;
// Initialize Commands
this.CreateAppointmentCommand = new DelegateCommand(
execute: this.CreateAppointment,
canExecute: () => !this.IsLoading
);
this.SaveAppointmentCommand = new DelegateCommand(
execute: this.SaveAppointment,
canExecute: () => this.SelectedAppointment != null && !this.IsLoading
);
this.ConfirmAppointmentCommand = new DelegateCommand(
execute: this.ConfirmAppointment,
canExecute: () => this.SelectedAppointment?.Status == AppointmentStatus.Proposed && !this.IsLoading
);
this.CancelAppointmentCommand = new DelegateCommand(
execute: this.CancelAppointment,
canExecute: () => this.SelectedAppointment != null && !this.IsLoading
);
this.FilterCommand = new DelegateCommand(
execute: this.ApplyFilters,
canExecute: () => !this.IsLoading
);
this.ClearFiltersCommand = new DelegateCommand(
execute: this.ClearFilters,
canExecute: () => !this.IsLoading
);
// Load initial data
_ = this.LoadInitialData();
}
// Properties
public DateTime? SelectedAppointmentDate
{
get => this._selectedAppointmentDate;
set => this.SetProperty(ref this._selectedAppointmentDate, value, nameof(this.SelectedAppointmentDate));
}
public DateTime? StartDate
{
get => this._startDate ?? DateTime.Today;
set => this.SetProperty(ref this._startDate, value, nameof(this.StartDate));
}
public DateTime? EndDate
{
get => this._endDate ?? DateTime.Today.AddDays(30);
set => this.SetProperty(ref this._endDate, value, nameof(this.EndDate));
}
public AppointmentItemViewModel SelectedAppointment
{
get => this._selectedAppointment;
set => this.SetProperty(ref this._selectedAppointment, value, nameof(this.SelectedAppointment));
}
public ObservableCollection<AppointmentItemViewModel> Appointments
{
get => this._appointments;
set => this.SetProperty(ref this._appointments, value, nameof(this.Appointments));
}
public DateTime SelectedCalendarDate
{
get => this._selectedCalendarDate;
set => this.SetProperty(ref this._selectedCalendarDate, value, nameof(this.SelectedCalendarDate));
}
public bool IsLoading
{
get => this._isLoading;
set => this.SetProperty(ref this._isLoading, value, nameof(this.IsLoading));
}
/// <summary>
/// UC 17.1.1: Load initial data (technicians, customers, appointments)
/// </summary>
private async Task LoadInitialData()
{
try
{
this.IsLoading = true;
// Load technicians
var techniciansResult = await this._schedulingLogic.GetActiveTechnicianListAsync();
if (techniciansResult.IsSuccess)
{
this.Technicians = new ObservableCollection<TechnicianViewModel>(
techniciansResult.Data.Select(t => new TechnicianViewModel(t))
);
}
// Load customers
var customersResult = await this._schedulingLogic.GetActiveCustomersAsync();
if (customersResult.IsSuccess)
{
this.Customers = new ObservableCollection<CustomerViewModel>(
customersResult.Data.Select(c => new CustomerViewModel(c))
);
}
// Load appointments for current week
await this.LoadAppointments();
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// UC 17.1.1: Load appointments for selected date range
/// </summary>
private async Task LoadAppointments()
{
try
{
var result = await this._schedulingLogic.GetAppointmentsAsync(
startDate: this.StartDate ?? DateTime.Today,
endDate: this.EndDate ?? DateTime.Today.AddDays(30)
);
if (result.IsSuccess)
{
this.Appointments = new ObservableCollection<AppointmentItemViewModel>(
result.Data.Select(a => new AppointmentItemViewModel(a))
);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error loading appointments: {ex.Message}");
}
}
/// <summary>
/// UC 17.1.1: Create new appointment request
/// </summary>
private void CreateAppointment()
{
this.SelectedAppointment = new AppointmentItemViewModel
{
AppointmentDate = DateTime.Today,
Status = AppointmentStatus.Draft,
DurationMinutes = 60
};
}
/// <summary>
/// UC 17.1.2-17.1.3: Save appointment (updates existing or creates new)
/// </summary>
private void SaveAppointment()
{
if (this.SelectedAppointment == null) return;
try
{
this.IsLoading = true;
var request = new CreateAppointmentRequest
{
CustomerI3D = this.SelectedAppointment.Customer.I3D,
TechnicianI3D = this.SelectedAppointment.Technician.I3D,
AppointmentDate = this.SelectedAppointment.AppointmentDate,
StartTime = this.SelectedAppointment.StartTime,
DurationMinutes = this.SelectedAppointment.DurationMinutes,
Description = this.SelectedAppointment.Description,
Status = AppointmentStatus.Proposed
};
var result = this._schedulingLogic.CreateOrUpdateAppointment(request);
if (result.IsSuccess)
{
System.Windows.MessageBox.Show("Termin gespeichert");
_ = this.LoadAppointments();
}
else
{
System.Windows.MessageBox.Show(result.Error);
}
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// UC 17.1.3: Confirm appointment (status: Proposed → Confirmed)
/// </summary>
private void ConfirmAppointment()
{
if (this.SelectedAppointment == null) return;
try
{
this.IsLoading = true;
var result = this._schedulingLogic.ConfirmAppointment(this.SelectedAppointment.I3D);
if (result.IsSuccess)
{
this.SelectedAppointment.Status = AppointmentStatus.Confirmed;
System.Windows.MessageBox.Show("Termin bestätigt");
}
else
{
System.Windows.MessageBox.Show(result.Error);
}
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// UC 17.1.4: Cancel appointment
/// </summary>
private void CancelAppointment()
{
if (this.SelectedAppointment == null) return;
var confirm = System.Windows.MessageBox.Show(
"Möchten Sie diesen Termin absagen?",
"Bestätigung",
System.Windows.MessageBoxButton.YesNo
);
if (confirm != System.Windows.MessageBoxResult.Yes) return;
try
{
this.IsLoading = true;
var result = this._schedulingLogic.CancelAppointment(this.SelectedAppointment.I3D);
if (result.IsSuccess)
{
this.Appointments.Remove(this.SelectedAppointment);
this.SelectedAppointment = null;
}
else
{
System.Windows.MessageBox.Show(result.Error);
}
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// Apply filters
/// </summary>
private void ApplyFilters()
{
_ = this.LoadAppointments();
}
/// <summary>
/// Clear all filters
/// </summary>
private void ClearFilters()
{
this.StartDate = DateTime.Today;
this.EndDate = DateTime.Today.AddDays(30);
this.SelectedTechnician = null;
this.SelectedCustomer = null;
_ = this.LoadAppointments();
}
/// <summary>
/// Cleanup
/// </summary>
public void Dispose()
{
ClassContainer.Instance.ReleaseInstance(this._schedulingLogic);
}
}
}
Module Registration
ModuleRegistration.cs (Updated)
Pfad: src/centron/Centron.WPF.UI/Infrastructure/ModuleRegistration.cs
Add to existing registration:
// Asset Management Module Registration (UC 16)
if (this.CheckUserRight(UserRightsConst.Administration.ASSET_MANAGEMENT))
{
var assetController = new AssetManagementAppModuleController(_regionManager, _containerProvider);
assetController.Initialize();
this.RegisteredModules.Add(
new ModuleRegistrationItem
{
Name = "Asset Management",
Path = "Administration/AssetManagement",
Controller = assetController,
Icon = "Assets48.png",
MenuOrder = 160,
Rights = UserRightsConst.Administration.ASSET_MANAGEMENT
}
);
}
// Scheduling Module Registration (UC 17)
if (this.CheckUserRight(UserRightsConst.Helpdesk.APPOINTMENT_MANAGEMENT))
{
var schedulingController = new SchedulingAppModuleController(_regionManager, _containerProvider);
schedulingController.Initialize();
this.RegisteredModules.Add(
new ModuleRegistrationItem
{
Name = "Terminverwaltung & Planung",
Path = "Helpdesk/Scheduling",
Controller = schedulingController,
Icon = "Calendar48.png",
MenuOrder = 140,
Rights = UserRightsConst.Helpdesk.APPOINTMENT_MANAGEMENT
}
);
}
User Rights Constants
UserRightsConst.cs (Updated)
// Add to Administration rights section
public static class Administration
{
// ... existing rights ...
// UC 16: Asset Management Rights
public const int ASSET_MANAGEMENT = 200600001; // View Asset Management module
public const int ASSET_VIEW = 200600002; // View assets
public const int ASSET_CREATE = 200600003; // Create new assets
public const int ASSET_EDIT = 200600004; // Edit existing assets
public const int ASSET_DELETE = 200600005; // Delete assets
public const int PATCH_MANAGEMENT = 200600010; // Manage patches
public const int SNMP_MONITORING = 200600015; // Configure SNMP monitoring
public const int LICENSE_MANAGEMENT = 200600020; // Manage licenses
public const int COMPLIANCE_REPORTS = 200600025; // View compliance reports
}
// Add to Helpdesk rights section
public static class Helpdesk
{
// ... existing rights ...
// UC 17: Scheduling Rights
public const int APPOINTMENT_MANAGEMENT = 210100001; // View Scheduling module
public const int APPOINTMENT_CREATE = 210100002; // Create appointments
public const int APPOINTMENT_EDIT = 210100003; // Edit appointments
public const int APPOINTMENT_CONFIRM = 210100004; // Confirm appointments
public const int APPOINTMENT_CANCEL = 210100005; // Cancel appointments
public const int ROUTE_OPTIMIZATION = 210100010; // Use route optimization
public const int CAPACITY_PLANNING = 210100015; // Access capacity planning
public const int SLA_MANAGEMENT = 210100020; // Configure SLAs
}
Summary
Controllers Created: 2 primary module controllers
- AssetManagementAppModuleController
- SchedulingAppModuleController
ViewModels Created (templates): 2 main
- AssetInventoryViewModel (70+ lines of business logic)
- AppointmentManagementViewModel (80+ lines of scheduling logic)
Patterns Followed:
- ICentronAppModuleController interface
- Prism IoC container registration
- DelegateCommand and AsyncCommand patterns
- ClassContainer.Instance for ViewModel dependency injection
- Result pattern for error handling
- Observable collections for data binding
- Soft delete pattern (IsDeleted flag)
Key Features:
- Full ViewModel initialization with dependencies
- Filter and search functionality
- CRUD operations (Create, Read, Update, Delete)
- Async operations for I/O
- Command binding patterns
- Collection management
Next Steps for Developers:
- Create directory structure per module
- Implement actual Views (.xaml files) using XAML templates
- Implement Business Logic (BL) layer methods
- Implement REST API endpoints
- Create unit tests
- Deploy and test with real data