from django.db import models from django.core.validators import MinValueValidator from django.utils.translation import gettext_lazy as _ from decimal import Decimal class ManufacturingOrder(models.Model): """Simple manufacturing order without BOM - just input end product result""" ORDER_STATUS = [ ('completed', 'Completed'), ('in_progress', 'In Progress'), ('cancelled', 'Cancelled'), ] order_number = models.CharField(max_length=50, unique=True, help_text='Unique manufacturing order number') date = models.DateField() product = models.ForeignKey('inventory.Product', on_delete=models.CASCADE, related_name='manufacturing_orders') quantity = models.DecimalField( max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal('0.01'))] ) # Manufacturing details notes = models.TextField(blank=True, help_text='Manufacturing notes and instructions') status = models.CharField(max_length=20, choices=ORDER_STATUS, default='completed') # Cost tracking (optional) labor_cost = models.DecimalField( max_digits=10, decimal_places=2, default=0, validators=[MinValueValidator(Decimal('0'))] ) overhead_cost = models.DecimalField( max_digits=10, decimal_places=2, default=0, validators=[MinValueValidator(Decimal('0'))] ) total_cost = models.DecimalField( max_digits=10, decimal_places=2, default=0, validators=[MinValueValidator(Decimal('0'))] ) # User tracking created_by = models.ForeignKey('users.CustomUser', on_delete=models.SET_NULL, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-date', '-created_at'] verbose_name = _('Manufacturing Order') verbose_name_plural = _('Manufacturing Orders') def __str__(self): return f"MO-{self.order_number} - {self.product.name} ({self.quantity})" def save(self, *args, **kwargs): """Override save to calculate total cost and update product stock""" # Calculate total cost self.total_cost = self.labor_cost + self.overhead_cost # If this is a new order and status is completed, add to inventory if not self.pk and self.status == 'completed': # Update product stock self.product.current_stock += self.quantity # Update product cost price if this is the first manufacturing order if self.product.manufacturing_orders.count() == 0: self.product.cost_price = self.total_cost / self.quantity else: # Calculate weighted average cost total_quantity = self.product.manufacturing_orders.aggregate( total=models.Sum('quantity') )['total'] or 0 total_cost = self.product.manufacturing_orders.aggregate( total=models.Sum('total_cost') )['total'] or 0 if total_quantity > 0: self.product.cost_price = total_cost / total_quantity self.product.save() super().save(*args, **kwargs) def get_unit_cost(self): """Calculate unit cost for this manufacturing order""" if self.quantity > 0: return self.total_cost / self.quantity return Decimal('0') def get_profit_margin(self): """Calculate potential profit margin if product has selling price""" if self.product.selling_price > 0: unit_cost = self.get_unit_cost() if unit_cost > 0: return ((self.product.selling_price - unit_cost) / unit_cost) * 100 return Decimal('0') class ManufacturingLine(models.Model): """Manufacturing line/workstation information""" name = models.CharField(max_length=100) description = models.TextField(blank=True) capacity_per_hour = models.DecimalField( max_digits=8, decimal_places=2, blank=True, null=True, help_text='Production capacity per hour' ) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['name'] verbose_name = _('Manufacturing Line') verbose_name_plural = _('Manufacturing Lines') def __str__(self): return self.name class ManufacturingOrderLine(models.Model): """Individual line items for manufacturing orders (optional for future expansion)""" manufacturing_order = models.ForeignKey(ManufacturingOrder, on_delete=models.CASCADE, related_name='lines') manufacturing_line = models.ForeignKey(ManufacturingLine, on_delete=models.SET_NULL, null=True, blank=True) start_time = models.DateTimeField(blank=True, null=True) end_time = models.DateTimeField(blank=True, null=True) actual_quantity = models.DecimalField( max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal('0'))] ) notes = models.TextField(blank=True) class Meta: verbose_name = _('Manufacturing Order Line') verbose_name_plural = _('Manufacturing Order Lines') def __str__(self): return f"{self.manufacturing_order.order_number} - Line {self.id}" def get_duration(self): """Calculate duration of manufacturing line operation""" if self.start_time and self.end_time: return self.end_time - self.start_time return None