diff --git a/addons/delivery/models/sale_order.py b/addons/delivery/models/sale_order.py index 02c6bf605b3..fa6401f54c7 100644 --- a/addons/delivery/models/sale_order.py +++ b/addons/delivery/models/sale_order.py @@ -51,8 +51,14 @@ class SaleOrder(models.Model): def action_open_delivery_wizard(self): view_id = self.env.ref('delivery.choose_delivery_carrier_view_form').id + if self.env.context.get('carrier_recompute'): + name = _('Update shipping cost') + carrier = self.carrier_id + else: + name = _('Add a shipping method') + carrier = self.partner_id.property_delivery_carrier_id return { - 'name': _('Add a shipping method'), + 'name': name, 'type': 'ir.actions.act_window', 'view_mode': 'form', 'res_model': 'choose.delivery.carrier', @@ -61,27 +67,10 @@ class SaleOrder(models.Model): 'target': 'new', 'context': { 'default_order_id': self.id, - 'default_carrier_id': self.partner_id.property_delivery_carrier_id.id, + 'default_carrier_id': carrier.id, } } - def recompute_delivery_cost(self): - self.ensure_one() - delivery_line = self.order_line.filtered('is_delivery')[:1] - res = self.carrier_id.rate_shipment(self) - if res.get('success'): - self.delivery_message = res.get('warning_message', False) - else: - raise UserError(res['error_message']) - delivery_line.name = self.carrier_id.with_context(lang=self.partner_id.lang).name - if self.carrier_id.invoice_policy == 'real': - delivery_line.name += _(' (Estimated Cost: %s )') % self._format_currency_amount(res['price']) - else: - delivery_line.price_unit = res['price'] - if self.carrier_id.free_over and self._compute_amount_total_without_delivery() >= res['price']: - delivery_line.name += '\nFree Shipping' - self.recompute_delivery_price = False - def _create_delivery_line(self, carrier, price_unit, price_unit_in_description=False): SaleOrderLine = self.env['sale.order.line'] if self.partner_id: @@ -163,3 +152,17 @@ class SaleOrderLine(models.Model): def _is_delivery(self): self.ensure_one() return self.is_delivery + + # override to allow deletion of delivery line in a confirmed order + def _check_line_unlink(self): + """ + Extend the allowed deletion policy of SO lines. + + Lines that are delivery lines can be deleted from a confirmed order. + + :rtype: recordset sale.order.line + :returns: set of lines that cannot be deleted + """ + + undeletable_lines = super()._check_line_unlink() + return undeletable_lines.filtered(lambda line: not line.is_delivery) diff --git a/addons/delivery/views/delivery_view.xml b/addons/delivery/views/delivery_view.xml index 211603bc6b0..ac0ef7071a7 100644 --- a/addons/delivery/views/delivery_view.xml +++ b/addons/delivery/views/delivery_view.xml @@ -331,15 +331,17 @@ attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('order_line', '=', []), ('delivery_set', '=', True)]}" /> diff --git a/addons/delivery/wizard/choose_delivery_carrier.py b/addons/delivery/wizard/choose_delivery_carrier.py index c7bac396c61..c02fa5d30ca 100644 --- a/addons/delivery/wizard/choose_delivery_carrier.py +++ b/addons/delivery/wizard/choose_delivery_carrier.py @@ -19,7 +19,7 @@ class ChooseDeliveryCarrier(models.TransientModel): ) delivery_type = fields.Selection(related='carrier_id.delivery_type') delivery_price = fields.Float() - display_price = fields.Float(string='Cost', readonly=True) + display_price = fields.Float(string='Cost') currency_id = fields.Many2one('res.currency', related='order_id.currency_id') available_carrier_ids = fields.Many2many("delivery.carrier", compute='_compute_available_carrier', string="Available Carriers") invoicing_message = fields.Text(compute='_compute_invoicing_message') @@ -29,18 +29,25 @@ class ChooseDeliveryCarrier(models.TransientModel): def _onchange_carrier_id(self): self.delivery_message = False if self.delivery_type in ('fixed', 'base_on_rule'): - vals = self.carrier_id.rate_shipment(self.order_id) - if vals.get('success'): - if vals.get('warning_message'): - self.delivery_message = vals['warning_message'] - self.delivery_price = vals['price'] - self.display_price = vals['carrier_price'] - else: + vals = self._get_shipment_rate() + if vals.get('error_message'): return {'error': vals['error_message']} else: self.display_price = 0 self.delivery_price = 0 + @api.onchange('order_id') + def _onchange_order_id(self): + # fixed and base_on_rule delivery price will computed on each carrier change so no need to recompute here + if self.carrier_id and self.order_id.delivery_set and self.delivery_type not in ('fixed', 'base_on_rule'): + vals = self._get_shipment_rate() + if vals.get('error_message'): + warning = { + 'title': '%s Error' % self.carrier_id.name, + 'message': vals.get('error_message') + } + return {'warning': warning} + @api.depends('carrier_id') def _compute_invoicing_message(self): self.ensure_one() @@ -51,19 +58,24 @@ class ChooseDeliveryCarrier(models.TransientModel): @api.depends('partner_id') def _compute_available_carrier(self): - carriers = self.env['delivery.carrier'].search([]) for rec in self: + carriers = self.env['delivery.carrier'].search(['|', ('company_id', '=', False), ('company_id', '=', rec.order_id.company_id.id)]) rec.available_carrier_ids = carriers.available_carriers(rec.order_id.partner_shipping_id) if rec.partner_id else carriers - def update_price(self): + def _get_shipment_rate(self): vals = self.carrier_id.rate_shipment(self.order_id) if vals.get('success'): - if vals['warning_message']: + if vals.get('warning_message'): self.delivery_message = vals['warning_message'] self.delivery_price = vals['price'] self.display_price = vals['carrier_price'] - else: - raise UserError(vals['error_message']) + return {} + return {'error_message': vals['error_message']} + + def update_price(self): + vals = self._get_shipment_rate() + if vals.get('error_message'): + raise UserError(vals.get('error_message')) return { 'name': _('Add a shipping method'), 'type': 'ir.actions.act_window', @@ -74,6 +86,9 @@ class ChooseDeliveryCarrier(models.TransientModel): } def button_confirm(self): - self.order_id.carrier_id = self.carrier_id - self.order_id.delivery_message = self.delivery_message - self.order_id.set_delivery_line(self.carrier_id, self.delivery_price) + self.order_id.set_delivery_line(self.carrier_id, self.display_price) + self.order_id.write({ + 'recompute_delivery_price': False, + 'delivery_message': self.delivery_message, + 'carrier_id': self.carrier_id.id + }) diff --git a/addons/delivery/wizard/choose_delivery_carrier_views.xml b/addons/delivery/wizard/choose_delivery_carrier_views.xml index e34849eccbe..c0876733a1a 100644 --- a/addons/delivery/wizard/choose_delivery_carrier_views.xml +++ b/addons/delivery/wizard/choose_delivery_carrier_views.xml @@ -7,17 +7,21 @@
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 8145d8166bf..60f4cfad5fc 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -478,12 +478,6 @@ class SaleOrder(models.Model): } return invoice_vals - def print_quotation(self): - self.filtered(lambda s: s.state == 'draft').write({'state': 'sent'}) - - return self.env.ref('sale.action_report_saleorder')\ - .with_context({'discard_logo_check': True}).report_action(self) - def action_view_invoice(self): invoices = self.mapped('invoice_ids') action = self.env.ref('account.action_move_out_invoice_type').read()[0] @@ -1456,8 +1450,19 @@ class SaleOrderLine(models.Model): ]) return super(SaleOrderLine, self)._name_search(name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid) + def _check_line_unlink(self): + """ + Check wether a line can be deleted or not. + + Lines cannot be deleted if the order is confirmed; downpayment + lines who have not yet been invoiced bypass that exception. + :rtype: recordset sale.order.line + :returns: set of lines that cannot be deleted + """ + return self.filtered(lambda line: line.state in ('sale', 'done') and (line.invoice_lines or not line.is_downpayment)) + def unlink(self): - if self.filtered(lambda line: line.state in ('sale', 'done') and (line.invoice_lines or not line.is_downpayment)): + if self._check_line_unlink(): raise UserError(_('You can not remove an order line once the sales order is confirmed.\nYou should rather set the quantity to 0.')) return super(SaleOrderLine, self).unlink()