ধন্যবাদ আপনার প্রয়োজনীয়তার জন্য! 😊 আপনি চান Production Floor Control মডিউলের জন্য একটি সম্পূর্ণ মডেল, যেখানে অ্যাপের নাম হবে production এবং নিম্নলিখিত ফিচারগুলো থাকবে:
- প্রতিটি ফ্লোরে স্ক্যানিং: বারকোড স্ক্যান করে বান্ডিল ট্র্যাক করা।
- ফ্লোর-টু-ফ্লোর ট্রান্সফার: প্রতিটি ফ্লোরের জন্য আলাদা এন্ট্রি, ট্রান্সফার ট্র্যাকিং।
- একটি ফর্ম: স্ক্যানিং, ট্রান্সফার, এবং রিসিপ্ট একটি ফর্মের মাধ্যমে ম্যানেজ করা।
- প্রোডাকশন অর্ডার এবং রিসিপ্ট: প্রোডাকশন অর্ডার এবং রিসিপ্টের মাধ্যমে কাজ ট্র্যাক করা।
- অর্ডার ম্যানেজমেন্ট ইন্টিগ্রেশন: আপনার দেওয়া
order_management/models.pyএর সাথে সামঞ্জস্য, যেখানে একই স্টাইল একাধিক অর্ডারে থাকতে পারে। - প্রোডাকশন রিপোর্ট এবং কোয়েরি: ফ্লোর, অর্ডার, এবং স্টাইল-ভিত্তিক রিয়েল-টাইম তথ্য।
আমি আপনার পূর্ববর্তী প্রয়োজনীয়তা এবং order_management/models.py বিশ্লেষণ করেছি। বর্তমান মডেল ইতিমধ্যে উপযুক্ত, তবে অ্যাপের নাম production করার জন্য মডেল, ফর্ম, অ্যাডমিন, এবং ফিক্সচার ফাইলগুলো আপডেট করছি। নিচে নতুন গল্প এবং সম্পূর্ণ মডেল দেওয়া হলো।
🎬 গল্প: “The Seamless Flow of Production”
মডিউল: Production (Production Floor Control)
উদ্দেশ্য: প্রোডাকশন ফ্লোরে কাটিং, সেলাই, ফিনিশিং, এবং প্যাকিং প্রক্রিয়া ট্র্যাক করা, বারকোড স্ক্যানিং, ফ্লোর-টু-ফ্লোর ট্রান্সফার, এবং একটি ফর্মের মাধ্যমে এন্ট্রি ম্যানেজমেন্ট। ফিচারগুলো:
- Barcode-based Bundle Tracking: বারকোড দিয়ে বান্ডিল ট্র্যাকিং।
- Floor-specific Entries: প্রতিটি ফ্লোরের জন্য আলাদা এন্ট্রি।
- Single Form Management: একটি ফর্ম দিয়ে স্ক্যানিং, ট্রান্সফার, এবং রিসিপ্ট এন্ট্রি।
- Production Order and Receipt: প্রোডাকশন অর্ডার এবং রিসিপ্টের মাধ্যমে কাজ ট্র্যাকিং।
- Line Wise Dashboard: ফ্লোর-ভিত্তিক রিয়েল-টাইম স্ট্যাটাস।
- Rework/Rejected Tracking: ফ্লোর-ভিত্তিক ত্রুটিপূর্ণ/প্রত্যাখ্যাত পিস ট্র্যাকিং।
- WIP Status: ফ্লোর এবং অর্ডার-ভিত্তিক প্রোডাকশন অগ্রগতি।
- Production Report: ফ্লোর, অর্ডার, এবং স্টাইল-ভিত্তিক রিপোর্ট।
- Order-based Query: যেকোনো সময় অর্ডার বা স্টাইলের তথ্য দেখা।
🎭 চরিত্র পরিচিতি
| নাম | পদের নাম | দায়িত্ব |
|---|---|---|
| Nadia | Merchandiser | অর্ডার এবং স্টাইল তথ্য প্রদান |
| Karim | Inventory Controller | ফ্যাব্রিক ইস্যু এবং স্টক ম্যানেজমেন্ট |
| Faisal | Floor Supervisor | বারকোড স্ক্যানিং এবং ফ্লোর ট্রান্সফার |
| Ruma | Production Manager | ফ্লোর মনিটরিং, রিপোর্টিং, এবং WIP স্ট্যাটাস |
| Factory Z | Production Warehouse | প্রোডাকশন গুদাম (ফ্যাব্রিক স্টোরেজ) |
| LMN Buyer | Buyer | অর্ডার দাতা (LMN-BLUE-JACKET-2025) |
🧩 গল্পের ধাপগুলো
🧾 ধাপ ১: অর্ডার এবং ফ্যাব্রিক ইস্যু
Nadia, মার্চেন্ডাইজার, LMN Buyer-এর দুটি অর্ডার তৈরি করেন:
- Order 1: LMN-BLUE-JACKET-2025, 12000 জ্যাকেট (M, L, XL)।
- Order 2: LMN-BLUE-JACKET-2025, 8000 জ্যাকেট (S, M)।
Karim, Inventory Controller, Factory Z থেকে 1500 মিটার Polyester Blue Fabric ইস্যু করেন (Order 1-এর জন্য 1000 মিটার, Order 2-এর জন্য 500 মিটার)।
📦 Table: order_management_order
| id | buyer_id | quantity | sizes | colors | delivery_date | order_date | status | remarks |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 12000 | M,L,XL | Blue | 2025-09-25 | 2025-08-04 | received | Managed by Nadia |
| 2 | 1 | 8000 | S,M | Blue | 2025-10-10 | 2025-08-04 | received | Managed by Nadia |
📦 Table: order_management_orderstyle
| id | order_id | style_name |
|---|---|---|
| 1 | 1 | LMN-BLUE-JACKET-2025 |
| 2 | 2 | LMN-BLUE-JACKET-2025 |
📦 Table: inventory_goodsissue
| id | issue_id | item_id | warehouse_id | quantity | unit | issue_date | status | approved_by | style_id | remarks |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | IS-2025-001 | 1 | 1 (Factory Z) | 1000.00 | meter | 2025-08-04 | approved | Karim | 1 | Issued for Order 1 |
| 2 | IS-2025-002 | 1 | 1 (Factory Z) | 500.00 | meter | 2025-08-04 | approved | Karim | 2 | Issued for Order 2 |
🧾 ধাপ ২: প্রোডাকশন অর্ডার তৈরি
Ruma একটি Production Order তৈরি করেন, যা অর্ডার এবং স্টাইলের উপর ভিত্তি করে ফ্লোরে কাজ শুরু করে। Order 1-এর জন্য 50টি বান্ডিল এবং Order 2-এর জন্য 25টি বান্ডিল।
📦 Table: production_productionorder
| id | production_order_id | order_id | style_id | total_bundles | quantity | unit | issue_date | status | created_by | remarks |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | PO-2025-001 | 1 | 1 | 50 | 1000.00 | meter | 2025-08-04 | active | Ruma | Order 1, LMN-BLUE-JACKET-2025 |
| 2 | PO-2025-002 | 2 | 2 | 25 | 500.00 | meter | 2025-08-04 | active | Ruma | Order 2, LMN-BLUE-JACKET-2025 |
🧾 ধাপ ৩: বান্ডিল ট্র্যাকিং এবং ফ্লোর এন্ট্রি
Faisal প্রোডাকশন অর্ডারের বান্ডিলগুলো কাটিং ফ্লোরে (Floor 1) স্ক্যান করে। প্রতিটি বান্ডিলে 20 মিটার ফ্যাব্রিক এবং একটি বারকোড। Order 1-এর 20টি বান্ডিল Floor 2 (সেলাই) তে ট্রান্সফার হয়, যেখানে Ruma রিসিভ করেন।
📦 Table: production_bundletracking
| id | bundle_id | production_order_id | order_id | style_id | quantity | unit | barcode | current_floor | current_stage | date | scanned_by | status | remarks |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | BUN-2025-001 | PO-2025-001 | 1 | 1 | 20.00 | meter | BAR-2025-001 | Floor 1 | cutting | 2025-08-04 | Faisal | active | Order 1, Floor 1, Jacket |
| 2 | BUN-2025-002 | PO-2025-001 | 1 | 1 | 20.00 | meter | BAR-2025-002 | Floor 2 | sewing | 2025-08-04 | Faisal | active | Order 1, Transferred to Floor 2 |
| 3 | BUN-2025-003 | PO-2025-002 | 2 | 2 | 20.00 | meter | BAR-2025-003 | Floor 1 | cutting | 2025-08-04 | Faisal | active | Order 2, Floor 1, Jacket |
📦 Table: production_bundletransfer
| id | bundle_id | production_order_id | order_id | style_id | from_floor | to_floor | quantity | unit | transfer_date | delivered_by | received_by | remarks |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | BUN-2025-002 | PO-2025-001 | 1 | 1 | Floor 1 | Floor 2 | 20.00 | meter | 2025-08-04 | Faisal | Ruma | Order 1, Transferred to Sewing |
🧾 ধাপ ৪: প্রোডাকশন রিসিপ্ট
Faisal প্রতিটি ফ্লোরে কাজ শেষ হলে Production Receipt তৈরি করে। Floor 1-এ Order 1-এর 30টি বান্ডিল কাটিং সম্পন্ন, Floor 2-এ 20টি বান্ডিল সেলাই সম্পন্ন।
📦 Table: production_productionreceipt
| id | receipt_id | production_order_id | order_id | style_id | floor_name | quantity | unit | stage | receipt_date | created_by | remarks |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | REC-2025-001 | PO-2025-001 | 1 | 1 | Floor 1 | 600.00 | meter | cutting | 2025-08-04 | Faisal | Order 1, Cutting completed |
| 2 | REC-2025-002 | PO-2025-001 | 1 | 1 | Floor 2 | 400.00 | meter | sewing | 2025-08-04 | Faisal | Order 1, Sewing completed |
🧾 ধাপ ৫: একক ফর্ম ম্যানেজমেন্ট
Faisal একটি ফর্ম ব্যবহার করে:
- স্ক্যানিং: বারকোড স্ক্যান করে বান্ডিল এন্ট্রি।
- ট্রান্সফার: ফ্লোর-টু-ফ্লোর ট্রান্সফার এন্ট্রি।
- রিসিপ্ট: ফ্লোরে কাজ সম্পন্ন হলে রিসিপ্ট এন্ট্রি।
Ruma ফর্মটি ব্যবহার করে রিসিভ এন্ট্রি এবং রিপোর্ট চেক করে।
🧾 ধাপ ৬: লাইন ওয়াইজ ড্যাশবোর্ড
Ruma Line Wise Dashboard এ দেখেন:
- Floor 1: Order 1 (30 বান্ডিল, কাটিং), Order 2 (25 বান্ডিল, কাটিং)।
- Floor 2: Order 1 (20 বান্ডিল, সেলাই)।
📦 Table: production_linedashboard
| id | floor_name | order_id | style_id | total_bundles | active_bundles | completed_bundles | rejected_bundles | wip_stage | last_updated |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Floor 1 | 1 | 1 | 30 | 30 | 0 | 0 | cutting | 2025-08-04T13:00:00Z |
| 2 | Floor 1 | 2 | 2 | 25 | 25 | 0 | 0 | cutting | 2025-08-04T13:00:00Z |
| 3 | Floor 2 | 1 | 1 | 20 | 20 | 0 | 0 | sewing | 2025-08-04T13:00:00Z |
🧾 ধাপ ৭: রিওয়ার্ক/প্রত্যাখ্যান ট্র্যাকিং
Faisal Floor 2-এ বান্ডিল BUN-2025-002 (Order 1) স্ক্যান করে এবং ৫টি জ্যাকেটে সেলাই ত্রুটি দেখেন, যা রিওয়ার্কে পাঠানো হয়। Ruma Floor 1-এ বান্ডিল BUN-2025-001 (Order 1) থেকে ৩টি জ্যাকেট প্রত্যাখ্যান করেন ফ্যাব্রিক দাগের কারণে।
📦 Table: production_reworkrejectedtracking
| id | bundle_id | production_order_id | order_id | style_id | floor_name | quantity | reason | action | date | reported_by | remarks |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | BUN-2025-002 | PO-2025-001 | 1 | 1 | Floor 2 | 5 | Stitching error | rework | 2025-08-04 | Faisal | Order 1, Sent for rework |
| 2 | BUN-2025-001 | PO-2025-001 | 1 | 1 | Floor 1 | 3 | Fabric defect | rejected | 2025-08-04 | Ruma | Order 1, Rejected due to issue |
🧾 ধাপ ৮: WIP স্ট্যাটাস
Ruma WIP স্ট্যাটাস চেক করেন:
- Floor 1: Order 1 (30 বান্ডিল, 27 কাটিংয়ে, 3 প্রত্যাখ্যাত), Order 2 (25 বান্ডিল, কাটিংয়ে)।
- Floor 2: Order 1 (20 বান্ডিল, 15 সেলাইয়ে, 5 রিওয়ার্কে)।
📦 Table: production_wipstatus
| id | floor_name | order_id | style_id | total_bundles | cutting | sewing | finishing | packing | rework | rejected | last_updated |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Floor 1 | 1 | 1 | 30 | 27 | 0 | 0 | 0 | 0 | 3 | 2025-08-04T13:30:00Z |
| 2 | Floor 1 | 2 | 2 | 25 | 25 | 0 | 0 | 0 | 0 | 0 | 2025-08-04T13:30:00Z |
| 3 | Floor 2 | 1 | 1 | 20 | 0 | 15 | 0 | 0 | 5 | 0 | 2025-08-04T13:30:00Z |
🧾 ধাপ ৯: প্রোডাকশন রিপোর্ট এবং অর্ডার কোয়েরি
Ruma Production Report তৈরি করেন:
- Floor 1: Order 1 (30 বান্ডিল, 27 কাটিং, 3 প্রত্যাখ্যাত), Order 2 (25 বান্ডিল, কাটিং)।
- Floor 2: Order 1 (20 বান্ডিল, 15 সেলাই, 5 রিওয়ার্ক)।
- কোয়েরি: Ruma অর্ডার 1 চেক করেন: 30 বান্ডিল Floor 1-এ (27 কাটিং, 3 প্রত্যাখ্যাত), 20 বান্ডিল Floor 2-এ (15 সেলাই, 5 রিওয়ার্ক)।
📦 Table: production_productionreport
| id | order_id | style_id | floor_name | total_bundles | cutting | sewing | finishing | packing | rework | rejected | report_date | generated_by | remarks |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 1 | Floor 1 | 30 | 27 | 0 | 0 | 0 | 0 | 3 | 2025-08-04 | Ruma | Order 1, Floor 1 Report |
| 2 | 2 | 2 | Floor 1 | 25 | 25 | 0 | 0 | 0 | 0 | 0 | 2025-08-04 | Ruma | Order 2, Floor 1 Report |
| 3 | 1 | 1 | Floor 2 | 20 | 0 | 15 | 0 | 0 | 5 | 0 | 2025-08-04 | Ruma | Order 1, Floor 2 Report |
📊 ইন্টিগ্রেশন ফ্লো
[LMN Buyer: Orders] → [Nadia: OrderStyle for Orders] → [Karim: GoodsIssue] → [Ruma: ProductionOrder] → [Faisal: BundleTracking, BundleTransfer, ProductionReceipt via Form] → [Ruma: LineDashboard, ReworkRejectedTracking, WIPStatus, ProductionReport]
🧰 Django মডেল
মডেল: production/models.py
from django.db import models
from django.contrib.auth.models import User
from order_management.models import Order, OrderStyle
class ProductionOrder(models.Model):
STATUS_CHOICES = (
('active', 'Active'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
)
production_order_id = models.CharField(max_length=20, unique=True, verbose_name="Production Order ID")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
total_bundles = models.IntegerField(verbose_name="Total Bundles")
quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Quantity")
unit = models.CharField(max_length=20, verbose_name="Unit")
issue_date = models.DateField(verbose_name="Issue Date")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active', verbose_name="Status")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="Created By")
remarks = models.TextField(blank=True, verbose_name="Remarks")
class Meta:
verbose_name = "Production Order"
verbose_name_plural = "Production Orders"
def __str__(self):
return f"{self.production_order_id} - Order {self.order.id}"
class BundleTracking(models.Model):
STAGE_CHOICES = (
('cutting', 'Cutting'),
('sewing', 'Sewing'),
('finishing', 'Finishing'),
('packing', 'Packing'),
)
STATUS_CHOICES = (
('active', 'Active'),
('completed', 'Completed'),
('rejected', 'Rejected'),
)
bundle_id = models.CharField(max_length=20, unique=True, verbose_name="Bundle ID")
production_order = models.ForeignKey(ProductionOrder, on_delete=models.CASCADE, verbose_name="Production Order")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Quantity")
unit = models.CharField(max_length=20, verbose_name="Unit")
barcode = models.CharField(max_length=50, unique=True, verbose_name="Barcode")
current_floor = models.CharField(max_length=100, verbose_name="Current Floor")
current_stage = models.CharField(max_length=20, choices=STAGE_CHOICES, verbose_name="Current Stage")
date = models.DateField(verbose_name="Date")
scanned_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='scanned_bundles', verbose_name="Scanned By")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active', verbose_name="Status")
remarks = models.TextField(blank=True, verbose_name="Remarks")
class Meta:
verbose_name = "Bundle Tracking"
verbose_name_plural = "Bundle Trackings"
def __str__(self):
return f"{self.bundle_id} - Order {self.order.id} - {self.current_floor}"
class BundleTransfer(models.Model):
bundle = models.ForeignKey(BundleTracking, on_delete=models.CASCADE, verbose_name="Bundle")
production_order = models.ForeignKey(ProductionOrder, on_delete=models.CASCADE, verbose_name="Production Order")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
from_floor = models.CharField(max_length=100, verbose_name="From Floor")
to_floor = models.CharField(max_length=100, verbose_name="To Floor")
quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Quantity")
unit = models.CharField(max_length=20, verbose_name="Unit")
transfer_date = models.DateField(verbose_name="Transfer Date")
delivered_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='delivered_transfers', verbose_name="Delivered By")
received_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='received_transfers', verbose_name="Received By")
remarks = models.TextField(blank=True, verbose_name="Remarks")
class Meta:
verbose_name = "Bundle Transfer"
verbose_name_plural = "Bundle Transfers"
def __str__(self):
return f"{self.bundle.bundle_id} - {self.from_floor} to {self.to_floor} - Order {self.order.id}"
class ProductionReceipt(models.Model):
STAGE_CHOICES = (
('cutting', 'Cutting'),
('sewing', 'Sewing'),
('finishing', 'Finishing'),
('packing', 'Packing'),
)
receipt_id = models.CharField(max_length=20, unique=True, verbose_name="Receipt ID")
production_order = models.ForeignKey(ProductionOrder, on_delete=models.CASCADE, verbose_name="Production Order")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
floor_name = models.CharField(max_length=100, verbose_name="Floor Name")
quantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Quantity")
unit = models.CharField(max_length=20, verbose_name="Unit")
stage = models.CharField(max_length=20, choices=STAGE_CHOICES, verbose_name="Stage")
receipt_date = models.DateField(verbose_name="Receipt Date")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="Created By")
remarks = models.TextField(blank=True, verbose_name="Remarks")
class Meta:
verbose_name = "Production Receipt"
verbose_name_plural = "Production Receipts"
def __str__(self):
return f"{self.receipt_id} - Order {self.order.id} - {self.floor_name}"
class LineDashboard(models.Model):
STAGE_CHOICES = (
('cutting', 'Cutting'),
('sewing', 'Sewing'),
('finishing', 'Finishing'),
('packing', 'Packing'),
)
floor_name = models.CharField(max_length=100, verbose_name="Floor Name")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
total_bundles = models.IntegerField(verbose_name="Total Bundles")
active_bundles = models.IntegerField(verbose_name="Active Bundles")
completed_bundles = models.IntegerField(verbose_name="Completed Bundles")
rejected_bundles = models.IntegerField(verbose_name="Rejected Bundles")
wip_stage = models.CharField(max_length=20, choices=STAGE_CHOICES, verbose_name="WIP Stage")
last_updated = models.DateTimeField(auto_now=True, verbose_name="Last Updated")
class Meta:
verbose_name = "Line Dashboard"
verbose_name_plural = "Line Dashboards"
def __str__(self):
return f"{self.floor_name} - Order {self.order.id} - {self.wip_stage}"
class ReworkRejectedTracking(models.Model):
ACTION_CHOICES = (
('rework', 'Rework'),
('rejected', 'Rejected'),
)
bundle = models.ForeignKey(BundleTracking, on_delete=models.CASCADE, verbose_name="Bundle")
production_order = models.ForeignKey(ProductionOrder, on_delete=models.CASCADE, verbose_name="Production Order")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
floor_name = models.CharField(max_length=100, verbose_name="Floor Name")
quantity = models.IntegerField(verbose_name="Quantity")
reason = models.CharField(max_length=200, verbose_name="Reason")
action = models.CharField(max_length=20, choices=ACTION_CHOICES, verbose_name="Action")
date = models.DateField(verbose_name="Date")
reported_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="Reported By")
remarks = models.TextField(blank=True, verbose_name="Remarks")
class Meta:
verbose_name = "Rework/Rejected Tracking"
verbose_name_plural = "Rework/Rejected Trackings"
def __str__(self):
return f"{self.bundle.bundle_id} - {self.action} - {self.floor_name} - Order {self.order.id}"
class WIPStatus(models.Model):
floor_name = models.CharField(max_length=100, verbose_name="Floor Name")
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
total_bundles = models.IntegerField(verbose_name="Total Bundles")
cutting = models.IntegerField(verbose_name="Cutting")
sewing = models.IntegerField(verbose_name="Sewing")
finishing = models.IntegerField(verbose_name="Finishing")
packing = models.IntegerField(verbose_name="Packing")
rework = models.IntegerField(verbose_name="Rework")
rejected = models.IntegerField(verbose_name="Rejected")
last_updated = models.DateTimeField(auto_now=True, verbose_name="Last Updated")
class Meta:
verbose_name = "WIP Status"
verbose_name_plural = "WIP Statuses"
def __str__(self):
return f"{self.floor_name} - Order {self.order.id} - {self.style.style_name}"
class ProductionReport(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name="Order")
style = models.ForeignKey(OrderStyle, on_delete=models.CASCADE, verbose_name="Style")
floor_name = models.CharField(max_length=100, verbose_name="Floor Name")
total_bundles = models.IntegerField(verbose_name="Total Bundles")
cutting = models.IntegerField(verbose_name="Cutting")
sewing = models.IntegerField(verbose_name="Sewing")
finishing = models.IntegerField(verbose_name="Finishing")
packing = models.IntegerField(verbose_name="Packing")
rework = models.IntegerField(verbose_name="Rework")
rejected = models.IntegerField(verbose_name="Rejected")
report_date = models.DateField(verbose_name="Report Date")
generated_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="Generated By")
remarks = models.TextField(blank=True, verbose_name="Remarks")
class Meta:
verbose_name = "Production Report"
verbose_name_plural = "Production Reports"
def __str__(self):
return f"Report for Order {self.order.id} - {self.floor_name} - {self.report_date}"
ব্যাখ্যা:
- ProductionOrder: প্রোডাকশন শুরুর জন্য অর্ডার এবং স্টাইল-ভিত্তিক নির্দেশ।
- BundleTracking: বারকোড-ভিত্তিক ট্র্যাকিং,
production_orderফিল্ড যুক্ত। - BundleTransfer: ফ্লোর-টু-ফ্লোর ট্রান্সফার, প্রতিটি ফ্লোরের জন্য আলাদা এন্ট্রি।
- ProductionReceipt: ফ্লোরে কাজ সম্পন্ন হলে রিসিপ্ট।
- LineDashboard, ReworkRejectedTracking, WIPStatus, ProductionReport: ফ্লোর এবং অর্ডার-ভিত্তিক তথ্য।
- একই স্টাইল (LMN-BLUE-JACKET-2025) দুটি অর্ডারে থাকলেও
OrderStyleএবংorderফিল্ডের মাধ্যমে আলাদা করা হয়েছে।
ফর্ম: production/forms.py
from django import forms
from .models import ProductionOrder, BundleTracking, BundleTransfer, ProductionReceipt
class ProductionFloorForm(forms.Form):
ACTION_CHOICES = (
('scan', 'Scan Bundle'),
('transfer', 'Transfer Bundle'),
('receipt', 'Create Receipt'),
)
action = forms.ChoiceField(choices=ACTION_CHOICES, label="Action")
barcode = forms.CharField(max_length=50, label="Barcode", required=False)
production_order = forms.ModelChoiceField(queryset=ProductionOrder.objects.all(), label="Production Order", required=False)
floor_name = forms.CharField(max_length=100, label="Floor Name", required=False)
to_floor = forms.CharField(max_length=100, label="To Floor", required=False)
quantity = forms.DecimalField(max_digits=10, decimal_places=2, label="Quantity", required=False)
unit = forms.CharField(max_length=20, label="Unit", required=False)
stage = forms.ChoiceField(choices=BundleTracking.STAGE_CHOICES, label="Stage", required=False)
remarks = forms.CharField(widget=forms.Textarea, required=False, label="Remarks")
def clean(self):
cleaned_data = super().clean()
action = cleaned_data.get('action')
barcode = cleaned_data.get('barcode')
floor_name = cleaned_data.get('floor_name')
to_floor = cleaned_data.get('to_floor')
production_order = cleaned_data.get('production_order')
if action == 'scan' and not barcode:
raise forms.ValidationError("Barcode is required for scanning.")
if action == 'transfer' and (not barcode or not floor_name or not to_floor):
raise forms.ValidationError("Barcode, From Floor, and To Floor are required for transfer.")
if action == 'receipt' and (not production_order or not floor_name or not stage):
raise forms.ValidationError("Production Order, Floor Name, and Stage are required for receipt.")
return cleaned_data
ব্যাখ্যা:
- ProductionFloorForm: স্ক্যানিং, ট্রান্সফার, এবং রিসিপ্টের জন্য একটি ফর্ম।
- ব্যালিডেশন নিশ্চিত করে প্রয়োজনীয় ফিল্ড পূরণ।
অ্যাডমিন: production/admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
from .models import ProductionOrder, BundleTracking, BundleTransfer, ProductionReceipt, LineDashboard, ReworkRejectedTracking, WIPStatus, ProductionReport
@admin.register(ProductionOrder)
class ProductionOrderAdmin(ModelAdmin):
list_display = ('production_order_id', 'order', 'style', 'total_bundles', 'quantity', 'unit', 'issue_date', 'status', 'created_by')
search_fields = ('production_order_id', 'order__id', 'style__style_name')
list_filter = ('status', 'issue_date', 'order')
ordering = ('-issue_date',)
@admin.register(BundleTracking)
class BundleTrackingAdmin(ModelAdmin):
list_display = ('bundle_id', 'production_order', 'order', 'style', 'quantity', 'unit', 'barcode', 'current_floor', 'current_stage', 'date', 'scanned_by', 'status')
search_fields = ('bundle_id', 'barcode', 'production_order__production_order_id', 'order__id', 'style__style_name', 'current_floor')
list_filter = ('current_floor', 'current_stage', 'status', 'date', 'order')
ordering = ('-date',)
@admin.register(BundleTransfer)
class BundleTransferAdmin(ModelAdmin):
list_display = ('bundle', 'production_order', 'order', 'style', 'from_floor', 'to_floor', 'quantity', 'unit', 'transfer_date', 'delivered_by', 'received_by')
search_fields = ('bundle__bundle_id', 'production_order__production_order_id', 'order__id', 'style__style_name', 'from_floor', 'to_floor')
list_filter = ('transfer_date', 'from_floor', 'to_floor', 'order')
ordering = ('-transfer_date',)
@admin.register(ProductionReceipt)
class ProductionReceiptAdmin(ModelAdmin):
list_display = ('receipt_id', 'production_order', 'order', 'style', 'floor_name', 'quantity', 'unit', 'stage', 'receipt_date', 'created_by')
search_fields = ('receipt_id', 'production_order__production_order_id', 'order__id', 'style__style_name', 'floor_name')
list_filter = ('stage', 'receipt_date', 'order')
ordering = ('-receipt_date',)
@admin.register(LineDashboard)
class LineDashboardAdmin(ModelAdmin):
list_display = ('floor_name', 'order', 'style', 'total_bundles', 'active_bundles', 'completed_bundles', 'rejected_bundles', 'wip_stage')
search_fields = ('floor_name', 'order__id', 'style__style_name')
list_filter = ('wip_stage', 'last_updated', 'order')
ordering = ('-last_updated',)
@admin.register(ReworkRejectedTracking)
class ReworkRejectedTrackingAdmin(ModelAdmin):
list_display = ('bundle', 'production_order', 'order', 'style', 'floor_name', 'quantity', 'reason', 'action', 'date', 'reported_by')
search_fields = ('bundle__bundle_id', 'production_order__production_order_id', 'order__id', 'style__style_name', 'floor_name', 'reason')
list_filter = ('action', 'floor_name', 'date', 'order')
ordering = ('-date',)
@admin.register(WIPStatus)
class WIPStatusAdmin(ModelAdmin):
list_display = ('floor_name', 'order', 'style', 'total_bundles', 'cutting', 'sewing', 'finishing', 'packing', 'rework', 'rejected')
search_fields = ('floor_name', 'order__id', 'style__style_name')
list_filter = ('last_updated', 'order')
ordering = ('-last_updated',)
@admin.register(ProductionReport)
class ProductionReportAdmin(ModelAdmin):
list_display = ('order', 'style', 'floor_name', 'total_bundles', 'cutting', 'sewing', 'finishing', 'packing', 'rework', 'rejected', 'report_date')
search_fields = ('order__id', 'style__style_name', 'floor_name')
list_filter = ('report_date', 'floor_name', 'order')
ordering = ('-report_date',)
ব্যাখ্যা:
- অ্যাপের নাম production হিসেবে আপডেট করা হয়েছে।
- প্রতিটি মডেলে
order,style, এবংproduction_orderফিল্ড ফিল্টার এবং সার্চে যুক্ত।
স্যাম্পল ডেটা: fixtures/production_sample_data.json
[
{
"model": "auth.user",
"pk": 1,
"fields": {
"username": "nadia",
"first_name": "Nadia",
"last_name": "",
"email": "nadia@example.com",
"password": "pbkdf2_sha256$260000$randomsalt$hashedpassword",
"is_staff": true,
"is_active": true
}
},
{
"model": "auth.user",
"pk": 2,
"fields": {
"username": "karim",
"first_name": "Karim",
"last_name": "",
"email": "karim@example.com",
"password": "pbkdf2_sha256$260000$randomsalt$hashedpassword",
"is_staff": true,
"is_active": true
}
},
{
"model": "auth.user",
"pk": 3,
"fields": {
"username": "faisal",
"first_name": "Faisal",
"last_name": "",
"email": "faisal@example.com",
"password": "pbkdf2_sha256$260000$randomsalt$hashedpassword",
"is_staff": true,
"is_active": true
}
},
{
"model": "auth.user",
"pk": 4,
"fields": {
"username": "ruma",
"first_name": "Ruma",
"last_name": "",
"email": "ruma@example.com",
"password": "pbkdf2_sha256$260000$randomsalt$hashedpassword",
"is_staff": true,
"is_active": true
}
},
{
"model": "order_management.buyer",
"pk": 1,
"fields": {
"name": "LMN Buyer",
"country": "USA",
"contact_person": "John Doe",
"phone": "+1234567890",
"email": "john@lmnbuyer.com"
}
},
{
"model": "order_management.order",
"pk": 1,
"fields": {
"buyer": 1,
"quantity": 12000,
"sizes": "M,L,XL",
"colors": "Blue",
"delivery_date": "2025-09-25",
"order_date": "2025-08-04",
"status": "received",
"remarks": "Managed by Nadia"
}
},
{
"model": "order_management.order",
"pk": 2,
"fields": {
"buyer": 1,
"quantity": 8000,
"sizes": "S,M",
"colors": "Blue",
"delivery_date": "2025-10-10",
"order_date": "2025-08-04",
"status": "received",
"remarks": "Managed by Nadia"
}
},
{
"model": "order_management.orderstyle",
"pk": 1,
"fields": {
"order": 1,
"style_name": "LMN-BLUE-JACKET-2025"
}
},
{
"model": "order_management.orderstyle",
"pk": 2,
"fields": {
"order": 2,
"style_name": "LMN-BLUE-JACKET-2025"
}
},
{
"model": "inventory.warehouse",
"pk": 1,
"fields": {
"name": "Factory Z",
"location": "Dhaka Production Warehouse",
"manager": 2
}
},
{
"model": "inventory.item",
"pk": 1,
"fields": {
"item_name": "Polyester Blue Fabric",
"item_type": "fabric",
"unit": "meter",
"default_warehouse": 1
}
},
{
"model": "inventory.goodsissue",
"pk": 1,
"fields": {
"issue_id": "IS-2025-001",
"item": 1,
"warehouse": 1,
"quantity": 1000.00,
"unit": "meter",
"issue_date": "2025-08-04",
"status": "approved",
"approved_by": 2,
"style": 1,
"remarks": "Issued for Order 1"
}
},
{
"model": "inventory.goodsissue",
"pk": 2,
"fields": {
"issue_id": "IS-2025-002",
"item": 1,
"warehouse": 1,
"quantity": 500.00,
"unit": "meter",
"issue_date": "2025-08-04",
"status": "approved",
"approved_by": 2,
"style": 2,
"remarks": "Issued for Order 2"
}
},
{
"model": "production.productionorder",
"pk": 1,
"fields": {
"production_order_id": "PO-2025-001",
"order": 1,
"style": 1,
"total_bundles": 50,
"quantity": 1000.00,
"unit": "meter",
"issue_date": "2025-08-04",
"status": "active",
"created_by": 4,
"remarks": "Order 1, LMN-BLUE-JACKET-2025"
}
},
{
"model": "production.productionorder",
"pk": 2,
"fields": {
"production_order_id": "PO-2025-002",
"order": 2,
"style": 2,
"total_bundles": 25,
"quantity": 500.00,
"unit": "meter",
"issue_date": "2025-08-04",
"status": "active",
"created_by": 4,
"remarks": "Order 2, LMN-BLUE-JACKET-2025"
}
},
{
"model": "production.bundletracking",
"pk": 1,
"fields": {
"bundle_id": "BUN-2025-001",
"production_order": 1,
"order": 1,
"style": 1,
"quantity": 20.00,
"unit": "meter",
"barcode": "BAR-2025-001",
"current_floor": "Floor 1",
"current_stage": "cutting",
"date": "2025-08-04",
"scanned_by": 3,
"status": "active",
"remarks": "Order 1, Floor 1, Jacket"
}
},
{
"model": "production.bundletracking",
"pk": 2,
"fields": {
"bundle_id": "BUN-2025-002",
"production_order": 1,
"order": 1,
"style": 1,
"quantity": 20.00,
"unit": "meter",
"barcode": "BAR-2025-002",
"current_floor": "Floor 2",
"current_stage": "sewing",
"date": "2025-08-04",
"scanned_by": 3,
"status": "active",
"remarks": "Order 1, Transferred to Floor 2"
}
},
{
"model": "production.bundletracking",
"pk": 3,
"fields": {
"bundle_id": "BUN-2025-003",
"production_order": 2,
"order": 2,
"style": 2,
"quantity": 20.00,
"unit": "meter",
"barcode": "BAR-2025-003",
"current_floor": "Floor 1",
"current_stage": "cutting",
"date": "2025-08-04",
"scanned_by": 3,
"status": "active",
"remarks": "Order 2, Floor 1, Jacket"
}
},
{
"model": "production.bundletransfer",
"pk": 1,
"fields": {
"bundle": 2,
"production_order": 1,
"order": 1,
"style": 1,
"from_floor": "Floor 1",
"to_floor": "Floor 2",
"quantity": 20.00,
"unit": "meter",
"transfer_date": "2025-08-04",
"delivered_by": 3,
"received_by": 4,
"remarks": "Order 1, Transferred to Sewing Floor"
}
},
{
"model": "production.productionreceipt",
"pk": 1,
"fields": {
"receipt_id": "REC-2025-001",
"production_order": 1,
"order": 1,
"style": 1,
"floor_name": "Floor 1",
"quantity": 600.00,
"unit": "meter",
"stage": "cutting",
"receipt_date": "2025-08-04",
"created_by": 3,
"remarks": "Order 1, Cutting completed"
}
},
{
"model": "production.productionreceipt",
"pk": 2,
"fields": {
"receipt_id": "REC-2025-002",
"production_order": 1,
"order": 1,
"style": 1,
"floor_name": "Floor 2",
"quantity": 400.00,
"unit": "meter",
"stage": "sewing",
"receipt_date": "2025-08-04",
"created_by": 3,
"remarks": "Order 1, Sewing completed"
}
},
{
"model": "production.linedashboard",
"pk": 1,
"fields": {
"floor_name": "Floor 1",
"order": 1,
"style": 1,
"total_bundles": 30,
"active_bundles": 30,
"completed_bundles": 0,
"rejected_bundles": 0,
"wip_stage": "cutting",
"last_updated": "2025-08-04T13:00:00Z"
}
},
{
"model": "production.linedashboard",
"pk": 2,
"fields": {
"floor_name": "Floor 1",
"order": 2,
"style": 2,
"total_bundles": 25,
"active_bundles": 25,
"completed_bundles": 0,
"rejected_bundles": 0,
"wip_stage": "cutting",
"last_updated": "2025-08-04T13:00:00Z"
}
},
{
"model": "production.linedashboard",
"pk": 3,
"fields": {
"floor_name": "Floor 2",
"order": 1,
"style": 1,
"total_bundles": 20,
"active_bundles": 20,
"completed_bundles": 0,
"rejected_bundles": 0,
"wip_stage": "sewing",
"last_updated": "2025-08-04T13:00:00Z"
}
},
{
"model": "production.reworkrejectedtracking",
"pk": 1,
"fields": {
"bundle": 2,
"production_order": 1,
"order": 1,
"style": 1,
"floor_name": "Floor 2",
"quantity": 5,
"reason": "Stitching error",
"action": "rework",
"date": "2025-08-04",
"reported_by": 3,
"remarks": "Order 1, Sent for rework"
}
},
{
"model": "production.reworkrejectedtracking",
"pk": 2,
"fields": {
"bundle": 1,
"production_order": 1,
"order": 1,
"style": 1,
"floor_name": "Floor 1",
"quantity": 3,
"reason": "Fabric defect",
"action": "rejected",
"date": "2025-08-04",
"reported_by": 4,
"remarks": "Order 1, Rejected due to quality issue"
}
},
{
"model": "production.wipstatus",
"pk": 1,
"fields": {
"floor_name": "Floor 1",
"order": 1,
"style": 1,
"total_bundles": 30,
"cutting": 27,
"sewing": 0,
"finishing": 0,
"packing": 0,
"rework": 0,
"rejected": 3,
"last_updated": "2025-08-04T13:30:00Z"
}
},
{
"model": "production.wipstatus",
"pk": 2,
"fields": {
"floor_name": "Floor 1",
"order": 2,
"style": 2,
"total_bundles": 25,
"cutting": 25,
"sewing": 0,
"finishing": 0,
"packing": 0,
"rework": 0,
"rejected": 0,
"last_updated": "2025-08-04T13:30:00Z"
}
},
{
"model": "production.wipstatus",
"pk": 3,
"fields": {
"floor_name": "Floor 2",
"order": 1,
"style": 1,
"total_bundles": 20,
"cutting": 0,
"sewing": 15,
"finishing": 0,
"packing": 0,
"rework": 5,
"rejected": 0,
"last_updated": "2025-08-04T13:30:00Z"
}
},
{
"model": "production.productionreport",
"pk": 1,
"fields": {
"order": 1,
"style": 1,
"floor_name": "Floor 1",
"total_bundles": 30,
"cutting": 27,
"sewing": 0,
"finishing": 0,
"packing": 0,
"rework": 0,
"rejected": 3,
"report_date": "2025-08-04",
"generated_by": 4,
"remarks": "Order 1, Floor 1 Report"
}
},
{
"model": "production.productionreport",
"pk": 2,
"fields": {
"order": 2,
"style": 2,
"floor_name": "Floor 1",
"total_bundles": 25,
"cutting": 25,
"sewing": 0,
"finishing": 0,
"packing": 0,
"rework": 0,
"rejected": 0,
"report_date": "2025-08-04",
"generated_by": 4,
"remarks": "Order 2, Floor 1 Report"
}
},
{
"model": "production.productionreport",
"pk": 3,
"fields": {
"order": 1,
"style": 1,
"floor_name": "Floor 2",
"total_bundles": 20,
"cutting": 0,
"sewing": 15,
"finishing": 0,
"packing": 0,
"rework": 5,
"rejected": 0,
"report_date": "2025-08-04",
"generated_by": 4,
"remarks": "Order 1, Floor 2 Report"
}
}
]
ব্যাখ্যা:
- অ্যাপের নাম production হিসেবে আপডেট করা হয়েছে।
- ProductionOrder, BundleTracking, BundleTransfer, ProductionReceipt: প্রতিটি ফ্লোরের জন্য আলাদা এন্ট্রি।
- LineDashboard, WIPStatus, ProductionReport: ফ্লোর এবং অর্ডার-ভিত্তিক তথ্য।
ধাপ: অ্যাপ সেটআপ
- অ্যাপ তৈরি:
python manage.py startapp production
- মডেল, ফর্ম, এবং অ্যাডমিন সেভ:
production/models.py,production/forms.py, এবংproduction/admin.pyসেভ করুন।
- settings.py আপডেট:
INSTALLED_APPS = [
...
'order_management',
'production',
'unfold',
]
- মাইগ্রেশন:
python manage.py makemigrations
python manage.py migrate
- ডেটা লোড:
python manage.py loaddata fixtures/production_sample_data
- সার্ভার চালু:
python manage.py runserver
- অ্যাডমিন চেক:
http://127.0.0.1:8000/admin/এ লগইন করুন।- Production সেকশনে দেখুন:
- Production Order: PO-2025-001 (Order 1), PO-2025-002 (Order 2)।
- Bundle Tracking: BUN-2025-001 (Order 1, Floor 1), BUN-2025-002 (Order 1, Floor 2), BUN-2025-003 (Order 2, Floor 1)।
- Bundle Transfer: BUN-2025-002 (Order 1, Floor 1 → Floor 2)।
- Production Receipt: REC-2025-001 (Order 1, Floor 1), REC-2025-002 (Order 1, Floor 2)।
- Line Dashboard, Rework/Rejected Tracking, WIP Status, Production Report।
🎯 অর্ডার মডেল মডিফিকেশন
আপনার বর্তমান order_management/models.py ইতিমধ্যে প্রোডাকশন ফ্লোরের সাথে ইন্টিগ্রেশনের জন্য উপযুক্ত। কোনো পরিবর্তনের প্রয়োজন নেই, কারণ:
- OrderStyle একটি স্টাইলকে নির্দিষ্ট অর্ডারের সাথে লিঙ্ক করে।
- ProductionOrder এবং অন্যান্য মডেলে
orderএবংstyleফিল্ড ব্যবহার করে একই স্টাইলের একাধিক অর্ডার হ্যান্ডেল করা হয়। - OrderBreakdown এবং DeliverySchedule অর্ডারের বিস্তারিত তথ্য প্রদান করে।
🔄 পরবর্তী ধাপ
অনুগ্রহ করে জানান:
- ProductionFloorForm এর জন্য HTML/CSS/JS ফ্রন্টএন্ড তৈরি করব?
- Barcode Generation এর জন্য
python-barcodeইন্টিগ্রেশন? - REST API তৈরি (যেমন,
/api/productionorder/,/api/bundletracking/)? - Order-based Query এর জন্য কাস্টম ভিউ বা ফিল্টার ফর্ম?
- মডেল বা গল্পে কোনো নির্দিষ্ট পরিবর্তন চান?
- ডেটা লোড বা অ্যাডমিনে সমস্যা হলে বিস্তারিত জানান।
আশা করি এই গল্প এবং মডেলগুলো আপনার প্রয়োজনীয়তা পূরণ করেছে! 😊 কোনো অতিরিক্ত ফিচার বা পরিবর্তন চাইলে বলুন।