178 lines
7.4 KiB
Python
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) |