[IMP] delivery, sale: improvements in recomputing of shipping cost
The "Recompute shipping cost" button will now reopen the wizard
to be able to change carrier.
When adding to the order, it will replace the current shipping cost.
Relabelled the "Recompute shipping cost" to “Update Shipping Cost”
and moved it to the right.
Shipping method field will have radio widget and cost will be editable
in the wizard.
Also, it was not allowed to delete a delivery line when the order was
confirmed ('sale' state). So here, as we want to allow user to change the
carrier even after the order is confirmed, we have allowed delivery line
to be deleted so that new line is added in place of that.
task-2033229
This commit is contained in:
committed by
Damien Bouvy
parent
9f32b937ba
commit
6173dd2141
@@ -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)
|
||||
|
||||
@@ -331,15 +331,17 @@
|
||||
attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('order_line', '=', []), ('delivery_set', '=', True)]}"
|
||||
/>
|
||||
<button
|
||||
string="Recompute shipping cost"
|
||||
name="recompute_delivery_cost"
|
||||
string="Update shipping cost"
|
||||
name="action_open_delivery_wizard"
|
||||
context="{'carrier_recompute':True}"
|
||||
type="object"
|
||||
class="text-warning btn-secondary"
|
||||
attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('recompute_delivery_price', '=', False), ('delivery_set', '=', False)]}"
|
||||
/>
|
||||
<button
|
||||
string="Recompute shipping cost"
|
||||
name="recompute_delivery_cost"
|
||||
string="Update shipping cost"
|
||||
name="action_open_delivery_wizard"
|
||||
context="{'carrier_recompute':True}"
|
||||
type="object"
|
||||
attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('recompute_delivery_price', '=', True), ('delivery_set', '=', False)]}"
|
||||
/>
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -7,17 +7,21 @@
|
||||
<form>
|
||||
<field name='available_carrier_ids' invisible="1"/>
|
||||
<group>
|
||||
<field name="carrier_id" domain="[('id', 'in', available_carrier_ids)]"/>
|
||||
<field name="delivery_type" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="order_id" invisible="1"/>
|
||||
<field name='delivery_price' invisible="1"/>
|
||||
<field name='display_price' string="Cost" widget="monetary" options="{'currency_field': 'currency_id'}" attrs="{'invisible': [('carrier_id','=', False)]}"/>
|
||||
<group>
|
||||
<field name="carrier_id" domain="[('id', 'in', available_carrier_ids)]" widget='radio'/>
|
||||
<field name="delivery_type" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="order_id" invisible="1"/>
|
||||
<field name='delivery_price' invisible="1"/>
|
||||
<label for="display_price"/>
|
||||
<div class="o_row">
|
||||
<field name='display_price' widget="monetary" options="{'currency_field': 'currency_id'}" attrs="{'invisible': [('carrier_id','=', False)]}"/>
|
||||
<button name="update_price" type="object" attrs="{'invisible': [('delivery_type','in', ('fixed', 'base_on_rule'))]}">
|
||||
<i class="fa fa-arrow-right mr-1"/>Get rate
|
||||
</button>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<button name="update_price" type="object" attrs="{'invisible': [('delivery_type','in', ('fixed', 'base_on_rule'))]}">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
Get rate
|
||||
</button>
|
||||
<div role="alert" class="alert alert-warning" attrs="{'invisible': [('invoicing_message', '=', '')]}">
|
||||
<field name="invoicing_message" nolabel="1"/>
|
||||
</div>
|
||||
@@ -25,7 +29,8 @@
|
||||
<field name="delivery_message" nolabel="1"/>
|
||||
</div>
|
||||
<footer>
|
||||
<button name="button_confirm" type="object" string="Add" class="btn-primary"/>
|
||||
<button name="button_confirm" invisible="not context.get('carrier_recompute')" type="object" string="Update" class="btn-primary"/>
|
||||
<button name="button_confirm" invisible="context.get('carrier_recompute')" type="object" string="Add" class="btn-primary"/>
|
||||
<button string="Discard" special="cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user