# 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 ```csharp 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 { /// /// UC 16: Asset Management Module Controller /// Verwaltet IT-Hardware-Bestände, Patch-Management, SNMP-Überwachung, Lizenzen, Compliance /// 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)); } /// /// UC 16: Initialize Asset Management Module /// - Register ViewModels /// - Register Views /// - Register Business Logic /// - Setup Ribbon Commands /// public void Initialize() { try { // Register ViewModels (Prism IoC) this._containerProvider.Resolve() .Register() .Register() .Register() .Register() .Register() .Register() .Register() .Register(); // Register Views this._containerProvider.Resolve() .Register() .Register() .Register() .Register() .Register(); // Register Business Logic Layer this._containerProvider.Resolve() .Register(); // Register WebService Logic (for REST API calls) this._containerProvider.Resolve() .Register(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; } } /// /// UC 16: Show main Asset Management view /// public void ShowModule() { try { var view = this._containerProvider.Resolve(); 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}"); } } /// /// UC 16: Return Ribbon implementation for Asset Management /// public IRibbonTab GetRibbon() { return new AssetManagementRibbonController(); } /// /// UC 16: Cleanup when module is closed /// 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` ```csharp 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 { /// /// UC 1.1-1.5: Device Inventory Management ViewModel /// Hauptview für Geräte-Erfassung, Bearbeitung, Lebenszyklusmanagement /// 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 _assets; private ObservableCollection _departments; private ObservableCollection _locations; private ObservableCollection _deviceTypes; private ObservableCollection _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(); this._departmentLogic = ClassContainer.Instance.GetInstance(); this._locationLogic = ClassContainer.Instance.GetInstance(); // Initialize Collections this.Assets = new ObservableCollection(); this.Departments = new ObservableCollection(); this.Locations = new ObservableCollection(); this.DeviceTypes = new ObservableCollection(); this.Statuses = new ObservableCollection(); // 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 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)); } /// /// UC 1.1: Load initial departments, locations, types, statuses /// 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( 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( locationsResult.Data.Select(l => new LocationViewModel(l)) ); } // Initialize device types and statuses this.DeviceTypes = new ObservableCollection( Enum.GetValues(typeof(AssetDeviceType)).Cast() ); this.Statuses = new ObservableCollection( Enum.GetValues(typeof(AssetStatus)).Cast() ); // Load assets await this.RefreshAssetList(); } finally { this.IsLoading = false; } } /// /// UC 1.1: Create new asset (initializes form with default values) /// 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; } /// /// UC 1.1: Save asset to database /// 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; } } /// /// UC 1.4: Delete asset (soft delete - sets IsDeleted flag) /// 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; } } /// /// UC 1.5: Import batch of assets from CSV/Excel /// 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; } } } /// /// UC 1.1-1.3: Refresh asset list with applied filters /// 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( result.Data.Select(a => new AssetItemViewModel(a)) ); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error refreshing assets: {ex.Message}"); } } /// /// UC 1.1: Generate unique asset tag /// private string GenerateAssetTag() { return $"ASSET-{DateTime.Now:yyyyMMdd}-{Guid.NewGuid().ToString().Substring(0, 6).ToUpper()}"; } /// /// Clear all filters and reload /// private void ClearFilters() { this.SearchText = string.Empty; this.SelectedDeviceType = AssetDeviceType.Unknown; this.SelectedStatus = AssetStatus.Unknown; _ = this.RefreshAssetList(); } /// /// Cleanup resources /// 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` ```csharp 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 { /// /// UC 17: Scheduling & Appointment Management Module Controller /// Verwaltet Terminverwaltung, Route-Optimierung, Kapazitätsplanung, SLA-Management /// 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)); } /// /// UC 17: Initialize Scheduling Module /// public void Initialize() { try { // Register ViewModels this._containerProvider.Resolve() .Register() .Register() .Register() .Register(); // Register Views this._containerProvider.Resolve() .Register() .Register() .Register() .Register(); // Register Business Logic this._containerProvider.Resolve() .Register(); this.IsActive = true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Scheduling Module initialization failed: {ex.Message}"); this.IsActive = false; throw; } } /// /// UC 17: Show Appointment Management view /// public void ShowModule() { try { var view = this._containerProvider.Resolve(); 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}"); } } /// /// UC 17: Return Ribbon implementation /// public IRibbonTab GetRibbon() { return new SchedulingRibbonController(); } /// /// Cleanup /// public void Cleanup() { this.IsActive = false; } } } ``` --- ### 17.1 AppointmentManagementViewModel.cs **Pfad**: `src/centron/Centron.WPF.UI/Modules/Helpdesk/Scheduling/ViewModels/AppointmentManagementViewModel.cs` ```csharp 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 { /// /// UC 17.1: Appointment Management ViewModel /// Hauptview für Terminverwaltung und Buchungen /// 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 _appointments; private ObservableCollection _technicians; private ObservableCollection _customers; private ObservableCollection _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(); // Initialize Collections this.Appointments = new ObservableCollection(); this.Technicians = new ObservableCollection(); this.Customers = new ObservableCollection(); this.AvailableSlots = new ObservableCollection(); 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 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)); } /// /// UC 17.1.1: Load initial data (technicians, customers, appointments) /// private async Task LoadInitialData() { try { this.IsLoading = true; // Load technicians var techniciansResult = await this._schedulingLogic.GetActiveTechnicianListAsync(); if (techniciansResult.IsSuccess) { this.Technicians = new ObservableCollection( techniciansResult.Data.Select(t => new TechnicianViewModel(t)) ); } // Load customers var customersResult = await this._schedulingLogic.GetActiveCustomersAsync(); if (customersResult.IsSuccess) { this.Customers = new ObservableCollection( customersResult.Data.Select(c => new CustomerViewModel(c)) ); } // Load appointments for current week await this.LoadAppointments(); } finally { this.IsLoading = false; } } /// /// UC 17.1.1: Load appointments for selected date range /// 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( result.Data.Select(a => new AppointmentItemViewModel(a)) ); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error loading appointments: {ex.Message}"); } } /// /// UC 17.1.1: Create new appointment request /// private void CreateAppointment() { this.SelectedAppointment = new AppointmentItemViewModel { AppointmentDate = DateTime.Today, Status = AppointmentStatus.Draft, DurationMinutes = 60 }; } /// /// UC 17.1.2-17.1.3: Save appointment (updates existing or creates new) /// 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; } } /// /// UC 17.1.3: Confirm appointment (status: Proposed → Confirmed) /// 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; } } /// /// UC 17.1.4: Cancel appointment /// 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; } } /// /// Apply filters /// private void ApplyFilters() { _ = this.LoadAppointments(); } /// /// Clear all filters /// private void ClearFilters() { this.StartDate = DateTime.Today; this.EndDate = DateTime.Today.AddDays(30); this.SelectedTechnician = null; this.SelectedCustomer = null; _ = this.LoadAppointments(); } /// /// Cleanup /// 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**: ```csharp // 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) ```csharp // 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**: 1. Create directory structure per module 2. Implement actual Views (.xaml files) using XAML templates 3. Implement Business Logic (BL) layer methods 4. Implement REST API endpoints 5. Create unit tests 6. Deploy and test with real data