Auto actualizar campo al cambiar valores de un campo one2many

4,678 views
Skip to first unread message

Rafael Montes

unread,
Sep 3, 2013, 2:27:10 PM9/3/13
to openerp-s...@googlegroups.com
Hola a todos. Estoy haciendo un modulo para contemplar pagos de clientes. Tengo un campo one2many en partners, donde selecciono el concepto del pago, la fecha, y la cantidad. Debajo de ese campo, tengo otro de tipo float en el que me gustaria que se fuera actualizando el sumatorio de la columna cantidad del campo one2many que tengo arriba. He probado con un select sum(cantidad) from pagos, pero no me lo da en tiempo real, ya que cuando lee la tabla, aun no he guardado los cambios del cliente, asi que lee los datos desfasados. En las facturas a cliente, he visto que los desarrolladores de openerp utilizan un boton que pone "actualizar", que te hace lo que yo quiero, solo que me gustaria hacerlo sin tener que pulsar ningun boton, sino que fuera cambiando automaticamente su valor conforme yo voy añadiendo o borrando lineas del campo one2many. He añadido un onchange en el campo one2many, pero desde ahi no se como acceder a la columna "cantidad" de esa vista (no quiero mirar en la tabla por lo que he dicho antes, sino en la vista actual, en tiempo real). Por favor, me gustaria que alguien me dijera si esto es posible, y si es asi, que me diga como se haria. Muchas gracias y un saludo

Carlos López

unread,
Sep 3, 2013, 10:42:12 PM9/3/13
to openerp-s...@googlegroups.com
Hola, para autosumar el valor de un campo, en la vista tree, en la definicion del campo ubicas sum="Etiqueta de suma", ejm:
<tree>
<field name="cantidad" sum="total cantidad"/>
</tree>

con eso a nivel de vistas te mostrara el total de campo, si solo deseas mostrar el total con eso bastaria, pero si el resultado lo vas a utilizar en otras cosas, puedes adicional a eso, crear un campo function para hacer el calculo una vez guardados los datos

Espero te sirva

Saludos Coordiales

Rafael Montes

unread,
Sep 4, 2013, 6:20:08 AM9/4/13
to openerp-s...@googlegroups.com
Hola Carlos! Como podria hacer esa funcion para que me lo calcule al guardar los cambios? Es que el onchange lo tengo bien, pero no se precisamente como hacer ese calculo cuando se graben los datos en vez de cuando se cambie alguna linea del one2many. Muchas gracias

Roberto Lizana

unread,
Sep 4, 2013, 6:31:58 AM9/4/13
to openerp-s...@googlegroups.com
Rafael, otra forma es usando un campo function, te pongo un ejemplo:

def _total(self, cr, uid, ids, field_name, arg, context=None):
result = {}
for line in self.browse(cr, uid, ids, context=context):
result[line.id] = line.quantity * line.price
return result

