diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/delivery/models/stock_picking.py | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/delivery/models/stock_picking.py')
| -rw-r--r-- | addons/delivery/models/stock_picking.py | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py new file mode 100644 index 00000000..d58c2cf6 --- /dev/null +++ b/addons/delivery/models/stock_picking.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import json + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + + +class StockQuantPackage(models.Model): + _inherit = "stock.quant.package" + + @api.depends('quant_ids') + def _compute_weight(self): + for package in self: + weight = 0.0 + if self.env.context.get('picking_id'): + # TODO: potential bottleneck: N packages = N queries, use groupby ? + current_picking_move_line_ids = self.env['stock.move.line'].search([ + ('result_package_id', '=', package.id), + ('picking_id', '=', self.env.context['picking_id']) + ]) + for ml in current_picking_move_line_ids: + weight += ml.product_uom_id._compute_quantity( + ml.qty_done, ml.product_id.uom_id) * ml.product_id.weight + else: + for quant in package.quant_ids: + weight += quant.quantity * quant.product_id.weight + package.weight = weight + + def _get_default_weight_uom(self): + return self.env['product.template']._get_weight_uom_name_from_ir_config_parameter() + + def _compute_weight_uom_name(self): + for package in self: + package.weight_uom_name = self.env['product.template']._get_weight_uom_name_from_ir_config_parameter() + + weight = fields.Float(compute='_compute_weight', help="Total weight of all the products contained in the package.") + weight_uom_name = fields.Char(string='Weight unit of measure label', compute='_compute_weight_uom_name', readonly=True, default=_get_default_weight_uom) + shipping_weight = fields.Float(string='Shipping Weight', help="Total weight of the package.") + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + + @api.depends('move_line_ids', 'move_line_ids.result_package_id') + def _compute_packages(self): + for package in self: + packs = set() + for move_line in package.move_line_ids: + if move_line.result_package_id: + packs.add(move_line.result_package_id.id) + package.package_ids = list(packs) + + @api.depends('move_line_ids', 'move_line_ids.result_package_id', 'move_line_ids.product_uom_id', 'move_line_ids.qty_done') + def _compute_bulk_weight(self): + for picking in self: + weight = 0.0 + for move_line in picking.move_line_ids: + if move_line.product_id and not move_line.result_package_id: + weight += move_line.product_uom_id._compute_quantity(move_line.qty_done, move_line.product_id.uom_id) * move_line.product_id.weight + picking.weight_bulk = weight + + @api.depends('move_line_ids.result_package_id', 'move_line_ids.result_package_id.shipping_weight', 'weight_bulk') + def _compute_shipping_weight(self): + for picking in self: + # if shipping weight is not assigned => default to calculated product weight + picking.shipping_weight = picking.weight_bulk + sum([pack.shipping_weight or pack.weight for pack in picking.package_ids]) + + def _get_default_weight_uom(self): + return self.env['product.template']._get_weight_uom_name_from_ir_config_parameter() + + def _compute_weight_uom_name(self): + for package in self: + package.weight_uom_name = self.env['product.template']._get_weight_uom_name_from_ir_config_parameter() + + carrier_price = fields.Float(string="Shipping Cost") + delivery_type = fields.Selection(related='carrier_id.delivery_type', readonly=True) + carrier_id = fields.Many2one("delivery.carrier", string="Carrier", check_company=True) + weight = fields.Float(compute='_cal_weight', digits='Stock Weight', store=True, help="Total weight of the products in the picking.", compute_sudo=True) + carrier_tracking_ref = fields.Char(string='Tracking Reference', copy=False) + carrier_tracking_url = fields.Char(string='Tracking URL', compute='_compute_carrier_tracking_url') + weight_uom_name = fields.Char(string='Weight unit of measure label', compute='_compute_weight_uom_name', readonly=True, default=_get_default_weight_uom) + package_ids = fields.Many2many('stock.quant.package', compute='_compute_packages', string='Packages') + weight_bulk = fields.Float('Bulk Weight', compute='_compute_bulk_weight', help="Total weight of products which are not in a package.") + shipping_weight = fields.Float("Weight for Shipping", compute='_compute_shipping_weight', + help="Total weight of packages and products not in a package. Packages with no shipping weight specified will default to their products' total weight. This is the weight used to compute the cost of the shipping.") + is_return_picking = fields.Boolean(compute='_compute_return_picking') + return_label_ids = fields.One2many('ir.attachment', compute='_compute_return_label') + + @api.depends('carrier_id', 'carrier_tracking_ref') + def _compute_carrier_tracking_url(self): + for picking in self: + picking.carrier_tracking_url = picking.carrier_id.get_tracking_link(picking) if picking.carrier_id and picking.carrier_tracking_ref else False + + @api.depends('carrier_id', 'move_ids_without_package') + def _compute_return_picking(self): + for picking in self: + if picking.carrier_id and picking.carrier_id.can_generate_return: + picking.is_return_picking = any(m.origin_returned_move_id for m in picking.move_ids_without_package) + else: + picking.is_return_picking = False + + def _compute_return_label(self): + for picking in self: + if picking.carrier_id: + picking.return_label_ids = self.env['ir.attachment'].search([('res_model', '=', 'stock.picking'), ('res_id', '=', picking.id), ('name', 'like', '%s%%' % picking.carrier_id.get_return_label_prefix())]) + else: + picking.return_label_ids = False + + def get_multiple_carrier_tracking(self): + self.ensure_one() + try: + return json.loads(self.carrier_tracking_url) + except (ValueError, TypeError): + return False + + @api.depends('move_lines') + def _cal_weight(self): + for picking in self: + picking.weight = sum(move.weight for move in picking.move_lines if move.state != 'cancel') + + def _send_confirmation_email(self): + for pick in self: + if pick.carrier_id: + if pick.carrier_id.integration_level == 'rate_and_ship' and pick.picking_type_code != 'incoming': + pick.send_to_shipper() + pick._check_carrier_details_compliance() + return super(StockPicking, self)._send_confirmation_email() + + def _pre_put_in_pack_hook(self, move_line_ids): + res = super(StockPicking, self)._pre_put_in_pack_hook(move_line_ids) + if not res: + if self.carrier_id: + return self._set_delivery_packaging() + else: + return res + + def _set_delivery_packaging(self): + """ This method returns an action allowing to set the product packaging and the shipping weight + on the stock.quant.package. + """ + self.ensure_one() + view_id = self.env.ref('delivery.choose_delivery_package_view_form').id + context = dict( + self.env.context, + current_package_carrier_type=self.carrier_id.delivery_type, + default_picking_id=self.id + ) + # As we pass the `delivery_type` ('fixed' or 'base_on_rule' by default) in a key who + # correspond to the `package_carrier_type` ('none' to default), we make a conversion. + # No need conversion for other carriers as the `delivery_type` and + #`package_carrier_type` will be the same in these cases. + if context['current_package_carrier_type'] in ['fixed', 'base_on_rule']: + context['current_package_carrier_type'] = 'none' + return { + 'name': _('Package Details'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'choose.delivery.package', + 'view_id': view_id, + 'views': [(view_id, 'form')], + 'target': 'new', + 'context': context, + } + + def send_to_shipper(self): + self.ensure_one() + res = self.carrier_id.send_shipping(self)[0] + if self.carrier_id.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= self.carrier_id.amount: + res['exact_price'] = 0.0 + self.carrier_price = res['exact_price'] * (1.0 + (self.carrier_id.margin / 100.0)) + if res['tracking_number']: + self.carrier_tracking_ref = res['tracking_number'] + order_currency = self.sale_id.currency_id or self.company_id.currency_id + msg = _( + "Shipment sent to carrier %(carrier_name)s for shipping with tracking number %(ref)s<br/>Cost: %(price).2f %(currency)s", + carrier_name=self.carrier_id.name, + ref=self.carrier_tracking_ref, + price=self.carrier_price, + currency=order_currency.name + ) + self.message_post(body=msg) + self._add_delivery_cost_to_so() + + def _check_carrier_details_compliance(self): + """Hook to check if a delivery is compliant in regard of the carrier. + """ + pass + + def print_return_label(self): + self.ensure_one() + self.carrier_id.get_return_label(self) + + def _add_delivery_cost_to_so(self): + self.ensure_one() + sale_order = self.sale_id + if sale_order and self.carrier_id.invoice_policy == 'real' and self.carrier_price: + delivery_lines = sale_order.order_line.filtered(lambda l: l.is_delivery and l.currency_id.is_zero(l.price_unit) and l.product_id == self.carrier_id.product_id) + carrier_price = self.carrier_price * (1.0 + (float(self.carrier_id.margin) / 100.0)) + if not delivery_lines: + delivery_lines = [sale_order._create_delivery_line(self.carrier_id, carrier_price)] + delivery_line = delivery_lines[0] + delivery_line[0].write({ + 'price_unit': carrier_price, + # remove the estimated price from the description + 'name': sale_order.carrier_id.with_context(lang=self.partner_id.lang).name, + }) + + def open_website_url(self): + self.ensure_one() + if not self.carrier_tracking_url: + raise UserError(_("Your delivery method has no redirect on courier provider's website to track this order.")) + + carrier_trackers = [] + try: + carrier_trackers = json.loads(self.carrier_tracking_url) + except ValueError: + carrier_trackers = self.carrier_tracking_url + else: + msg = "Tracking links for shipment: <br/>" + for tracker in carrier_trackers: + msg += '<a href=' + tracker[1] + '>' + tracker[0] + '</a><br/>' + self.message_post(body=msg) + return self.env["ir.actions.actions"]._for_xml_id("delivery.act_delivery_trackers_url") + + client_action = { + 'type': 'ir.actions.act_url', + 'name': "Shipment Tracking Page", + 'target': 'new', + 'url': self.carrier_tracking_url, + } + return client_action + + def cancel_shipment(self): + for picking in self: + picking.carrier_id.cancel_shipment(self) + msg = "Shipment %s cancelled" % picking.carrier_tracking_ref + picking.message_post(body=msg) + picking.carrier_tracking_ref = False + + def _get_estimated_weight(self): + self.ensure_one() + weight = 0.0 + for move in self.move_lines: + weight += move.product_qty * move.product_id.weight + return weight + + +class StockReturnPicking(models.TransientModel): + _inherit = 'stock.return.picking' + + def _create_returns(self): + # Prevent copy of the carrier and carrier price when generating return picking + # (we have no integration of returns for now) + new_picking, pick_type_id = super(StockReturnPicking, self)._create_returns() + picking = self.env['stock.picking'].browse(new_picking) + picking.write({'carrier_id': False, + 'carrier_price': 0.0}) + return new_picking, pick_type_id |
