# -*- coding: utf-8 -*- from odoo import models, fields, api, _ class GarmentFabricRoll(models.Model): _name = 'garment.fabric.roll' _inherit = ['mail.thread', 'mail.activity.mixin'] _description = 'Fabric Roll Inventory Tracking' _order = 'name desc' name = fields.Char(string='Roll Number', required=True, default=lambda self: _('New'), copy=False, tracking=True) product_id = fields.Many2one('product.product', string='Fabric Product', required=True) fabric_type_id = fields.Many2one('garment.fabric.type', string='Fabric Structure') color_id = fields.Many2one('garment.color', string='Color / Shade') shade_lot = fields.Char(string='Shade / Dyeing Lot Number', required=True, index=True, tracking=True) barcode = fields.Char(string='Barcode / QR Code', index=True, copy=False) # Technical Specifications gsm = fields.Integer(string='GSM (Actual)', required=True, default=180) width = fields.Float(string='Width (Inches)', required=True, default=30.0) width_type = fields.Selection([('tube', 'Tubular'), ('open', 'Open Width')], string='Width Type', default='tube') yarn_count = fields.Char(string='Yarn Count', default='30s Combed') # Quantitative Weights (Kg) gross_weight = fields.Float(string='Gross Weight (Kg)', required=True, default=25.0) tare_weight = fields.Float(string='Tare / Core Weight (Kg)', default=0.8) net_weight = fields.Float(string='Initial Net Weight (Kg)', compute='_compute_net_weight', store=True) length_meters = fields.Float(string='Fabric Length (Meters)', default=75.0) # Consumption & Balance issued_weight = fields.Float(string='Issued Weight (Kg)', default=0.0) scrap_weight = fields.Float(string='End-Bit Scrap (Kg)', default=0.0) remaining_weight = fields.Float(string='Remaining Weight (Kg)', compute='_compute_remaining_weight', store=True) # Origin & Storage supplier_id = fields.Many2one('res.partner', string='Fabric Mill / Supplier', domain=[('supplier_rank', '>', 0)]) purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order Ref') date_received = fields.Date(string='Date Received', default=fields.Date.today) location_id = fields.Many2one('stock.location', string='Storage Warehouse Location') # Workflow State status = fields.Selection([ ('received', 'Received / Uninspected'), ('inspected', 'Inspected'), ('approved', 'Quality Approved (Ready to Cut)'), ('issued', 'Partially Issued'), ('exhausted', 'Fully Exhausted'), ('rejected', 'Quality Rejected (Quarantine)'), ], string='Roll Status', default='received', tracking=True, required=True) # Inspections inspection_ids = fields.One2many('garment.fabric.inspection', 'roll_id', string='Fabric Inspections') inspection_count = fields.Integer(string='Inspections', compute='_compute_inspection_count') @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.roll') or _('ROLL-%s') % fields.Date.today().strftime('%Y%m%d') if not vals.get('barcode'): vals['barcode'] = vals['name'] return super().create(vals_list) @api.depends('gross_weight', 'tare_weight') def _compute_net_weight(self): for roll in self: roll.net_weight = max(0.0, roll.gross_weight - roll.tare_weight) @api.depends('net_weight', 'issued_weight', 'scrap_weight') def _compute_remaining_weight(self): for roll in self: rem = roll.net_weight - roll.issued_weight - roll.scrap_weight roll.remaining_weight = max(0.0, round(rem, 2)) if roll.remaining_weight <= 0.2 and roll.status in ['approved', 'issued']: roll.status = 'exhausted' @api.depends('inspection_ids') def _compute_inspection_count(self): for roll in self: roll.inspection_count = len(roll.inspection_ids) def action_create_inspection(self): self.ensure_one() insp = self.env['garment.fabric.inspection'].create({ 'roll_id': self.id, 'target_gsm': self.gsm, 'target_width': self.width, 'inspected_meters': min(20.0, self.length_meters), }) return { 'name': _('Fabric 4-Point Inspection'), 'type': 'ir.actions.act_window', 'res_model': 'garment.fabric.inspection', 'res_id': insp.id, 'view_mode': 'form', }