from odoo import api, fields, models, _ from odoo.exceptions import UserError class StockQuantPackage(models.Model): _inherit = 'stock.quant.package' carrier_id = fields.Many2one('delivery.carrier', string='Carrier') carrier_tracking_ref = fields.Char(string='Tracking Reference') def _get_active_picking(self): picking_id = self._context.get('active_id') picking_model = self._context.get('active_model') if not picking_id or picking_model != 'stock.picking': raise UserError('Cannot cancel package other than through shipment/picking.') return self.env['stock.picking'].browse(picking_id) def send_to_shipper(self): picking = self._get_active_picking() picking.with_context(packages=self).send_to_shipper() def cancel_shipment(self): picking = self._get_active_picking() picking.with_context(packages=self).cancel_shipment() class StockPicking(models.Model): _inherit = 'stock.picking' shipping_account_id = fields.Many2one('partner.shipping.account', string='Shipping Account') require_insurance = fields.Selection([ ('auto', 'Automatic'), ('yes', 'Yes'), ('no', 'No'), ], string='Require Insurance', default='auto', help='If your carrier supports it, auto should be calculated off of the "Automatic Insurance Value" field.') package_carrier_tracking_ref = fields.Char(string='Package Tracking Numbers', compute='_compute_package_carrier_tracking_ref') @api.depends('package_ids.carrier_tracking_ref') def _compute_package_carrier_tracking_ref(self): for picking in self: package_refs = picking.package_ids.filtered('carrier_tracking_ref').mapped('carrier_tracking_ref') if package_refs: picking.package_carrier_tracking_ref = ','.join(package_refs) else: picking.package_carrier_tracking_ref = False @api.onchange('carrier_id') def _onchange_carrier_id_for_priority(self): for picking in self: if picking.carrier_id and picking.carrier_id.procurement_priority: picking.priority = picking.carrier_id.procurement_priority @api.one @api.depends('move_lines.priority', 'carrier_id') def _compute_priority(self): if self.carrier_id.procurement_priority: self.priority = self.carrier_id.procurement_priority else: super(StockPicking, self)._compute_priority() @api.model def create(self, values): origin = values.get('origin') if origin and not values.get('shipping_account_id'): so = self.env['sale.order'].search([('name', '=', str(origin))], limit=1) if so and so.shipping_account_id: values['shipping_account_id'] = so.shipping_account_id.id carrier_id = values.get('carrier_id') if carrier_id: carrier = self.env['delivery.carrier'].browse(carrier_id) if carrier.procurement_priority: values['priority'] = carrier.procurement_priority res = super(StockPicking, self).create(values) return res def declared_value(self): self.ensure_one() cost = sum([(l.product_id.standard_price * l.qty_done) for l in self.move_line_ids] or [0.0]) if not cost: # Assume Full Value cost = sum([(l.product_id.standard_price * l.product_uom_qty) for l in self.move_lines] or [0.0]) return cost def clear_carrier_tracking_ref(self): self.write({'carrier_tracking_ref': False}) def reset_carrier_tracking_ref(self): for picking in self: picking.carrier_tracking_ref = picking.package_carrier_tracking_ref # Override to send to specific packaging carriers def send_to_shipper(self): packages = self._context.get('packages') self.ensure_one() if not packages: packages = self.package_ids package_carriers = packages.mapped('carrier_id') if not package_carriers: # Original behavior return super().send_to_shipper() tracking_numbers = [] carrier_prices = [] order_currency = self.sale_id.currency_id or self.company_id.currency_id for carrier in package_carriers: self.carrier_id = carrier carrier_packages = packages.filtered(lambda p: p.carrier_id == carrier) res = carrier.send_shipping(self) if res: res = res[0] if carrier.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= carrier.amount: res['exact_price'] = 0.0 carrier_price = float(res['exact_price']) * (1.0 + (self.carrier_id.margin / 100.0)) carrier_prices.append(carrier_price) tracking_number = '' if res['tracking_number']: tracking_number = res['tracking_number'] tracking_numbers.append(tracking_number) # Try to add tracking to the individual packages. potential_tracking_numbers = tracking_number.split(',') if len(potential_tracking_numbers) >= len(carrier_packages): for t, p in zip(potential_tracking_numbers, carrier_packages): p.carrier_tracking_ref = t else: carrier_packages.write({'carrier_tracking_ref': tracking_number}) msg = _("Shipment sent to carrier %s for shipping with tracking number %s
Cost: %.2f %s") % (carrier.name, tracking_number, carrier_price, order_currency.name) self.message_post(body=msg) self.carrier_price = sum(carrier_prices or [0.0]) self.carrier_tracking_ref = ','.join(tracking_numbers or ['']) # Override to provide per-package versions... def cancel_shipment(self): packages = self._context.get('packages') pickings_with_package_tracking = self.filtered(lambda p: p.package_carrier_tracking_ref) for picking in pickings_with_package_tracking: if packages: current_packages = packages else: current_packages = picking.package_ids # Packages without a carrier can just be cleared packages_without_carrier = current_packages.filtered(lambda p: not p.carrier_id and p.carrier_tracking_ref) packages_without_carrier.write({ 'carrier_tracking_ref': False, }) # Packages with carrier can use the carrier method packages_with_carrier = current_packages.filtered(lambda p: p.carrier_id and p.carrier_tracking_ref) carriers = packages_with_carrier.mapped('carrier_id') for carrier in carriers: carrier_packages = packages_with_carrier.filtered(lambda p: p.carrier_id == carrier) carrier.cancel_shipment(self, packages=carrier_packages) package_refs = ','.join(carrier_packages.mapped('carrier_tracking_ref')) msg = "Shipment %s cancelled" % package_refs picking.message_post(body=msg) carrier_packages.write({'carrier_tracking_ref': False}) pickings_without_package_tracking = self - pickings_with_package_tracking if pickings_without_package_tracking: # use original on these super(StockPicking, pickings_without_package_tracking).cancel_shipment() class StockMove(models.Model): _inherit = 'stock.move' def _prepare_procurement_values(self): res = super(StockMove, self)._prepare_procurement_values() res['priority'] = self.picking_id.priority or self.priority return res