17 KiB
17 KiB
Django Manufacturing App - Database Models Design
Overview
This document details the database models for each module of the manufacturing application. The models are designed to support all required functionality while maintaining data integrity and relationships.
Core Models
1. User Management Models
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
phone = models.CharField(max_length=20, blank=True)
department = models.CharField(max_length=100, blank=True)
position = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
class Role(models.Model):
name = models.CharField(max_length=50, unique=True)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class UserRole(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
assigned_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('user', 'role')
class Permission(models.Model):
name = models.CharField(max_length=100, unique=True)
codename = models.CharField(max_length=100, unique=True)
module = models.CharField(max_length=50) # e.g., 'inventory', 'sales'
description = models.TextField(blank=True)
class RolePermission(models.Model):
role = models.ForeignKey(Role, on_delete=models.CASCADE)
permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
class Meta:
unique_together = ('role', 'permission')
2. Inventory Management Models
# inventory/models.py
from django.db import models
from django.core.validators import MinValueValidator
class Category(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class UnitOfMeasure(models.Model):
name = models.CharField(max_length=50)
abbreviation = models.CharField(max_length=10)
def __str__(self):
return self.name
class Product(models.Model):
PRODUCT_TYPES = [
('raw_material', 'Raw Material'),
('finished_good', 'Finished Good'),
('component', 'Component'),
]
code = models.CharField(max_length=50, unique=True)
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
unit_of_measure = models.ForeignKey(UnitOfMeasure, on_delete=models.SET_NULL, null=True)
product_type = models.CharField(max_length=20, choices=PRODUCT_TYPES)
reorder_level = models.DecimalField(max_digits=10, decimal_places=2, default=0)
selling_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
cost_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.code} - {self.name}"
class Warehouse(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=200)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Inventory(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=10, decimal_places=2, default=0)
reserved_quantity = models.DecimalField(max_digits=10, decimal_places=2, default=0)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('product', 'warehouse')
@property
def available_quantity(self):
return self.quantity - self.reserved_quantity
class StockMovement(models.Model):
MOVEMENT_TYPES = [
('in', 'Stock In'),
('out', 'Stock Out'),
('adjustment', 'Adjustment'),
('transfer', 'Transfer'),
]
product = models.ForeignKey(Product, on_delete=models.CASCADE)
warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
movement_type = models.CharField(max_length=20, choices=MOVEMENT_TYPES)
quantity = models.DecimalField(max_digits=10, decimal_places=2)
reference_number = models.CharField(max_length=100, blank=True)
notes = models.TextField(blank=True)
created_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
3. Purchase Management Models
# purchasing/models.py
from django.db import models
class Supplier(models.Model):
code = models.CharField(max_length=50, unique=True)
name = models.CharField(max_length=200)
contact_person = models.CharField(max_length=100, blank=True)
email = models.EmailField(blank=True)
phone = models.CharField(max_length=20, blank=True)
address = models.TextField(blank=True)
tax_id = models.CharField(max_length=50, blank=True)
payment_terms = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class PurchaseOrder(models.Model):
STATUS_CHOICES = [
('ordered', 'Ordered'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
]
po_number = models.CharField(max_length=50, unique=True)
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
order_date = models.DateField()
expected_delivery_date = models.DateField(null=True, blank=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='ordered')
subtotal = models.DecimalField(max_digits=12, decimal_places=2, default=0)
tax_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
total_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
notes = models.TextField(blank=True)
created_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.po_number
class PurchaseOrderItem(models.Model):
po = models.ForeignKey(PurchaseOrder, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey('inventory.Product', on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=10, decimal_places=2)
unit_price = models.DecimalField(max_digits=10, decimal_places=2)
total_price = models.DecimalField(max_digits=12, decimal_places=2)
received_quantity = models.DecimalField(max_digits=10, decimal_places=2, default=0)
def save(self, *args, **kwargs):
self.total_price = self.quantity * self.unit_price
super().save(*args, **kwargs)
class GoodsReceipt(models.Model):
gr_number = models.CharField(max_length=50, unique=True)
po = models.ForeignKey(PurchaseOrder, on_delete=models.CASCADE)
receipt_date = models.DateField()
received_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.gr_number
class GoodsReceiptItem(models.Model):
receipt = models.ForeignKey(GoodsReceipt, on_delete=models.CASCADE, related_name='items')
po_item = models.ForeignKey(PurchaseOrderItem, on_delete=models.CASCADE)
received_quantity = models.DecimalField(max_digits=10, decimal_places=2)
notes = models.TextField(blank=True)
4. Sales Management Models
# sales/models.py
from django.db import models
class Customer(models.Model):
code = models.CharField(max_length=50, unique=True)
name = models.CharField(max_length=200)
contact_person = models.CharField(max_length=100, blank=True)
email = models.EmailField(blank=True)
phone = models.CharField(max_length=20, blank=True)
address = models.TextField(blank=True)
tax_id = models.CharField(max_length=50, blank=True)
payment_terms = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class SalesOrder(models.Model):
STATUS_CHOICES = [
('processing', 'Processing'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
]
so_number = models.CharField(max_length=50, unique=True)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
order_date = models.DateField()
expected_delivery_date = models.DateField(null=True, blank=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='processing')
subtotal = models.DecimalField(max_digits=12, decimal_places=2, default=0)
tax_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
total_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
notes = models.TextField(blank=True)
created_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.so_number
class SalesOrderItem(models.Model):
so = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey('inventory.Product', on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=10, decimal_places=2)
unit_price = models.DecimalField(max_digits=10, decimal_places=2)
total_price = models.DecimalField(max_digits=12, decimal_places=2)
shipped_quantity = models.DecimalField(max_digits=10, decimal_places=2, default=0)
def save(self, *args, **kwargs):
self.total_price = self.quantity * self.unit_price
super().save(*args, **kwargs)
class Delivery(models.Model):
delivery_number = models.CharField(max_length=50, unique=True)
so = models.ForeignKey(SalesOrder, on_delete=models.CASCADE)
delivery_date = models.DateField()
delivered_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.delivery_number
class DeliveryItem(models.Model):
delivery = models.ForeignKey(Delivery, on_delete=models.CASCADE, related_name='items')
so_item = models.ForeignKey(SalesOrderItem, on_delete=models.CASCADE)
delivered_quantity = models.DecimalField(max_digits=10, decimal_places=2)
notes = models.TextField(blank=True)
5. Manufacturing Management Models
# manufacturing/models.py
from django.db import models
class BillOfMaterial(models.Model):
product = models.ForeignKey('inventory.Product', on_delete=models.CASCADE, related_name='boms')
bom_code = models.CharField(max_length=50, unique=True)
version = models.CharField(max_length=20, default='1.0')
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.bom_code} - {self.product.name}"
class BOMItem(models.Model):
bom = models.ForeignKey(BillOfMaterial, on_delete=models.CASCADE, related_name='items')
component = models.ForeignKey('inventory.Product', on_delete=models.CASCADE, related_name='bom_components')
quantity = models.DecimalField(max_digits=10, decimal_places=2)
unit_of_measure = models.ForeignKey('inventory.UnitOfMeasure', on_delete=models.SET_NULL, null=True)
class Meta:
unique_together = ('bom', 'component')
class ManufacturingOrder(models.Model):
STATUS_CHOICES = [
('in_progress', 'In Progress'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
]
mo_number = models.CharField(max_length=50, unique=True)
bom = models.ForeignKey(BillOfMaterial, on_delete=models.CASCADE)
quantity_to_produce = models.DecimalField(max_digits=10, decimal_places=2)
scheduled_start_date = models.DateField()
scheduled_end_date = models.DateField()
actual_start_date = models.DateField(null=True, blank=True)
actual_end_date = models.DateField(null=True, blank=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='in_progress')
created_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.mo_number
class MOComponent(models.Model):
mo = models.ForeignKey(ManufacturingOrder, on_delete=models.CASCADE, related_name='components')
component = models.ForeignKey('inventory.Product', on_delete=models.CASCADE)
required_quantity = models.DecimalField(max_digits=10, decimal_places=2)
consumed_quantity = models.DecimalField(max_digits=10, decimal_places=2, default=0)
class Meta:
unique_together = ('mo', 'component')
6. Database Management Models
# database_management/models.py
from django.db import models
class DatabaseBackup(models.Model):
BACKUP_STATUS = [
('pending', 'Pending'),
('in_progress', 'In Progress'),
('completed', 'Completed'),
('failed', 'Failed'),
]
filename = models.CharField(max_length=25)
file_path = models.CharField(max_length=500)
size = models.BigIntegerField()
status = models.CharField(max_length=20, choices=BACKUP_STATUS)
created_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
completed_at = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.filename
7. Reporting Models
# reports/models.py
from django.db import models
class ReportTemplate(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
template_file = models.FileField(upload_to='report_templates/')
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class GeneratedReport(models.Model):
REPORT_FORMATS = [
('pdf', 'PDF'),
('excel', 'Excel'),
('csv', 'CSV'),
]
report_template = models.ForeignKey(ReportTemplate, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
format = models.CharField(max_length=10, choices=REPORT_FORMATS)
file_path = models.CharField(max_length=500)
size = models.BigIntegerField()
generated_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True)
generated_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
8. Dashboard Models
# dashboard/models.py
from django.db import models
class DashboardWidget(models.Model):
WIDGET_TYPES = [
('chart', 'Chart'),
('table', 'Table'),
('kpi', 'KPI'),
('summary', 'Summary'),
]
name = models.CharField(max_length=100)
title = models.CharField(max_length=200)
widget_type = models.CharField(max_length=20, choices=WIDGET_TYPES)
config = models.JSONField() # Store widget configuration as JSON
is_active = models.BooleanField(default=True)
order = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['order']
class UserDashboard(models.Model):
user = models.ForeignKey('accounts.User', on_delete=models.CASCADE)
widgets = models.ManyToManyField(DashboardWidget, through='UserDashboardWidget')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class UserDashboardWidget(models.Model):
user_dashboard = models.ForeignKey(UserDashboard, on_delete=models.CASCADE)
widget = models.ForeignKey(DashboardWidget, on_delete=models.CASCADE)
position = models.IntegerField()
is_visible = models.BooleanField(default=True)
class Meta:
unique_together = ('user_dashboard', 'widget')
ordering = ['position']