631 lines
29 KiB
Python
631 lines
29 KiB
Python
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'}),
|
|
'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', 'area_sqm',
|
|
'capacity', 'occupancy_status', 'is_accessible', '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'}),
|
|
'is_accessible': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
}
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
|
|
|
|
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 = [
|
|
'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'
|
|
]
|
|
exclude = ['asset_id']
|
|
widgets = {
|
|
'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
|
|
exclude = ['request_id', 'requested_date']
|
|
fields = [
|
|
'title', 'description', 'maintenance_type', 'building',
|
|
'floor', 'room', 'asset', 'priority', 'scheduled_date',
|
|
'estimated_cost', 'actual_cost', 'notes', 'assigned_to',
|
|
'estimated_hours', 'status', 'completion_notes'
|
|
]
|
|
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'}),
|
|
'actual_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', 'step': '0.5', 'min': '0'}),
|
|
'status': forms.Select(attrs={'class': 'form-select'}),
|
|
'completion_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
|
|
}
|
|
|
|
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_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', 'crn', 'vrn', '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}),
|
|
'crn': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'vrn': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'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)
|
|
|
|
|
|
class InspectionForm(forms.ModelForm):
|
|
"""Form for managing inspections"""
|
|
|
|
class Meta:
|
|
model = Inspection
|
|
fields = [
|
|
'inspection_type', 'title', 'description', 'building',
|
|
'floors', 'rooms', 'assets', 'scheduled_date',
|
|
'estimated_duration_hours', 'inspector', 'inspector_external',
|
|
'inspector_organization', 'status', 'overall_rating',
|
|
'findings', 'recommendations', 'requires_followup', 'followup_date'
|
|
]
|
|
widgets = {
|
|
'inspection_type': forms.Select(attrs={'class': 'form-select'}),
|
|
'title': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'building': forms.Select(attrs={'class': 'form-select'}),
|
|
'floors': forms.CheckboxSelectMultiple(),
|
|
'rooms': forms.CheckboxSelectMultiple(),
|
|
'assets': forms.CheckboxSelectMultiple(),
|
|
'scheduled_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'estimated_duration_hours': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.5', 'min': '0'}),
|
|
'inspector': forms.Select(attrs={'class': 'form-select'}),
|
|
'inspector_external': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'inspector_organization': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'status': forms.Select(attrs={'class': 'form-select'}),
|
|
'overall_rating': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'e.g., Pass, Fail, Conditional'}),
|
|
'findings': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
|
|
'recommendations': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
|
|
'requires_followup': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'followup_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['building'].queryset = Building.objects.filter(is_active=True)
|
|
self.fields['inspector'].queryset = User.objects.filter(is_active=True)
|
|
self.fields['floors'].required = False
|
|
self.fields['rooms'].required = False
|
|
self.fields['assets'].required = False
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
requires_followup = cleaned_data.get('requires_followup')
|
|
followup_date = cleaned_data.get('followup_date')
|
|
|
|
if requires_followup and not followup_date:
|
|
raise ValidationError('Follow-up date is required when follow-up is needed.')
|
|
|
|
return cleaned_data
|
|
|
|
|
|
class EnergyMeterForm(forms.ModelForm):
|
|
"""Form for managing energy meters"""
|
|
|
|
class Meta:
|
|
model = EnergyMeter
|
|
fields = [
|
|
'meter_id', 'meter_type', 'building', 'location_description',
|
|
'manufacturer', 'model', 'serial_number', 'installation_date',
|
|
'is_active', 'calibration_date', 'next_calibration_date'
|
|
]
|
|
widgets = {
|
|
'meter_id': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'meter_type': forms.Select(attrs={'class': 'form-select'}),
|
|
'building': 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'}),
|
|
'installation_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'calibration_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'next_calibration_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['building'].queryset = Building.objects.filter(is_active=True)
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
calibration_date = cleaned_data.get('calibration_date')
|
|
next_calibration_date = cleaned_data.get('next_calibration_date')
|
|
|
|
if calibration_date and next_calibration_date and next_calibration_date <= calibration_date:
|
|
raise ValidationError('Next calibration date must be after calibration date.')
|
|
|
|
return cleaned_data
|
|
|
|
|
|
class EnergyReadingForm(forms.ModelForm):
|
|
"""Form for recording energy readings"""
|
|
|
|
class Meta:
|
|
model = EnergyReading
|
|
fields = [
|
|
'meter', 'reading_date', 'reading_value', 'cost',
|
|
'is_estimated', 'notes'
|
|
]
|
|
widgets = {
|
|
'meter': forms.Select(attrs={'class': 'form-select'}),
|
|
'reading_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'reading_value': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}),
|
|
'cost': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}),
|
|
'is_estimated': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['meter'].queryset = EnergyMeter.objects.filter(is_active=True)
|
|
|
|
def clean_reading_value(self):
|
|
reading_value = self.cleaned_data.get('reading_value')
|
|
meter = self.cleaned_data.get('meter')
|
|
|
|
if meter and reading_value < meter.current_reading:
|
|
raise ValidationError(
|
|
f'Reading value cannot be less than current meter reading ({meter.current_reading}).'
|
|
)
|
|
|
|
return reading_value
|
|
|
|
|
|
class SpaceReservationForm(forms.ModelForm):
|
|
"""Form for managing space reservations"""
|
|
|
|
class Meta:
|
|
model = SpaceReservation
|
|
fields = [
|
|
'room', 'title', 'description', 'start_datetime', 'end_datetime',
|
|
'contact_person', 'contact_email', 'contact_phone',
|
|
'expected_attendees', 'setup_requirements', 'catering_required',
|
|
'av_equipment_required', 'hourly_rate', 'notes'
|
|
]
|
|
widgets = {
|
|
'room': forms.Select(attrs={'class': 'form-select'}),
|
|
'title': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'start_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'end_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'contact_person': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'contact_email': forms.EmailInput(attrs={'class': 'form-control'}),
|
|
'contact_phone': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'expected_attendees': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
|
|
'setup_requirements': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'catering_required': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'av_equipment_required': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'hourly_rate': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01', 'min': '0'}),
|
|
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
}
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
start_datetime = cleaned_data.get('start_datetime')
|
|
end_datetime = cleaned_data.get('end_datetime')
|
|
room = cleaned_data.get('room')
|
|
|
|
if start_datetime and end_datetime:
|
|
if start_datetime >= end_datetime:
|
|
raise ValidationError('End time must be after start time.')
|
|
|
|
# Check for conflicts with existing reservations
|
|
if room:
|
|
conflicts = SpaceReservation.objects.filter(
|
|
room=room,
|
|
status__in=['PENDING', 'CONFIRMED']
|
|
).filter(
|
|
Q(start_datetime__lt=end_datetime, end_datetime__gt=start_datetime)
|
|
)
|
|
|
|
# Exclude current instance if updating
|
|
if self.instance.pk:
|
|
conflicts = conflicts.exclude(pk=self.instance.pk)
|
|
|
|
if conflicts.exists():
|
|
raise ValidationError(
|
|
f'This room is already reserved during the selected time period. '
|
|
f'Conflicting reservation: {conflicts.first().title}'
|
|
)
|
|
|
|
return cleaned_data
|
|
|
|
|
|
class SpaceReservationUpdateForm(forms.ModelForm):
|
|
"""Form for updating space reservation status"""
|
|
|
|
class Meta:
|
|
model = SpaceReservation
|
|
fields = ['status', 'notes']
|
|
widgets = {
|
|
'status': forms.Select(attrs={'class': 'form-select'}),
|
|
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
}
|