from django import forms from django.core.exceptions import ValidationError from django.utils import timezone from decimal import Decimal from .models import * from accounts.models import User class BuildingForm(forms.ModelForm): """Form for managing buildings""" class Meta: model = Building fields = [ 'name', 'code', 'building_type', 'address', 'floor_count', 'total_area_sqm', 'construction_year', 'architect', 'contractor', 'facility_manager', 'last_major_renovation', 'is_active' ] widgets = { 'name': forms.TextInput(attrs={'class': 'form-control'}), 'code': forms.TextInput(attrs={'class': 'form-control'}), 'building_type': forms.Select(attrs={'class': 'form-select'}), 'airport_code': forms.TextInput(attrs={'class': 'form-control'}), 'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'floor_count': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), 'total_area_sqm': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'construction_year': forms.NumberInput(attrs={'class': 'form-control', 'min': '1900', 'max': '2030'}), 'architect': forms.TextInput(attrs={'class': 'form-control'}), 'contractor': forms.TextInput(attrs={'class': 'form-control'}), 'facility_manager': forms.Select(attrs={'class': 'form-select'}), 'last_major_renovation': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def clean_construction_year(self): year = self.cleaned_data.get('construction_year') if year and year > timezone.now().year: raise ValidationError('Construction year cannot be in the future.') return year class FloorForm(forms.ModelForm): """Form for managing floors""" class Meta: model = Floor fields = [ 'building', 'floor_number', 'name', 'area_sqm', 'ceiling_height_m', 'is_public_access' ] widgets = { 'building': forms.Select(attrs={'class': 'form-select'}), 'floor_number': forms.NumberInput(attrs={'class': 'form-control'}), 'name': forms.TextInput(attrs={'class': 'form-control'}), 'area_sqm': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'ceiling_height_m': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'is_public_access': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['building'].queryset = Building.objects.filter(is_active=True) class RoomForm(forms.ModelForm): """Form for managing rooms""" class Meta: model = Room fields = [ 'floor', 'room_number', 'name', 'room_type', 'area_sqm', 'capacity', 'occupancy_status', 'is_accessible', 'current_tenant', 'lease_start_date', 'lease_end_date', 'monthly_rent', 'has_hvac', 'has_electrical', 'has_plumbing', 'has_internet', 'notes' ] widgets = { 'floor': forms.Select(attrs={'class': 'form-select'}), 'room_number': forms.TextInput(attrs={'class': 'form-control'}), 'name': forms.TextInput(attrs={'class': 'form-control'}), 'room_type': forms.Select(attrs={'class': 'form-select'}), 'area_sqm': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'capacity': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), 'occupancy_status': forms.Select(attrs={'class': 'form-select'}), 'is_accessible': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'current_tenant': forms.TextInput(attrs={'class': 'form-control'}), 'lease_start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'lease_end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'monthly_rent': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'has_hvac': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'has_electrical': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'has_plumbing': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'has_internet': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), } def clean(self): cleaned_data = super().clean() lease_start = cleaned_data.get('lease_start_date') lease_end = cleaned_data.get('lease_end_date') if lease_start and lease_end and lease_start >= lease_end: raise ValidationError('Lease end date must be after lease start date.') return cleaned_data class AssetCategoryForm(forms.ModelForm): """Form for managing asset categories""" class Meta: model = AssetCategory fields = ['name', 'code', 'description', 'parent_category', 'is_active'] widgets = { 'name': forms.TextInput(attrs={'class': 'form-control'}), 'code': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'parent_category': forms.Select(attrs={'class': 'form-select'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Exclude self from parent category choices if self.instance.pk: self.fields['parent_category'].queryset = AssetCategory.objects.exclude(pk=self.instance.pk) class AssetForm(forms.ModelForm): """Form for managing assets""" class Meta: model = Asset fields = [ 'asset_id', 'name', 'category', 'building', 'floor', 'room', 'location_description', 'manufacturer', 'model', 'serial_number', 'purchase_date', 'purchase_cost', 'current_value', 'depreciation_rate', 'warranty_start_date', 'warranty_end_date', 'service_provider', 'service_contract_number', 'status', 'condition', 'last_inspection_date', 'next_maintenance_date', 'assigned_to', 'notes' ] widgets = { 'asset_id': forms.TextInput(attrs={'class': 'form-control'}), 'name': forms.TextInput(attrs={'class': 'form-control'}), 'category': forms.Select(attrs={'class': 'form-select'}), 'building': forms.Select(attrs={'class': 'form-select'}), 'floor': forms.Select(attrs={'class': 'form-select'}), 'room': forms.Select(attrs={'class': 'form-select'}), 'location_description': forms.TextInput(attrs={'class': 'form-control'}), 'manufacturer': forms.TextInput(attrs={'class': 'form-control'}), 'model': forms.TextInput(attrs={'class': 'form-control'}), 'serial_number': forms.TextInput(attrs={'class': 'form-control'}), 'purchase_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'purchase_cost': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'current_value': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'depreciation_rate': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0', 'max': '100'}), 'warranty_start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'warranty_end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'service_provider': forms.TextInput(attrs={'class': 'form-control'}), 'service_contract_number': forms.TextInput(attrs={'class': 'form-control'}), 'status': forms.Select(attrs={'class': 'form-select'}), 'condition': forms.Select(attrs={'class': 'form-select'}), 'last_inspection_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'next_maintenance_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'assigned_to': forms.Select(attrs={'class': 'form-select'}), 'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['category'].queryset = AssetCategory.objects.filter(is_active=True) self.fields['building'].queryset = Building.objects.filter(is_active=True) self.fields['floor'].required = False self.fields['room'].required = False def clean(self): cleaned_data = super().clean() warranty_start = cleaned_data.get('warranty_start_date') warranty_end = cleaned_data.get('warranty_end_date') purchase_date = cleaned_data.get('purchase_date') purchase_cost = cleaned_data.get('purchase_cost') current_value = cleaned_data.get('current_value') if warranty_start and warranty_end and warranty_start >= warranty_end: raise ValidationError('Warranty end date must be after warranty start date.') if purchase_date and warranty_start and warranty_start < purchase_date: raise ValidationError('Warranty start date cannot be before purchase date.') if purchase_cost and current_value and current_value > purchase_cost: raise ValidationError('Current value cannot be greater than purchase cost.') return cleaned_data class MaintenanceRequestForm(forms.ModelForm): """Form for creating maintenance requests""" class Meta: model = MaintenanceRequest fields = [ 'title', 'description', 'maintenance_type', 'building', 'floor', 'room', 'asset', 'priority', 'scheduled_date', 'actual_cost', 'estimated_cost', 'notes', 'assigned_to', 'scheduled_date', 'estimated_hours', 'status', ] widgets = { 'title': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), 'maintenance_type': forms.Select(attrs={'class': 'form-select'}), 'building': forms.Select(attrs={'class': 'form-select'}), 'floor': forms.Select(attrs={'class': 'form-select'}), 'room': forms.Select(attrs={'class': 'form-select'}), 'asset': forms.Select(attrs={'class': 'form-select'}), 'priority': forms.Select(attrs={'class': 'form-select'}), 'scheduled_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'estimated_cost': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'assigned_to': forms.Select(attrs={'class': 'form-select'}), 'estimated_hours': forms.NumberInput(attrs={'class': 'form-control', 'type': 'number'}), 'status': forms.Select(attrs={'class': 'form-select'}), 'actual_cost': forms.NumberInput(attrs={'class': 'form-control', 'type': 'number'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['maintenance_type'].queryset = MaintenanceType.objects.filter(is_active=True) self.fields['building'].queryset = Building.objects.filter(is_active=True) self.fields['floor'].required = False self.fields['room'].required = False self.fields['asset'].required = False class MaintenanceUpdateForm(forms.ModelForm): """Form for updating maintenance requests""" class Meta: model = MaintenanceRequest fields = [ 'status', 'assigned_to', 'scheduled_date', 'started_date', 'completed_date', 'actual_cost', 'completion_notes' ] widgets = { 'status': forms.Select(attrs={'class': 'form-select'}), 'assigned_to': forms.Select(attrs={'class': 'form-select'}), 'scheduled_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'started_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'completed_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'actual_cost': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'completion_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), } def clean(self): cleaned_data = super().clean() scheduled_date = cleaned_data.get('scheduled_date') started_date = cleaned_data.get('started_date') completed_date = cleaned_data.get('completed_date') if started_date and scheduled_date and started_date < scheduled_date: raise ValidationError('Start date cannot be before scheduled date.') if completed_date and started_date and completed_date < started_date: raise ValidationError('Completion date cannot be before start date.') return cleaned_data class MaintenanceScheduleForm(forms.ModelForm): """Form for creating maintenance schedules""" class Meta: model = MaintenanceSchedule fields = [ 'name', 'description', 'maintenance_type', 'asset', 'building', 'room', 'frequency', 'frequency_interval', 'start_date', 'end_date', 'assigned_to', 'estimated_duration_hours', 'is_active' ] widgets = { 'name': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'maintenance_type': forms.Select(attrs={'class': 'form-select'}), 'asset': forms.Select(attrs={'class': 'form-select'}), 'building': forms.Select(attrs={'class': 'form-select'}), 'room': forms.Select(attrs={'class': 'form-select'}), 'frequency': forms.Select(attrs={'class': 'form-select'}), 'frequency_interval': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), 'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'assigned_to': forms.Select(attrs={'class': 'form-select'}), 'estimated_duration_hours': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.5', 'min': '0'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['maintenance_type'].queryset = MaintenanceType.objects.filter(is_active=True) self.fields['asset'].required = False self.fields['building'].required = False self.fields['room'].required = False def clean(self): cleaned_data = super().clean() start_date = cleaned_data.get('start_date') end_date = cleaned_data.get('end_date') asset = cleaned_data.get('asset') building = cleaned_data.get('building') room = cleaned_data.get('room') if start_date and end_date and start_date >= end_date: raise ValidationError('End date must be after start date.') # At least one scope must be specified if not any([asset, building, room]): raise ValidationError('Please specify at least one scope: asset, building, or room.') return cleaned_data class VendorForm(forms.ModelForm): """Form for managing vendors""" class Meta: model = Vendor fields = [ 'name', 'vendor_type', 'contact_person', 'email', 'phone', 'address', 'license_number', 'insurance_policy', 'insurance_expiry', 'rating', 'is_active' ] widgets = { 'name': forms.TextInput(attrs={'class': 'form-control'}), 'vendor_type': forms.Select(attrs={'class': 'form-select'}), 'contact_person': forms.TextInput(attrs={'class': 'form-control'}), 'email': forms.EmailInput(attrs={'class': 'form-control'}), 'phone': forms.TextInput(attrs={'class': 'form-control'}), 'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'license_number': forms.TextInput(attrs={'class': 'form-control'}), 'insurance_policy': forms.TextInput(attrs={'class': 'form-control'}), 'insurance_expiry': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'rating': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1', 'min': '0', 'max': '5'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } class ServiceContractForm(forms.ModelForm): """Form for managing service contracts""" class Meta: model = ServiceContract fields = [ 'contract_number', 'vendor', 'title', 'description', 'start_date', 'end_date', 'contract_value', 'payment_terms', 'buildings', 'service_areas', 'auto_renewal', 'renewal_notice_days' ] widgets = { 'contract_number': forms.TextInput(attrs={'class': 'form-control'}), 'vendor': forms.Select(attrs={'class': 'form-select'}), 'title': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), 'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'contract_value': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}), 'payment_terms': forms.TextInput(attrs={'class': 'form-control'}), 'buildings': forms.CheckboxSelectMultiple(), 'service_areas': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'auto_renewal': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'renewal_notice_days': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['vendor'].queryset = Vendor.objects.filter(is_active=True) self.fields['buildings'].queryset = Building.objects.filter(is_active=True) def clean(self): cleaned_data = super().clean() start_date = cleaned_data.get('start_date') end_date = cleaned_data.get('end_date') if start_date and end_date and start_date >= end_date: raise ValidationError('End date must be after start date.') return cleaned_data class FacilitySearchForm(forms.Form): """Form for searching facilities""" search_query = forms.CharField( max_length=100, required=False, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Search buildings, rooms, assets...' }) ) building = forms.ModelChoiceField( queryset=Building.objects.filter(is_active=True), required=False, empty_label="All Buildings", widget=forms.Select(attrs={'class': 'form-select'}) ) asset_category = forms.ModelChoiceField( queryset=AssetCategory.objects.filter(is_active=True), required=False, empty_label="All Categories", widget=forms.Select(attrs={'class': 'form-select'}) ) class MaintenanceFilterForm(forms.Form): """Form for filtering maintenance requests""" STATUS_CHOICES = [('', 'All Statuses')] + MaintenanceRequest.MaintenanceStatus.choices PRIORITY_CHOICES = [('', 'All Priorities')] + MaintenanceRequest.Priority.choices status = forms.ChoiceField( choices=STATUS_CHOICES, required=False, widget=forms.Select(attrs={'class': 'form-select'}) ) priority = forms.ChoiceField( choices=PRIORITY_CHOICES, required=False, widget=forms.Select(attrs={'class': 'form-select'}) ) building = forms.ModelChoiceField( queryset=Building.objects.filter(is_active=True), required=False, empty_label="All Buildings", widget=forms.Select(attrs={'class': 'form-select'}) ) assigned_to = forms.ModelChoiceField( queryset=None, # Will be set in __init__ required=False, empty_label="All Assignees", widget=forms.Select(attrs={'class': 'form-select'}) ) date_from = forms.DateField( required=False, widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}) ) date_to = forms.DateField( required=False, widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['assigned_to'].queryset = User.objects.filter(is_active=True)