1. Home
  2. গার্মেন্টস ইন্ডাস্ট্রির জ...
  3. ০ ৬ প্রোডাকশন
  4. গল্প

গল্প

ধন্যবাদ আপনার প্রয়োজনীয়তার জন্য! 😊 আপনি চান 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: যেকোনো সময় অর্ডার বা স্টাইলের তথ্য দেখা।

🎭 চরিত্র পরিচিতি

নামপদের নামদায়িত্ব
NadiaMerchandiserঅর্ডার এবং স্টাইল তথ্য প্রদান
KarimInventory Controllerফ্যাব্রিক ইস্যু এবং স্টক ম্যানেজমেন্ট
FaisalFloor Supervisorবারকোড স্ক্যানিং এবং ফ্লোর ট্রান্সফার
RumaProduction Managerফ্লোর মনিটরিং, রিপোর্টিং, এবং WIP স্ট্যাটাস
Factory ZProduction Warehouseপ্রোডাকশন গুদাম (ফ্যাব্রিক স্টোরেজ)
LMN BuyerBuyerঅর্ডার দাতা (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

idbuyer_idquantitysizescolorsdelivery_dateorder_datestatusremarks
1112000M,L,XLBlue2025-09-252025-08-04receivedManaged by Nadia
218000S,MBlue2025-10-102025-08-04receivedManaged by Nadia

📦 Table: order_management_orderstyle

idorder_idstyle_name
11LMN-BLUE-JACKET-2025
22LMN-BLUE-JACKET-2025

📦 Table: inventory_goodsissue

idissue_iditem_idwarehouse_idquantityunitissue_datestatusapproved_bystyle_idremarks
1IS-2025-00111 (Factory Z)1000.00meter2025-08-04approvedKarim1Issued for Order 1
2IS-2025-00211 (Factory Z)500.00meter2025-08-04approvedKarim2Issued for Order 2

🧾 ধাপ ২: প্রোডাকশন অর্ডার তৈরি

Ruma একটি Production Order তৈরি করেন, যা অর্ডার এবং স্টাইলের উপর ভিত্তি করে ফ্লোরে কাজ শুরু করে। Order 1-এর জন্য 50টি বান্ডিল এবং Order 2-এর জন্য 25টি বান্ডিল।

📦 Table: production_productionorder

idproduction_order_idorder_idstyle_idtotal_bundlesquantityunitissue_datestatuscreated_byremarks
1PO-2025-00111501000.00meter2025-08-04activeRumaOrder 1, LMN-BLUE-JACKET-2025
2PO-2025-0022225500.00meter2025-08-04activeRumaOrder 2, LMN-BLUE-JACKET-2025

🧾 ধাপ ৩: বান্ডিল ট্র্যাকিং এবং ফ্লোর এন্ট্রি

Faisal প্রোডাকশন অর্ডারের বান্ডিলগুলো কাটিং ফ্লোরে (Floor 1) স্ক্যান করে। প্রতিটি বান্ডিলে 20 মিটার ফ্যাব্রিক এবং একটি বারকোড। Order 1-এর 20টি বান্ডিল Floor 2 (সেলাই) তে ট্রান্সফার হয়, যেখানে Ruma রিসিভ করেন।

📦 Table: production_bundletracking

idbundle_idproduction_order_idorder_idstyle_idquantityunitbarcodecurrent_floorcurrent_stagedatescanned_bystatusremarks
1BUN-2025-001PO-2025-0011120.00meterBAR-2025-001Floor 1cutting2025-08-04FaisalactiveOrder 1, Floor 1, Jacket
2BUN-2025-002PO-2025-0011120.00meterBAR-2025-002Floor 2sewing2025-08-04FaisalactiveOrder 1, Transferred to Floor 2
3BUN-2025-003PO-2025-0022220.00meterBAR-2025-003Floor 1cutting2025-08-04FaisalactiveOrder 2, Floor 1, Jacket

📦 Table: production_bundletransfer

idbundle_idproduction_order_idorder_idstyle_idfrom_floorto_floorquantityunittransfer_datedelivered_byreceived_byremarks
1BUN-2025-002PO-2025-00111Floor 1Floor 220.00meter2025-08-04FaisalRumaOrder 1, Transferred to Sewing

🧾 ধাপ ৪: প্রোডাকশন রিসিপ্ট

Faisal প্রতিটি ফ্লোরে কাজ শেষ হলে Production Receipt তৈরি করে। Floor 1-এ Order 1-এর 30টি বান্ডিল কাটিং সম্পন্ন, Floor 2-এ 20টি বান্ডিল সেলাই সম্পন্ন।

📦 Table: production_productionreceipt

idreceipt_idproduction_order_idorder_idstyle_idfloor_namequantityunitstagereceipt_datecreated_byremarks
1REC-2025-001PO-2025-00111Floor 1600.00metercutting2025-08-04FaisalOrder 1, Cutting completed
2REC-2025-002PO-2025-00111Floor 2400.00metersewing2025-08-04FaisalOrder 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

idfloor_nameorder_idstyle_idtotal_bundlesactive_bundlescompleted_bundlesrejected_bundleswip_stagelast_updated
1Floor 111303000cutting2025-08-04T13:00:00Z
2Floor 122252500cutting2025-08-04T13:00:00Z
3Floor 211202000sewing2025-08-04T13:00:00Z

🧾 ধাপ ৭: রিওয়ার্ক/প্রত্যাখ্যান ট্র্যাকিং

Faisal Floor 2-এ বান্ডিল BUN-2025-002 (Order 1) স্ক্যান করে এবং ৫টি জ্যাকেটে সেলাই ত্রুটি দেখেন, যা রিওয়ার্কে পাঠানো হয়। Ruma Floor 1-এ বান্ডিল BUN-2025-001 (Order 1) থেকে ৩টি জ্যাকেট প্রত্যাখ্যান করেন ফ্যাব্রিক দাগের কারণে।

📦 Table: production_reworkrejectedtracking

idbundle_idproduction_order_idorder_idstyle_idfloor_namequantityreasonactiondatereported_byremarks
1BUN-2025-002PO-2025-00111Floor 25Stitching errorrework2025-08-04FaisalOrder 1, Sent for rework
2BUN-2025-001PO-2025-00111Floor 13Fabric defectrejected2025-08-04RumaOrder 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

idfloor_nameorder_idstyle_idtotal_bundlescuttingsewingfinishingpackingreworkrejectedlast_updated
1Floor 1113027000032025-08-04T13:30:00Z
2Floor 1222525000002025-08-04T13:30:00Z
3Floor 2112001500502025-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

idorder_idstyle_idfloor_nametotal_bundlescuttingsewingfinishingpackingreworkrejectedreport_dategenerated_byremarks
111Floor 13027000032025-08-04RumaOrder 1, Floor 1 Report
222Floor 12525000002025-08-04RumaOrder 2, Floor 1 Report
311Floor 22001500502025-08-04RumaOrder 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: ফ্লোর এবং অর্ডার-ভিত্তিক তথ্য।

ধাপ: অ্যাপ সেটআপ

  1. অ্যাপ তৈরি:
   python manage.py startapp production
  1. মডেল, ফর্ম, এবং অ্যাডমিন সেভ:
  • production/models.py, production/forms.py, এবং production/admin.py সেভ করুন।
  1. settings.py আপডেট:
   INSTALLED_APPS = [
       ...
       'order_management',
       'production',
       'unfold',
   ]
  1. মাইগ্রেশন:
   python manage.py makemigrations
   python manage.py migrate
  1. ডেটা লোড:
   python manage.py loaddata fixtures/production_sample_data
  1. সার্ভার চালু:
   python manage.py runserver
  1. অ্যাডমিন চেক:
  • 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 অর্ডারের বিস্তারিত তথ্য প্রদান করে।

🔄 পরবর্তী ধাপ

অনুগ্রহ করে জানান:

  1. ProductionFloorForm এর জন্য HTML/CSS/JS ফ্রন্টএন্ড তৈরি করব?
  2. Barcode Generation এর জন্য python-barcode ইন্টিগ্রেশন?
  3. REST API তৈরি (যেমন, /api/productionorder/, /api/bundletracking/)?
  4. Order-based Query এর জন্য কাস্টম ভিউ বা ফিল্টার ফর্ম?
  5. মডেল বা গল্পে কোনো নির্দিষ্ট পরিবর্তন চান?
  6. ডেটা লোড বা অ্যাডমিনে সমস্যা হলে বিস্তারিত জানান।

আশা করি এই গল্প এবং মডেলগুলো আপনার প্রয়োজনীয়তা পূরণ করেছে! 😊 কোনো অতিরিক্ত ফিচার বা পরিবর্তন চাইলে বলুন।

How can we help?