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

144 lines
7.1 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import UserError
class GarmentPackingPlan(models.Model):
_name = 'garment.packing.plan'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Garment Export & Retail Packing Management'
_order = 'name desc'
name = fields.Char(string='Packing Plan No', required=True, default=lambda self: _('New'), copy=False, tracking=True)
production_plan_id = fields.Many2one('garment.production.plan', string='Production Order', required=True, tracking=True)
sale_order_id = fields.Many2one('sale.order', string='Sales Order', tracking=True)
style_id = fields.Many2one('garment.style', string='Garment Style', required=True, tracking=True)
company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company)
packing_type = fields.Selection([
('solid_color_solid_size', 'Solid Color / Solid Size (Single Variant per Box)'),
('solid_color_assorted_size', 'Solid Color / Assorted Size (Ratio Pack)'),
('assorted_color_assorted_size', 'Assorted Color / Assorted Size (Pre-Pack)'),
], string='Carton Packing Method', default='solid_color_assorted_size', required=True, tracking=True)
target_pack_qty = fields.Integer(string='Target Pack Pieces', default=1000, required=True)
total_packed_qty = fields.Integer(string='Total Packed Pieces', compute='_compute_carton_totals', store=True)
total_cartons = fields.Integer(string='Total Cartons', compute='_compute_carton_totals', store=True)
total_cbm = fields.Float(string='Total Shipment CBM (m³)', compute='_compute_carton_totals', store=True)
total_gross_weight = fields.Float(string='Total Gross Weight (Kg)', compute='_compute_carton_totals', store=True)
carton_ids = fields.One2many('garment.carton', 'packing_plan_id', string='Master Cartons')
state = fields.Selection([
('draft', 'Draft'),
('in_packing', 'Packing In Progress'),
('completed', 'Packed & Transferred to FG'),
], string='Status', default='draft', tracking=True, required=True)
@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.packing.plan') or _('PACK/%s') % fields.Date.today().strftime('%Y%m%d')
return super(GarmentPackingPlan, self).create(vals_list)
@api.depends('carton_ids.total_pieces', 'carton_ids.cbm', 'carton_ids.gross_weight_kg')
def _compute_carton_totals(self):
for plan in self:
plan.total_packed_qty = sum(plan.carton_ids.mapped('total_pieces'))
plan.total_cartons = len(plan.carton_ids)
plan.total_cbm = round(sum(plan.carton_ids.mapped('cbm')), 3)
plan.total_gross_weight = round(sum(plan.carton_ids.mapped('gross_weight_kg')), 2)
def action_start_packing(self):
self.write({'state': 'in_packing'})
def action_transfer_to_finished_goods(self):
"""Creates or updates finished goods stock records from packed cartons"""
self.ensure_one()
if not self.carton_ids:
raise UserError(_("No cartons found in this packing plan."))
FG = self.env['garment.finished.goods']
for carton in self.carton_ids:
for line in carton.line_ids:
existing_fg = FG.search([
('style_id', '=', self.style_id.id),
('color_id', '=', line.color_id.id),
('size_id', '=', line.size_id.id),
], limit=1)
if existing_fg:
existing_fg.write({'on_hand_qty': existing_fg.on_hand_qty + line.quantity})
else:
FG.create({
'style_id': self.style_id.id,
('color_id'): line.color_id.id,
('size_id'): line.size_id.id,
'on_hand_qty': line.quantity,
})
self.write({'state': 'completed'})
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Finished Goods Stock Updated'),
'message': _('Successfully transferred %d packed garments into Finished Goods Inventory.') % self.total_packed_qty,
'sticky': False,
'type': 'success',
}
}
class GarmentCarton(models.Model):
_name = 'garment.carton'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Master Export Carton'
_order = 'carton_no, name'
name = fields.Char(string='Carton ID', required=True, default=lambda self: _('New'), copy=False)
carton_no = fields.Integer(string='Carton #', default=1, required=True)
packing_plan_id = fields.Many2one('garment.packing.plan', string='Packing Plan', required=True, ondelete='cascade')
barcode = fields.Char(string='Carton Barcode / Shipping QR', index=True, copy=False)
style_id = fields.Many2one('garment.style', string='Garment Style', related='packing_plan_id.style_id', readonly=True)
# Dimensions & Weights
length_cm = fields.Float(string='Length (cm)', default=60.0)
width_cm = fields.Float(string='Width (cm)', default=40.0)
height_cm = fields.Float(string='Height (cm)', default=30.0)
cbm = fields.Float(string='Carton CBM (m³)', compute='_compute_cbm', store=True)
net_weight_kg = fields.Float(string='Net Weight (Kg)', default=12.5)
gross_weight_kg = fields.Float(string='Gross Weight (Kg)', default=14.0)
line_ids = fields.One2many('garment.carton.line', 'carton_id', string='Carton Garment Breakdown')
total_pieces = fields.Integer(string='Total Pieces in Box', compute='_compute_pieces', store=True)
dispatch_id = fields.Many2one('garment.dispatch', string='Dispatch Order')
@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.carton') or _('CTN-%s') % fields.Date.today().strftime('%Y%m%d')
if not vals.get('barcode'):
vals['barcode'] = vals['name']
return super(GarmentCarton, self).create(vals_list)
@api.depends('length_cm', 'width_cm', 'height_cm')
def _compute_cbm(self):
for ctn in self:
ctn.cbm = round((ctn.length_cm * ctn.width_cm * ctn.height_cm) / 1000000.0, 4)
@api.depends('line_ids.quantity')
def _compute_pieces(self):
for ctn in self:
ctn.total_pieces = sum(ctn.line_ids.mapped('quantity'))
class GarmentCartonLine(models.Model):
_name = 'garment.carton.line'
_description = 'Carton Contents Breakdown Line'
carton_id = fields.Many2one('garment.carton', string='Carton', required=True, ondelete='cascade')
color_id = fields.Many2one('garment.color', string='Color', required=True)
size_id = fields.Many2one('garment.size', string='Size', required=True)
quantity = fields.Integer(string='Pieces in Carton', default=10, required=True)