# -*- coding: utf-8 -*- from odoo import models, fields, api, _ from odoo.exceptions import UserError class GarmentDispatch(models.Model): _name = 'garment.dispatch' _inherit = ['mail.thread', 'mail.activity.mixin'] _description = 'Garment Dispatch & Delivery Management' _order = 'name desc' name = fields.Char(string='Dispatch Order No', required=True, default=lambda self: _('New'), copy=False, tracking=True) customer_id = fields.Many2one('res.partner', string='Buyer / Consignee', required=True, tracking=True) sale_order_id = fields.Many2one('sale.order', string='Sales Order Ref', tracking=True) production_plan_id = fields.Many2one('garment.production.plan', string='Production Plan Ref', tracking=True) company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company) # Dates & Logistics date_dispatch = fields.Date(string='Dispatch Date', default=fields.Date.today, tracking=True) carrier_name = fields.Char(string='Carrier / Shipping Line') bl_awb_no = fields.Char(string='B/L or Air Waybill No', tracking=True) container_no = fields.Char(string='Container No (FCL)') seal_no = fields.Char(string='Customs Seal No') vehicle_no = fields.Char(string='Truck / Vehicle Registration No') port_of_loading = fields.Char(string='Port of Loading', default='Tuticorin / Chennai Port') port_of_discharge = fields.Char(string='Port of Discharge') # Cartons & Metrics carton_ids = fields.One2many('garment.carton', 'dispatch_id', string='Dispatched Cartons') total_cartons = fields.Integer(string='Total Cartons', compute='_compute_totals', store=True) total_pieces = fields.Integer(string='Total Garments Dispatched', compute='_compute_totals', store=True) total_gross_weight = fields.Float(string='Total Gross Weight (Kg)', compute='_compute_totals', store=True) total_cbm = fields.Float(string='Total CBM (m³)', compute='_compute_totals', store=True) state = fields.Selection([ ('draft', 'Draft Shipment'), ('packed', 'Loaded & Ready to Depart'), ('dispatched', 'Dispatched / In Transit'), ('delivered', 'Delivered to Customer'), ('cancelled', 'Cancelled'), ], string='Status', default='draft', tracking=True, required=True) notes = fields.Text(string='Shipping Instructions / Marks & Numbers') @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.dispatch') or _('DISP/%s') % fields.Date.today().strftime('%Y%m%d') return super(GarmentDispatch, self).create(vals_list) @api.depends('carton_ids.total_pieces', 'carton_ids.gross_weight_kg', 'carton_ids.cbm') def _compute_totals(self): for rec in self: rec.total_cartons = len(rec.carton_ids) rec.total_pieces = sum(rec.carton_ids.mapped('total_pieces')) rec.total_gross_weight = round(sum(rec.carton_ids.mapped('gross_weight_kg')), 2) rec.total_cbm = round(sum(rec.carton_ids.mapped('cbm')), 3) def action_dispatch(self): """Dispatches the shipment, deducts Finished Goods inventory""" self.ensure_one() if not self.carton_ids: raise UserError(_("Please attach cartons to dispatch.")) FG = self.env['garment.finished.goods'] for ctn in self.carton_ids: for line in ctn.line_ids: fg = FG.search([ ('style_id', '=', ctn.style_id.id), ('color_id', '=', line.color_id.id), ('size_id', '=', line.size_id.id), ], limit=1) if fg: fg.write({ 'on_hand_qty': max(0, fg.on_hand_qty - line.quantity), 'shipped_qty': fg.shipped_qty + line.quantity, }) self.write({'state': 'dispatched'}) def action_mark_delivered(self): self.write({'state': 'delivered'})