# -*- coding: utf-8 -*- from odoo import models, fields, api, _ class GarmentFabricInspection(models.Model): _name = 'garment.fabric.inspection' _inherit = ['mail.thread', 'mail.activity.mixin'] _description = 'Fabric 4-Point System Quality Inspection' _order = 'name desc' name = fields.Char(string='Inspection Number', required=True, default=lambda self: _('New'), copy=False) roll_id = fields.Many2one('garment.fabric.roll', string='Fabric Roll', required=True, tracking=True) supplier_id = fields.Many2one('res.partner', string='Supplier', related='roll_id.supplier_id', readonly=True) fabric_product_id = fields.Many2one('product.product', string='Fabric Product', related='roll_id.product_id', readonly=True) shade_lot = fields.Char(string='Shade / Dyeing Lot', related='roll_id.shade_lot', readonly=True) # Inspector & Date inspector_id = fields.Many2one('res.users', string='Quality Inspector', default=lambda self: self.env.user, tracking=True) date_inspected = fields.Date(string='Inspection Date', default=fields.Date.today, tracking=True) # GSM & Width Verification target_gsm = fields.Integer(string='Target GSM', default=180) actual_gsm = fields.Integer(string='Actual GSM Measured', default=180) target_width = fields.Float(string='Target Width (Inches)', default=30.0) actual_width = fields.Float(string='Actual Width (Inches)', default=30.0) # Shade Evaluation shade_grade = fields.Selection([ ('grade_a', 'Grade A - Exact Match with Master Swatch'), ('grade_b', 'Grade B - Slight Variation (Approved)'), ('grade_c', 'Grade C - Noticeable Variation (Group Separately)'), ('grade_d', 'Grade D - Severely Off-Shade (Rejected)'), ], string='Shade / Color Evaluation', default='grade_a', required=True, tracking=True) # 4-Point Defect Scoring System # Points 1 to 4 based on defect length: # 1 pt: Defect up to 3 inches # 2 pts: Defect over 3 up to 6 inches # 3 pts: Defect over 6 up to 9 inches # 4 pts: Defect over 9 inches or any hole defects_1pt_count = fields.Integer(string='1-Point Defects (<=3")', default=0) defects_2pt_count = fields.Integer(string='2-Point Defects (3"-6")', default=0) defects_3pt_count = fields.Integer(string='3-Point Defects (6"-9")', default=0) defects_4pt_count = fields.Integer(string='4-Point Defects (>9" or Holes)', default=0) total_defect_points = fields.Integer(string='Total Defect Points', compute='_compute_points', store=True) inspected_meters = fields.Float(string='Inspected Length (Meters)', default=20.0, required=True) # 4-Point Formula: (Total Points * 100) / (Inspected Meters * Fabric Width in Meters) score_per_100_sqm = fields.Float(string='Score / 100 Sq. Meters', compute='_compute_points', store=True) max_allowed_score = fields.Float(string='AQL Max Points Threshold', default=28.0, help='Standard industry threshold: 28 points per 100 sq meters') # Quantitative Results passed_meters = fields.Float(string='Passed Quantity (Meters)') rejected_meters = fields.Float(string='Rejected Quantity (Meters)') # Status state = fields.Selection([ ('draft', 'Draft'), ('inspected', 'Inspected'), ('passed', 'Quality Passed'), ('failed', 'Quality Failed / Rejected'), ], string='Inspection Verdict', default='draft', tracking=True, required=True) remarks = fields.Text(string='Inspector Remarks / Observations') @api.model_create_multi def create(self, vals_list): for vals in vals_list: if vals.get('name', _('New')) == _('New'): vals['name'] = self.env['ir.sequence'].next_by_code('garment.fabric.inspection') or _('FINSP/%s') % fields.Date.today().strftime('%Y%m%d') return super().create(vals_list) @api.depends('defects_1pt_count', 'defects_2pt_count', 'defects_3pt_count', 'defects_4pt_count', 'inspected_meters', 'actual_width') def _compute_points(self): for insp in self: total_pts = (insp.defects_1pt_count * 1) + (insp.defects_2pt_count * 2) + (insp.defects_3pt_count * 3) + (insp.defects_4pt_count * 4) insp.total_defect_points = total_pts # Width in meters: width in inches * 0.0254 width_meters = (insp.actual_width or 30.0) * 0.0254 area_sqm = (insp.inspected_meters or 1.0) * width_meters if area_sqm > 0: insp.score_per_100_sqm = round((total_pts * 100.0) / area_sqm, 1) else: insp.score_per_100_sqm = 0.0 def action_pass(self): self.ensure_one() self.write({ 'state': 'passed', 'passed_meters': self.roll_id.length_meters, 'rejected_meters': 0.0, }) self.roll_id.write({'status': 'approved'}) def action_fail(self): self.ensure_one() self.write({ 'state': 'failed', 'passed_meters': 0.0, 'rejected_meters': self.roll_id.length_meters, }) self.roll_id.write({'status': 'rejected'})