144 lines
7.1 KiB
Python
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)
|