Odoo de wizards y trasients

Manuel Calero Solís
4 min readOct 21, 2019

Para solucionar una tarea que nos proponían, hemos decidido utilizar unos modelos que heredan de TransientModel y un Wizard para crear la solución.

La tarea consiste en mostrar a nuestro cliente un panel con información de como quedará su precio de venta tras la última compra de un producto concreto.

Para ello creamos un cuadro desde la propia recepción de mercancía, donde podamos ver cuanto a variado su precio de coste y si debe o no actualizar el precio de venta en función del margen y precio de mercado.

Hablando ya técnicamente, vamos a crear un par de modelos transitorios, que contengan información de cabecera, una referencia al albarán de recepción de mercancía, y una relación a las lineas del documento que contiene los datos que el cliente necesita para cambiar el precio de venta o dejarlo como estaba.

class SelectSalePrice(models.TransientModel):
_name = 'select.sale.price'
_description = 'Select Sale Price Wizard'

picking_id = fields.Many2one('stock.picking', 'Stock Picking')
price_line_ids = fields.One2many('select.sale.price.line', 'sale_id')
class SelectSalePriceLine(models.TransientModel):
_name = 'select.sale.price.line'
_description = 'Select Sale Price Line Wizard'

sale_id = fields.Many2one('select.sale.price')
selected = fields.Boolean(string='Selected', default=True, help='Indicate this line is coming to change')
product_id = fields.Many2one('product.product', string='Product', required=True)
previous_purchase_date = fields.Datetime('Previous Purchase Date', required=False)
previous_purchase_price = fields.Float('Previous Purchase Price', digits=dp.get_precision('Product Price'))
previous_cost_price = fields.Float('Previous Cost', digits=dp.get_precision('Product Price'))
current_cost_price = fields.Float('Current Cost', digits=dp.get_precision('Product Price'))
purchase_price = fields.Float('Purchase Price', digits=dp.get_precision('Product Price'))
cost_price = fields.Float('Cost Price', digits=dp.get_precision('Product Price'))
standard_price = fields.Float('Standard Price', digits=dp.get_precision('Product Price'))

Recogiendo los valores a mostrar.

Otro cambio que vamos a abordar es la modificación del modelo de las líneas del albarán, recogeremos dos campos para tener información de como estaba el precio de costo medio del producto antes y después de la recepción de la nueva mercancía.

class StockMove(models.Model):
_inherit = "stock.move"

previous_cost_price = fields.Float('Previous Cost', digits=dp.get_precision('Product Price'))
current_cost_price = fields.Float('Current Cost', digits=dp.get_precision('Product Price'))

Para recoger estos cambios vamos a intervenir en el proceso de validación, reescribiendo el método “button_validate” del modelo “Picking”.

@api.multi
def button_validate(self):
for line in self.move_lines:
line.previous_cost_price = line.product_id.standard_price

super(Picking, self).button_validate()

for line in self.move_lines:
line.current_cost_price = line.product_id.standard_price

Recogemos el valor del precio de coste en “previous_cost_price” y en “current_cost_price”.

Creando la llamada al wizard.

Para llamar al wizard y permita al cliente interactuar con la información y realizar los cambios, vamos a colocar un botón en la propia edición del albarán.

Heredamos de la vista “view_picking_form” e incorporamos el nuevo botón que llamara a la acción “action_open_picking_prices”

<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="view_picking_form_inherit" model="ir.ui.view">
<field name="name">stock.picking.form.inherit</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button class="oe_stat_button" name="action_open_picking_prices" icon="fa-money" type="object" string="Prices"/>
</div>
</field>
</record>

</odoo>

Abriendo el wizard

En la clase “Picking” agregamos el método “action_open_picking_prices”, incluye la verificación de que el albarán este validado para abrir el wizard al cliente.

@api.multi
def action_open_picking_prices(self):
self.ensure_one()
if self.state != 'done':
raise UserError(_('The selected picking does not have validated yet. Please validate the picking.'))
return

view = self.env.ref('account_invoice_change_price.select_sale_price_form')

return {'name': _('Picking Prices'),
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
'res_model': 'select.sale.price',
'view_id': view.id,
'views': [(view.id, 'form')],
'type': 'ir.actions.act_window',
'context': {'default_picking_id': self.id}}

En este método solo quiero destacar que pasamos como contexto el “id” actual del albarán, para que se reciba en el wizard.

Creando el wizard

El wizard esta dividido en dos ficheros, uno Python donde creamos las tablas transitorias y sus relaciones y un fichero xml, donde se define la vista.

En el modelo voy a destacar tan solo un método, cuando se cambia el identificador del albarán, cargamos sus líneas, después de haber borrado las líneas anteriores si las hubiera.

@api.onchange('picking_id')
def _onchange_picking_id(self):
data = []
self.price_line_ids = [(6, 0, [])]
for line in self.picking_id.move_line_ids:
data.append((0, False, self.get_dict_line(line)))
self.price_line_ids = data

Para completar la información que debemos mostrar a nuestro usuario, se realizan consultas en las bases de datos de la compra anterior a la actual y los precios a los que se compraron esos productos.

La ultima columna es editable y nos permite cambiar el valor del precio de venta, en función de la variación de costes que hemos tenido.

Al cambiar el precio la columna “selected” se marcará, indicándonos que la fila ha quedado seleccionada para proceder al cambio.

@api.onchange('standard_price')
def _onchange_standard_price(self):
self.selected = True

Al hacer “Submit” en el wizard se procederá al cambio de los precios.

@api.multi
def action_select_sale_price(self):
for line in self.price_line_ids.filtered(lambda r: r.selected):
line.product_id.standard_price = line.standard_price

Esto es todo, en este ejemplo he querido ayudar a primerizos como yo que se enfrenten a la creación de wizards y modelos transitorios donde recoger información de manera puntual, y trabajar con ellos.

Links:

Proyecto. https://github.com/manuelcalerosolis/account_invoice_change_price.git

https://www.odoo.com/documentation/13.0/howtos/backend.html#wizards

--

--