from django import forms from django.forms import formset_factory from django.forms.widgets import NumberInput from .models import ManufacturingOrder, BillOfMaterials from inventory.models import Product class ManufacturingOrderForm(forms.ModelForm): """Simple form for creating manufacturing orders with basic input""" class Meta: model = ManufacturingOrder fields = ['product', 'quantity', 'date', 'notes'] widgets = { 'date': forms.DateInput(attrs={'type': 'date'}), 'quantity': forms.NumberInput(attrs={'step': '0.01'}), 'notes': forms.Textarea(attrs={'rows': 3}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add Bootstrap classes for field in self.fields.values(): if isinstance(field.widget, (forms.CheckboxInput, forms.RadioSelect)): field.widget.attrs.update({'class': 'form-check-input'}) elif isinstance(field.widget, forms.Textarea): field.widget.attrs.update({'class': 'form-control'}) elif isinstance(field.widget, forms.DateInput): field.widget.attrs.update({'class': 'form-control'}) else: field.widget.attrs.update({'class': 'form-control'}) def clean_quantity(self): """Ensure quantity is positive""" quantity = self.cleaned_data.get('quantity') if quantity <= 0: raise forms.ValidationError("Quantity must be greater than zero.") return quantity class BillOfMaterialsForm(forms.ModelForm): """Form for creating and editing Bill of Materials entries""" class Meta: model = BillOfMaterials fields = ['manufactured_product', 'component', 'quantity', 'unit'] widgets = { 'quantity': forms.NumberInput(attrs={'step': '0.0001'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add Bootstrap classes for field in self.fields.values(): if isinstance(field.widget, (forms.CheckboxInput, forms.RadioSelect)): field.widget.attrs.update({'class': 'form-check-input'}) elif isinstance(field.widget, forms.Textarea): field.widget.attrs.update({'class': 'form-control'}) elif isinstance(field.widget, forms.DateInput): field.widget.attrs.update({'class': 'form-control'}) else: field.widget.attrs.update({'class': 'form-control'}) # Filter manufactured products to only show those marked as manufactured self.fields['manufactured_product'].queryset = Product.objects.filter(is_manufactured=True) def clean_quantity(self): """Ensure quantity is positive""" quantity = self.cleaned_data.get('quantity') if quantity <= 0: raise forms.ValidationError("Quantity must be greater than zero.") return quantity def clean(self): """Validate that manufactured product and component are not the same""" cleaned_data = super().clean() manufactured_product = cleaned_data.get('manufactured_product') component = cleaned_data.get('component') if manufactured_product and component and manufactured_product == component: raise forms.ValidationError("A product cannot be a component of itself.") return cleaned_data class MultipleBillOfMaterialsForm(forms.Form): """Form for creating multiple BOM entries at once""" manufactured_product = forms.ModelChoiceField( queryset=Product.objects.filter(is_manufactured=True), label="Manufactured Product", help_text="The product that is manufactured using these components" ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add Bootstrap classes for field in self.fields.values(): if isinstance(field.widget, (forms.CheckboxInput, forms.RadioSelect)): field.widget.attrs.update({'class': 'form-check-input'}) elif isinstance(field.widget, forms.Textarea): field.widget.attrs.update({'class': 'form-control'}) elif isinstance(field.widget, forms.DateInput): field.widget.attrs.update({'class': 'form-control'}) else: field.widget.attrs.update({'class': 'form-control'}) def clean_manufactured_product(self): """Ensure the selected product is marked as manufactured""" manufactured_product = self.cleaned_data.get('manufactured_product') if manufactured_product and not manufactured_product.is_manufactured: raise forms.ValidationError("Selected product is not marked as manufactured.") return manufactured_product class BOMComponentForm(forms.Form): """Form for individual BOM component entry""" component = forms.ModelChoiceField( queryset=Product.objects.all(), label="Component", help_text="A component used in manufacturing" ) quantity = forms.DecimalField( max_digits=10, decimal_places=4, min_value=0.0001, label="Quantity", help_text="Quantity of component needed per unit of manufactured product" ) unit = forms.CharField( max_length=20, required=False, label="Unit", help_text="Unit of measurement for the component (e.g., kg, pieces, meters)", widget=forms.TextInput(attrs={'readonly': 'readonly'}) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add Bootstrap classes for field in self.fields.values(): if isinstance(field.widget, (forms.CheckboxInput, forms.RadioSelect)): field.widget.attrs.update({'class': 'form-check-input'}) elif isinstance(field.widget, forms.Textarea): field.widget.attrs.update({'class': 'form-control'}) elif isinstance(field.widget, forms.DateInput): field.widget.attrs.update({'class': 'form-control'}) else: field.widget.attrs.update({'class': 'form-control'}) def clean_quantity(self): """Ensure quantity is positive""" quantity = self.cleaned_data.get('quantity') if quantity <= 0: raise forms.ValidationError("Quantity must be greater than zero.") return quantity def clean(self): """Validate that component is provided""" cleaned_data = super().clean() component = cleaned_data.get('component') # If no component is selected, this might be an empty form if not component: # Check if any other fields have data quantity = cleaned_data.get('quantity') unit = cleaned_data.get('unit') # If other fields have data but no component, raise error if quantity or unit: raise forms.ValidationError("Component is required.") # If all fields are empty, mark form as empty raise forms.ValidationError("This form is empty.") return cleaned_data # Create a formset for BOM components BOMComponentFormSet = formset_factory(BOMComponentForm, extra=1, min_num=1, validate_min=True)