_columns = {
'quantity': fields.float('Quantity'),
'price': fields.float('Price'),
'total': fields.function(_total, string='Total', store=True),
...
...

En el ejemplo al guardar un registro se actualizar� el campo total.

Un saludo,
Roberto Lizana

El 04/09/13 12:20, Rafael Montes escribi�:

Carlos López

unread,
Sep 4, 2013, 9:49:02 AM9/4/13
to openerp-s...@googlegroups.com
Hola, lo de la etiqueta sum en la definicion del campo en la vista tree es para tener un total actualizado conforme vayas añadiendo o quitando lineas "en vivo", ya que de otra manera, solo podras tener el total una vez que hayas guardado los cambios(como mencionabas). pero ese total no lo puedes acceder como un campo(porque no existe como tal), por ello lo que te recomendaba es que a nivel de vistas utilices eso, pero adicional, crees un campo function como menciona Roberto, solo que le añadiria un trigger para que se vuelva a recalcular el campo cuando cambie la cantidad o el precio, o  en su defecto utilices store=False para que siempre se recalcule

documentacion de campos en OpenERP:



El 4 de septiembre de 2013 05:31, Roberto Lizana <rober....@gmail.com> escribió:
Rafael, otra forma es usando un campo function, te pongo un ejemplo:

    def _total(self, cr, uid, ids, field_name, arg, context=None):
        result = {}
        for line in self.browse(cr, uid, ids, context=context):
            result[line.id] = line.quantity * line.price
        return result

    _columns = {
        'quantity': fields.float('Quantity'),
        'price': fields.float('Price'),
        'total': fields.function(_total, string='Total', store=True),
        ...
        ...

En el ejemplo al guardar un registro se actualizará el campo total.

Un saludo,
Roberto Lizana

El 04/09/13 12:20, Rafael Montes escribió:

Hola Carlos! Como podria hacer esa funcion para que me lo calcule al guardar los cambios? Es que el onchange lo tengo bien, pero no se precisamente como hacer ese calculo cuando se graben los datos en vez de cuando se cambie alguna linea del one2many. Muchas gracias


--
Has recibido este mensaje porque estás suscrito a un tema del grupo "Usuarios OpenERP en España" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/openerp-spain-users/RhckuLwd3uA/unsubscribe. Para anular la suscripción a este grupo y todos sus temas, envía un correo electrónico a openerp-spain-users+unsub...@googlegroups.com.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.



--
Saludos.

Rafael Montes

unread,
Sep 6, 2013, 7:48:10 AM9/6/13
to openerp-s...@googlegroups.com
Hola! lo he hecho así, pero lo que me almacena la función es el valor del campo cantidad de esa linea. Lo que yo necesitaría seria esto:
Imaginaros que esta es mi tabla "cargos":

Alumno Cantidad Fecha

Pepe 80.00 06/09/2013
Ramon 75.00 06/09/2013
Pepe 10.00 07/09/2013

Necesito que cuando entre en la ficha de Pepe, me aparezca en un campo 90.00€, y cuando entre en la de Ramon, 75.00€. Este tema me va a volver loco. Llevo toda mi vida programando en php y visual basic y el python este se va a dar conmigo!! jeje

Muchas gracias a todos que me estáis ayudando muchísimo!

Carlos López

unread,
Sep 6, 2013, 9:22:47 AM9/6/13
to openerp-s...@googlegroups.com
Hola, entiendo que debes tener dos clases, la cabecera y el detalle. en la cabecera es que debe ir el campo function, e iteras sobre las lineas que tenga. y en la misma funcion puedes evaluar de que Alumno estas tomando datos y en base a eso acumular las cantidades


--
Has recibido este mensaje porque estás suscrito a un tema del grupo "Usuarios OpenERP en España" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/openerp-spain-users/RhckuLwd3uA/unsubscribe. Para anular la suscripción a este grupo y todos sus temas, envía un correo electrónico a openerp-spain-u...@googlegroups.com.

Para obtener más opciones, visita https://groups.google.com/groups/opt_out.



--
Saludos.

Rafael Montes

unread,
Sep 6, 2013, 10:01:06 AM9/6/13
to openerp-s...@googlegroups.com
Hola Carlos. Es un campo one2many, y los datos de las filas se guardan en una tabla. Lo que necesito es crear un campo de tipo function que lea la tabla y me sume los valores de una columna.

Carlos López

unread,
Sep 6, 2013, 10:11:18 AM9/6/13
to openerp-s...@googlegroups.com
Ok y donde muestras ese campo function, en la misma vista???
Podiras explicarme un poco de que se trata porque no entiendo muy bien

Rafael Montes

unread,
Sep 6, 2013, 10:24:59 AM9/6/13
to openerp-s...@googlegroups.com
Si mira. He creado un objeto que se llama cargos, con los campos concepto, fecha y cantidad. En res_partners he creado un campo one2many al objeto cargos. Entonces se me muestra en el formulario de clientes una lista de cargos a ese cliente, donde puedo añadir,modificar o eliminar cargos. Entonces quiero añadir un nuevo campo de tipo function donde se almacene la suma del total de cantidades de ese cliente, con un stored=true ya que luego voy a poner lo mismo pero para ingresos, para finalmente tener otro campo donde mostrar el total de los ingresos menos el total de los cargos, para asi tener el saldo total.

Carlos López

unread,
Sep 6, 2013, 10:30:46 AM9/6/13
to openerp-s...@googlegroups.com
Ok, a eso me referia con la cabecera y el detalle, desde mi punto de vista, el campo function total_cantidades debe ir en res.partner(cabecera), ya que el total debe ser por cliente. toma como ejemplo las compras, facturas.


El 6 de septiembre de 2013 09:24, Rafael Montes <rafamon...@gmail.com> escribió:
Si mira. He creado un objeto que se llama cargos, con los campos concepto, fecha y cantidad. En res_partners he creado un campo one2many al objeto cargos. Entonces se me muestra en el formulario de clientes una lista de cargos a ese cliente, donde puedo añadir,modificar o eliminar cargos. Entonces quiero añadir un nuevo campo de tipo function donde se almacene la suma del total de cantidades de ese cliente, con un stored=true ya que luego voy a poner lo mismo pero para ingresos, para finalmente tener otro campo donde mostrar el total de los ingresos menos el total de los cargos, para asi tener el saldo total.
--
Has recibido este mensaje porque estás suscrito a un tema del grupo "Usuarios OpenERP en España" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/openerp-spain-users/RhckuLwd3uA/unsubscribe. Para anular la suscripción a este grupo y todos sus temas, envía un correo electrónico a openerp-spain-u...@googlegroups.com.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.



--
Saludos.

Rafael Montes

unread,
Sep 6, 2013, 11:38:51 AM9/6/13
to openerp-s...@googlegroups.com
Ok. Pero no hay forma en python de hacer una consulta? Por ejemplo select sum(cantidad) from cargos where id_usuario=5, y poder hacerla desde cualquier sitio?

Carlos López

unread,
Sep 6, 2013, 11:45:21 AM9/6/13
to openerp-s...@googlegroups.com
Pues si, en codigo python puedes utilizar el cursor que pasa el OpenERP a todas las funciones, aca la documentacion.
un ejm:
sql = "select sum(cantidad) from cargos where id_usuario=%s" % uid
cr.execute(sql)
resultados = cr.fecthall() #devuelve lista de tuplas O
resultados = cr.dictfecthall() #devuelve lista de diccionarios
print resultados


El 6 de septiembre de 2013 10:38, Rafael Montes <rafamon...@gmail.com> escribió:
Ok. Pero no hay forma en python de hacer una consulta? Por ejemplo select sum(cantidad) from cargos where id_usuario=5, y poder hacerla desde cualquier sitio?
--
Has recibido este mensaje porque estás suscrito a un tema del grupo "Usuarios OpenERP en España" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/openerp-spain-users/RhckuLwd3uA/unsubscribe. Para anular la suscripción a este grupo y todos sus temas, envía un correo electrónico a openerp-spain-u...@googlegroups.com.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.



--
Saludos.

Pedro Manuel Baeza Romero

unread,
Sep 6, 2013, 11:47:50 AM9/6/13
to openerp-s...@googlegroups.com
Se puede usar, pero siempre es conveniente no utilizar directamente SQL:

https://doc.openerp.com/6.0/contribute/15_guidelines/coding_guidelines_framework/#do-not-bypass-the-orm

Un saludo.


--
Has recibido este mensaje porque estás suscrito al grupo "Usuarios OpenERP en España" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a openerp-spain-u...@googlegroups.com.

Carlos López

unread,
Sep 6, 2013, 11:56:19 AM9/6/13
to openerp-s...@googlegroups.com
Gracias por la recomendacion, siempre es mejor usar el ORM, pero para tipo consultas que incluyen sum(campo) u otro,  hasta donde se, directamente no se pueden con el search, read u otro metodo.
a menos que sea un modelo tipo reporte
_auto=False 
y en el __init__ se escribe la SQL

Pero en los casos como los que plantea Rafael si utilizo sql directamente, o hay otra manera?????

Pedro Manuel Baeza Romero

unread,
Sep 6, 2013, 12:14:32 PM9/6/13
to openerp-s...@googlegroups.com
Se puede suplir esa ausencia con código Python:

total = sum(for x.campo in self.browse(cr, uid, ids, ['campo'])) - no sé si la sentencia es totalmente correcta -

Sería cuestión de ver si el rendimiento disminuye mucho.

Un saludo.

Carlos López

unread,
Sep 6, 2013, 12:17:00 PM9/6/13
to openerp-s...@googlegroups.com
mmm no se me habia ocurrido, muchas gracias, solo algo, el metodo seria read para optimizar el rendimiento

Pedro Manuel Baeza Romero

unread,
Sep 6, 2013, 12:20:16 PM9/6/13
to openerp-s...@googlegroups.com
Pues sí, buen apunte, sobre la marcha he puesto browse con sólo ese campo, pero probablemente el read obtenga mejor rendimiento.

Un saludo.

Rafael Montes

unread,
Sep 8, 2013, 10:06:18 AM9/8/13
to openerp-s...@googlegroups.com
Hola a todos! Pues no soy capaz de conseguirlo!
He puesto un defaults para que me cargue el campo con el valor de la función _sum_cargos.

_defaults = {
     'total_cargos':_sum_cargos,
}

La función la tengo así:

def _sum_cargos(self, cr, uid, ids, context = None):
     cr.execute('select sum(cantidad) from cargos_cargos where cargo_alumno=%s' % uid)
     cantidad = cr.fetchone()[0]
     return float(cantidad)

Pero al final, el campo total_cargos está siempre a 0.00. No se me cambia nunca el valor. Yo ya no se que hacer de verdad

Pedro Manuel Baeza Romero

unread,
Sep 9, 2013, 4:03:58 AM9/9/13
to openerp-s...@googlegroups.com
Buenas, Rafael,

El problema es que en el _defaults debes definir la función con la expresión lamba, ya que si no, coge un valor estático que se calcula la primera vez que se llama a esa función. En la parte del warning de la guía de estilo de este enlace hace mención a ello:

https://doc.openerp.com/6.0/contribute/15_guidelines/coding_guidelines_framework/#there-is-better-than-lambda-sometimes

Un saludo.


--

Pablo Cayuela Martínez - ASERTI

unread,
Sep 10, 2013, 2:52:55 AM9/10/13
to openerp-s...@googlegroups.com
Hola a todos

Rafael, si no me equivoco las funciones que definas en el _defaults sólo se llamarán una vez cuando crees un objeto de ese tipo. En ese momento el objeto cabecera no puede tener ninguna línea asociada (al no existir ni hay un id al que puedan apuntar los many2one de las líneas) y el recuento dará cero. Otra cosa con la que hay que tener cuidado es que las funciones que se usan en los defaults no llevan el argumento ids, así que es probable que estés recibiendo en él el diccionario del contexto. Deberían quedar tal que así:
def dfl_volume_uom(self, cr, uid, context=None)

La solución a mi parecer sería la que ya han comentado: definir un campo funcional sin store o con las reglas de store para que se dispare si cambia el campo o2m del propio objeto cabecera que "contiene" las lineas o el campo importe del objeto de las lineas.

@Pedro, creo que tanto una función lambda como una "nombrada" normal (que también es invocable) valdrían para que el valor no fuese estático como dicen en el warning. Al arrancar el servidor se evalúa lo que le hayas puesto (en el caso de la fecha al no ser invocable se evalúa y queda fija la fecha del arranque del servidor) y si es una función normal contendrá la referencia a la función que debe llamarse. Perdón por la corrección, pero como en la parte funcional estoy todavía un poco perdido y la única que empiezo a cogerle el tranquillo es la técnica, sentía el deber de colaborar con lo poco que sé a esta comunidad que tanto nos ha aportado.

Un saludo.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a openerp-spain-users+unsub...@googlegroups.com.

Pedro Manuel Baeza Romero

unread,
Sep 10, 2013, 7:59:02 AM9/10/13
to openerp-s...@googlegroups.com
Buenas, Pablo,

Tienes razón, yo me fijé simplemente en la sección _defaults, viendo que no había puesto el lambda, pero sin saber cuál era el contexto de lo que quería hacer, y desde luego si quiere obtener un total, eso no valdría. Creo que un ejemplo muy válido de algo parecido podría ser el campo amount_total de las facturas, que se calcula sobre el contenido de las líneas, de los impuestos, etc...

Perdón por la confusión.

Un saludo.


Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a openerp-spain-u...@googlegroups.com.

Rafael Montes

unread,
Sep 12, 2013, 7:46:29 PM9/12/13
to openerp-s...@googlegroups.com
Hola a todos! por fin lo conseguí! Aquí pongo el código por si alguien lo necesitara.

def suma_cargos(self, cr, uid, ids,name,arg, context=None):
return {obj.id: sum(cargos_ids.cantidad for cargos_ids in obj.cargos_ids)
for obj in self.browse(cr, uid, ids, context=context)}

_columns = {
'total_cargos': fields.function(suma_cargos, type = 'float', string = 'Total Cargos', store=True),

}

Un saludo

JUAN FRANCISCO CARRASQUEL RAMIREZ

unread,
Jul 14, 2016, 4:29:01 PM7/14/16
to Usuarios Odoo / OpenERP en España
Buenas esta función es la que yo suelo usar, para calculo no se si te sirva de referencia cambiando tus variables y adaptando a tu agrado:
   @api.one
    @api.depends('medida1', 'medida2', 'medida3', 'piece')
    def _calcular_suma_pieces(self):
        # print "Calcular suma y multiplicaciones de variables Commodity"
       if self.piece > 0:
      
           cantidad = self.piece
           length = self.medida2
           width = self.medida1 
           height = self.medida3
           self.total = width * length * height
           self.total1 = (width * length * height) * cantidad
        
       else:  
            self.piece = 1
            cantidad = self.piece
            width = self.medida1 
            length = self.medida2
            height = self.medida3
            self.total = width * length * height
            self.total1 = (width * length * height) * cantidad

Por otra parte tengo una duda, si me pudieran responder, se los agradecería,  ya que no logro comprender bien el material que leo por cuestiones de presión acá, requiero que ese total que arroja ese cálculo que coloque, se me muestre en dos vistas diferentes simultáneamente, como pudiera hacer eso? o con que métodos en específico una referencia a eso.

La otra duda es como pudiera, traerme a través de una vista dos valores de la base de datos a dos campos que contienen la vista, con que método, función, etc pudiera yo realizar este requerimiento?

La tercera duda es la siguiente como copio el valor de un campo y lo coloco en otro campo en una misma vista para ese caso estaba usando la siguiente api
Este es un resumen del archivo .PY:

    @api.one
    @api.depends('employee') # EL VALOR QUE ADQUIERE ESTE CAMPO A TRAVEZ DE MANY2ONE LO QUIERO MOSTRAR EN EL OTRO CAMPO LLAMADO Issued_by.
    def login(self):
        employee=self.employee 
        issued_by = {}
        if self.employee != 0:
            self.issued_by = employee
            print issued_by 


    employee = fields.Many2one('res.users')
    issued_by = fields.Char(string='Issue By:', store=True, readonly=True, compute='login', track_visibility='always')

Actualmente esta consulta me genera un resultado no muy esperado :

En este ejemplo: el campo issue By quiero que me muestre el mismo valor de arriba Employee que es extraido de un campo many2one colocar esa informacion en el Issue By el campo de abajo, para así obtener como resultado una especie de logeo de  usuario ya que así lo requieren, como podran ver en la imagen cuando coloco a juan carlos que es el registro n° 5 ubicado en la base de datos en issue by me arroja res.user (5) y cambia de numero a medida que cambio de usuario pero el resultado que quiero es que me arroje ese mismo nombre en ambos campos. espero me puedan ayudar y haber sido la mas explicito posible, feliz tarde.
Reply all
Reply to author
Forward
Message has been deleted
0 new messages