281 lines
11 KiB
Python
281 lines
11 KiB
Python
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.views.generic import ListView, DetailView
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.utils.decorators import method_decorator
|
|
from django.contrib import messages
|
|
from django.http import JsonResponse
|
|
from .models import ManufacturingOrder, BillOfMaterials
|
|
from inventory.models import Product
|
|
from .forms import ManufacturingOrderForm, BillOfMaterialsForm, MultipleBillOfMaterialsForm, BOMComponentFormSet
|
|
from users.views import has_manufacturing_access
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class ManufactureListView(ListView):
|
|
model = ManufacturingOrder
|
|
template_name = 'manufacture/manufacture_list.html'
|
|
context_object_name = 'manufacturing_orders'
|
|
paginate_by = 20
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class BillOfMaterialsListView(ListView):
|
|
model = BillOfMaterials
|
|
template_name = 'manufacture/bom_list.html'
|
|
context_object_name = 'boms'
|
|
paginate_by = 20
|
|
|
|
def get_queryset(self):
|
|
queryset = super().get_queryset()
|
|
# Optionally filter by manufactured product
|
|
product_id = self.request.GET.get('product_id')
|
|
if product_id:
|
|
queryset = queryset.filter(manufactured_product_id=product_id)
|
|
return queryset
|
|
|
|
|
|
@login_required
|
|
def manufacture_create(request):
|
|
"""Create a new manufacturing order with simple input"""
|
|
if request.method == 'POST':
|
|
form = ManufacturingOrderForm(request.POST)
|
|
if form.is_valid():
|
|
manufacturing_order = form.save(commit=False)
|
|
manufacturing_order.created_by = request.user
|
|
# Generate order number if not provided
|
|
if not manufacturing_order.order_number:
|
|
# Get the next order number
|
|
last_order = ManufacturingOrder.objects.order_by('-id').first()
|
|
if last_order:
|
|
try:
|
|
last_number = int(last_order.order_number.split('-')[-1])
|
|
new_number = last_number + 1
|
|
except:
|
|
new_number = 1
|
|
else:
|
|
new_number = 1
|
|
manufacturing_order.order_number = f"MO-{new_number:06d}"
|
|
|
|
# Set status to completed by default for simplicity
|
|
manufacturing_order.status = 'completed'
|
|
manufacturing_order.save()
|
|
|
|
messages.success(request, f'Manufacturing order "{manufacturing_order.order_number}" created successfully!')
|
|
return redirect('manufacture:manufacture_detail', pk=manufacturing_order.pk)
|
|
else:
|
|
form = ManufacturingOrderForm()
|
|
|
|
return render(request, 'manufacture/manufacture_form.html', {'form': form, 'title': 'Create Manufacturing Order'})
|
|
|
|
|
|
@login_required
|
|
def manufacture_edit(request, pk):
|
|
"""Edit an existing manufacturing order"""
|
|
if not has_manufacturing_access(request.user):
|
|
messages.error(request, 'You do not have permission to edit manufacturing orders.')
|
|
return redirect('manufacture:manufacture_detail', pk=pk)
|
|
|
|
manufacturing_order = get_object_or_404(ManufacturingOrder, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ManufacturingOrderForm(request.POST, instance=manufacturing_order)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, f'Manufacturing order "{manufacturing_order.order_number}" updated successfully!')
|
|
return redirect('manufacture:manufacture_detail', pk=manufacturing_order.pk)
|
|
else:
|
|
form = ManufacturingOrderForm(instance=manufacturing_order)
|
|
|
|
return render(request, 'manufacture/manufacture_form.html', {
|
|
'form': form,
|
|
'title': 'Edit Manufacturing Order',
|
|
'manufacturing_order': manufacturing_order
|
|
})
|
|
|
|
|
|
@login_required
|
|
def manufacture_delete(request, pk):
|
|
"""Delete a manufacturing order"""
|
|
if not has_manufacturing_access(request.user):
|
|
messages.error(request, 'You do not have permission to delete manufacturing orders.')
|
|
return redirect('manufacture:manufacture_list')
|
|
|
|
manufacturing_order = get_object_or_404(ManufacturingOrder, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
order_number = manufacturing_order.order_number
|
|
manufacturing_order.delete()
|
|
messages.success(request, f'Manufacturing order "{order_number}" deleted successfully!')
|
|
return redirect('manufacture:manufacture_list')
|
|
|
|
return render(request, 'manufacture/manufacture_confirm_delete.html', {'manufacturing_order': manufacturing_order})
|
|
|
|
|
|
@login_required
|
|
def bom_create(request):
|
|
"""Create a new bill of materials entry"""
|
|
if not has_manufacturing_access(request.user):
|
|
messages.error(request, 'You do not have permission to create bill of materials entries.')
|
|
return redirect('manufacture:bom_list')
|
|
|
|
if request.method == 'POST':
|
|
main_form = MultipleBillOfMaterialsForm(request.POST)
|
|
component_formset = BOMComponentFormSet(request.POST)
|
|
|
|
if main_form.is_valid() and component_formset.is_valid():
|
|
manufactured_product = main_form.cleaned_data['manufactured_product']
|
|
|
|
# Save all component entries
|
|
saved_components = []
|
|
for component_form in component_formset:
|
|
# Skip empty forms
|
|
if not component_form.cleaned_data:
|
|
continue
|
|
|
|
component = component_form.cleaned_data['component']
|
|
quantity = component_form.cleaned_data['quantity']
|
|
unit = component_form.cleaned_data['unit']
|
|
|
|
# Create BOM entry
|
|
bom = BillOfMaterials(
|
|
manufactured_product=manufactured_product,
|
|
component=component,
|
|
quantity=quantity,
|
|
unit=unit
|
|
)
|
|
bom.save()
|
|
saved_components.append(bom)
|
|
|
|
messages.success(request, f'Bill of Materials for "{manufactured_product.name}" with {len(saved_components)} components created successfully!')
|
|
return redirect('manufacture:bom_list')
|
|
else:
|
|
main_form = MultipleBillOfMaterialsForm()
|
|
component_formset = BOMComponentFormSet()
|
|
|
|
return render(request, 'manufacture/bom_form.html', {
|
|
'main_form': main_form,
|
|
'component_formset': component_formset,
|
|
'title': 'Create Bill of Materials'
|
|
})
|
|
|
|
|
|
@login_required
|
|
def bom_edit(request, pk):
|
|
"""Edit an existing bill of materials entry"""
|
|
if not has_manufacturing_access(request.user):
|
|
messages.error(request, 'You do not have permission to edit bill of materials.')
|
|
return redirect('manufacture:bom_list')
|
|
|
|
# Get the BOM entry to edit
|
|
bom = get_object_or_404(BillOfMaterials, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
main_form = MultipleBillOfMaterialsForm(request.POST)
|
|
component_formset = BOMComponentFormSet(request.POST)
|
|
|
|
if main_form.is_valid() and component_formset.is_valid():
|
|
manufactured_product = main_form.cleaned_data['manufactured_product']
|
|
|
|
# Delete existing BOM entries for this manufactured product
|
|
BillOfMaterials.objects.filter(manufactured_product=manufactured_product).delete()
|
|
|
|
# Save all component entries
|
|
saved_components = []
|
|
for component_form in component_formset:
|
|
# Skip empty forms
|
|
if not component_form.cleaned_data:
|
|
continue
|
|
|
|
component = component_form.cleaned_data['component']
|
|
quantity = component_form.cleaned_data['quantity']
|
|
unit = component_form.cleaned_data['unit']
|
|
|
|
# Create BOM entry
|
|
bom_entry = BillOfMaterials(
|
|
manufactured_product=manufactured_product,
|
|
component=component,
|
|
quantity=quantity,
|
|
unit=unit
|
|
)
|
|
bom_entry.save()
|
|
saved_components.append(bom_entry)
|
|
|
|
messages.success(request, f'Bill of Materials for "{manufactured_product.name}" with {len(saved_components)} components updated successfully!')
|
|
return redirect('manufacture:bom_list')
|
|
else:
|
|
# Pre-populate the form with existing components
|
|
main_form = MultipleBillOfMaterialsForm(initial={'manufactured_product': bom.manufactured_product})
|
|
|
|
# Get all existing components for this manufactured product
|
|
existing_components = BillOfMaterials.objects.filter(manufactured_product=bom.manufactured_product)
|
|
|
|
# Create initial data for the formset
|
|
initial_data = []
|
|
for existing_bom in existing_components:
|
|
initial_data.append({
|
|
'component': existing_bom.component,
|
|
'quantity': existing_bom.quantity,
|
|
'unit': existing_bom.unit
|
|
})
|
|
|
|
# Add extra empty forms if needed
|
|
while len(initial_data) < 1:
|
|
initial_data.append({})
|
|
|
|
component_formset = BOMComponentFormSet(initial=initial_data)
|
|
|
|
return render(request, 'manufacture/bom_form.html', {
|
|
'main_form': main_form,
|
|
'component_formset': component_formset,
|
|
'title': 'Edit Bill of Materials',
|
|
'bom': bom
|
|
})
|
|
|
|
|
|
@login_required
|
|
def bom_delete(request, pk):
|
|
"""Delete a bill of materials entry"""
|
|
if not has_manufacturing_access(request.user):
|
|
messages.error(request, 'You do not have permission to delete bill of materials entries.')
|
|
return redirect('manufacture:bom_list')
|
|
|
|
bom = get_object_or_404(BillOfMaterials, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
manufactured_product_name = bom.manufactured_product.name
|
|
bom.delete()
|
|
messages.success(request, f'Bill of Materials entry for "{manufactured_product_name}" deleted successfully!')
|
|
return redirect('manufacture:bom_list')
|
|
|
|
return render(request, 'manufacture/bom_confirm_delete.html', {'bom': bom})
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class ManufactureDetailView(DetailView):
|
|
model = ManufacturingOrder
|
|
template_name = 'manufacture/manufacture_detail.html'
|
|
context_object_name = 'manufacturing_order'
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class BillOfMaterialsDetailView(DetailView):
|
|
model = BillOfMaterials
|
|
template_name = 'manufacture/bom_detail.html'
|
|
context_object_name = 'bom'
|
|
|
|
@login_required
|
|
def get_product_info(request, product_id):
|
|
"""Return product information including unit as JSON"""
|
|
if not has_manufacturing_access(request.user):
|
|
return JsonResponse({'error': 'Permission denied'}, status=403)
|
|
|
|
try:
|
|
product = Product.objects.get(id=product_id)
|
|
return JsonResponse({
|
|
'id': product.id,
|
|
'name': product.name,
|
|
'unit': product.unit,
|
|
'unit_display': product.get_unit_display()
|
|
})
|
|
except Product.DoesNotExist:
|
|
return JsonResponse({'error': 'Product not found'}, status=404)
|