Files
Masterarbeit/Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_NEW_CONTROLLERS.md

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:

  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