Django_Basic_Manufacturing/manufacture/forms.py
2025-08-19 19:06:26 +07:00

178 lines
7.4 KiB
Python

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)