2026-09-21 16:43:23 +05:30

135 lines
7.0 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
class GarmentSewingLine(models.Model):
_name = 'garment.sewing.line'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Garment Sewing Line'
_order = 'name'
name = fields.Char(string='Line Name', required=True)
code = fields.Char(string='Line Code', required=True)
supervisor_id = fields.Many2one('res.users', string='Line Supervisor', tracking=True)
operator_count = fields.Integer(string='Standard Operators', default=20)
helper_count = fields.Integer(string='Helpers / Feeders', default=4)
daily_capacity_pcs = fields.Integer(string='Standard Daily Target (Pcs)', default=1000)
active = fields.Boolean(default=True)
notes = fields.Text(string='Machine Layout / Setup Notes')
_code_uniq = models.Constraint('UNIQUE(code)', 'Line Code must be unique!')
class GarmentSewingHourly(models.Model):
_name = 'garment.sewing.hourly'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Sewing Floor Hourly Production Log'
_order = 'date desc, line_id'
name = fields.Char(string='Reference', compute='_compute_name', store=True)
line_id = fields.Many2one('garment.sewing.line', string='Sewing Line', required=True, tracking=True)
date = fields.Date(string='Production Date', default=fields.Date.today, required=True, tracking=True)
shift = fields.Selection([('general', 'General Shift (08:30-17:30)'), ('shift_a', 'Shift A'), ('shift_b', 'Shift B')], string='Shift', default='general')
style_id = fields.Many2one('garment.style', string='Garment Style Running', required=True, tracking=True)
production_plan_id = fields.Many2one('garment.production.plan', string='Production Order', tracking=True)
# Operators & SAM
operators_present = fields.Integer(string='Operators Present', default=20, required=True)
sam = fields.Float(string='Garment SAM (Minutes)', default=12.0, required=True)
working_minutes_per_hour = fields.Integer(string='Minutes / Hour', default=60)
# Hourly Outputs (Target, Actual, Rejections)
h1_target = fields.Integer(string='H1 Target (08:30-09:30)', default=120)
h1_actual = fields.Integer(string='H1 Actual', default=0)
h1_defect = fields.Integer(string='H1 Defect', default=0)
h2_target = fields.Integer(string='H2 Target (09:30-10:30)', default=120)
h2_actual = fields.Integer(string='H2 Actual', default=0)
h2_defect = fields.Integer(string='H2 Defect', default=0)
h3_target = fields.Integer(string='H3 Target (10:30-11:30)', default=120)
h3_actual = fields.Integer(string='H3 Actual', default=0)
h3_defect = fields.Integer(string='H3 Defect', default=0)
h4_target = fields.Integer(string='H4 Target (11:30-12:30)', default=120)
h4_actual = fields.Integer(string='H4 Actual', default=0)
h4_defect = fields.Integer(string='H4 Defect', default=0)
h5_target = fields.Integer(string='H5 Target (13:15-14:15)', default=120)
h5_actual = fields.Integer(string='H5 Actual', default=0)
h5_defect = fields.Integer(string='H5 Defect', default=0)
h6_target = fields.Integer(string='H6 Target (14:15-15:15)', default=120)
h6_actual = fields.Integer(string='H6 Actual', default=0)
h6_defect = fields.Integer(string='H6 Defect', default=0)
h7_target = fields.Integer(string='H7 Target (15:15-16:15)', default=120)
h7_actual = fields.Integer(string='H7 Actual', default=0)
h7_defect = fields.Integer(string='H7 Defect', default=0)
h8_target = fields.Integer(string='H8 Target (16:15-17:15)', default=120)
h8_actual = fields.Integer(string='H8 Actual', default=0)
h8_defect = fields.Integer(string='H8 Defect', default=0)
ot_target = fields.Integer(string='OT Target (Overtime)', default=0)
ot_actual = fields.Integer(string='OT Actual', default=0)
ot_defect = fields.Integer(string='OT Defect', default=0)
# Cumulative Day Totals
total_target = fields.Integer(string='Total Day Target', compute='_compute_totals', store=True)
total_actual = fields.Integer(string='Total Day Output', compute='_compute_totals', store=True)
total_defects = fields.Integer(string='Total Alterations / Defects', compute='_compute_totals', store=True)
total_worked_hours = fields.Float(string='Total Hours Worked', default=8.0)
efficiency_pct = fields.Float(string='Line Efficiency %', compute='_compute_efficiency', store=True)
efficiency_badge = fields.Selection([
('excellent', 'Excellent (>=90%)'),
('good', 'Good (75%-89%)'),
('needs_attention', 'Below Target (<75%)'),
], string='Performance Rating', compute='_compute_efficiency', store=True)
@api.depends('line_id', 'date')
def _compute_name(self):
for rec in self:
line_name = rec.line_id.name if rec.line_id else 'Line'
rec.name = f"{line_name} - {rec.date}"
@api.onchange('line_id')
def _onchange_line_id(self):
if self.line_id:
self.operators_present = self.line_id.operator_count
@api.onchange('style_id')
def _onchange_style_id(self):
if self.style_id:
self.sam = self.style_id.sam or 12.0
@api.depends('h1_target', 'h2_target', 'h3_target', 'h4_target', 'h5_target', 'h6_target', 'h7_target', 'h8_target', 'ot_target',
'h1_actual', 'h2_actual', 'h3_actual', 'h4_actual', 'h5_actual', 'h6_actual', 'h7_actual', 'h8_actual', 'ot_actual',
'h1_defect', 'h2_defect', 'h3_defect', 'h4_defect', 'h5_defect', 'h6_defect', 'h7_defect', 'h8_defect', 'ot_defect')
def _compute_totals(self):
for rec in self:
rec.total_target = (rec.h1_target + rec.h2_target + rec.h3_target + rec.h4_target +
rec.h5_target + rec.h6_target + rec.h7_target + rec.h8_target + rec.ot_target)
rec.total_actual = (rec.h1_actual + rec.h2_actual + rec.h3_actual + rec.h4_actual +
rec.h5_actual + rec.h6_actual + rec.h7_actual + rec.h8_actual + rec.ot_actual)
rec.total_defects = (rec.h1_defect + rec.h2_defect + rec.h3_defect + rec.h4_defect +
rec.h5_defect + rec.h6_defect + rec.h7_defect + rec.h8_defect + rec.ot_defect)
@api.depends('total_actual', 'sam', 'operators_present', 'total_worked_hours', 'working_minutes_per_hour')
def _compute_efficiency(self):
for rec in self:
total_minutes = (rec.operators_present or 1) * (rec.total_worked_hours or 8.0) * (rec.working_minutes_per_hour or 60)
produced_minutes = (rec.total_actual or 0) * (rec.sam or 1.0)
if total_minutes > 0:
eff = round((produced_minutes / total_minutes) * 100.0, 1)
else:
eff = 0.0
rec.efficiency_pct = eff
if eff >= 90.0:
rec.efficiency_badge = 'excellent'
elif eff >= 75.0:
rec.efficiency_badge = 'good'
else:
rec.efficiency_badge = 'needs_attention'