Problem with self-calculated fields in a table

2

I have this table:

db.define_table('cotizaciones',
                Field('numero_ticket', 'reference tickets'),
                Field('numero_cotizacion', 'string'),
                Field('fecha_registro_cotizacion', 'date', default=now),
                Field('reporte_adjunto', 'upload'),
                Field('cliente', 'reference clientes', default='',requires=IS_EMPTY_OR(IS_IN_DB(db, db.clientes, '%(cliente)s', zero='---- Elegir Opcion ----'))),
                #Field('marca_gg', 'reference marcas_definiciones', requires=IS_EMPTY_OR(IS_IN_DB(db, db.marcas_definiciones, '%(marca)s', zero='---- Elegir Opcion ----'))),
                Field('contacto_cotizacion', 'reference contactos', requires=IS_EMPTY_OR(IS_IN_DB(db, db.contactos, '%(contacto_cliente)s', zero='---- Elegir Opcion ----'))),
                Field('fecha_cotizacion', 'date',default=now),
                Field('servicio_producto_cotizacion', 'reference servicios_productos', requires=IS_EMPTY_OR(IS_IN_DB(db, db.servicios_productos, '%(servicio_producto)s', zero='---- Elegir Opcion ----'))),
                Field('tipo_trabajo', 'reference tipos_trabajos', requires=IS_EMPTY_OR(IS_IN_DB(db, db.tipos_trabajos, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('tipo_horario', 'reference tipos_horarios', requires=IS_EMPTY_OR(IS_IN_DB(db, db.tipos_horarios, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('tipo_cotizacion', 'reference tipos_cotizaciones', requires=IS_EMPTY_OR(IS_IN_DB(db, db.tipos_cotizaciones, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('plazo_ejecucion_promedio', 'string', default="Null"),
                Field('moneda', 'reference monedas', requires=IS_EMPTY_OR(IS_IN_DB(db, db.monedas, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('alcances_servicio', 'reference alcances_servicios', requires=IS_EMPTY_OR(IS_IN_DB(db, db.alcances_servicios, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('aplica_adicionales', 'boolean'),
                Field('aplica_penalidades', 'boolean'),
                Field('plazo_pago', 'reference plazos_pagos', requires=IS_EMPTY_OR(IS_IN_DB(db, db.plazos_pagos, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('calidad_acabado', 'reference calidades_acabados', requires=IS_EMPTY_OR(IS_IN_DB(db, db.calidades_acabados, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('calidad_material', 'reference calidades_materiales' ,requires=IS_EMPTY_OR(IS_IN_DB(db, db.calidades_materiales, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('calidad_limpieza', 'reference calidades_limpieza' ,requires=IS_EMPTY_OR(IS_IN_DB(db, db.calidades_limpieza, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('disposicion_desmonte', 'reference disposiciones_desmonte' ,requires=IS_EMPTY_OR(IS_IN_DB(db, db.disposiciones_desmonte, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('tiempo_garantia', 'reference tiempos_garantia', requires=IS_EMPTY_OR(IS_IN_DB(db, db.tiempos_garantia, '%(nombre)s', zero='---- Elegir Opcion ----'))),
                Field('porcentaje_gastos_generales', type='decimal(10,2)'),
                Field('porcentaje_utilidad', type='decimal(10,2)'),
                Field('porcentaje_descuento', type='decimal(10,2)'),
                Field('porcentaje_igv', type='decimal(10,2)'),
                Field('costo_directo',type='decimal(10,2)'),
                Field('gastos_generales',
                      compute=lambda r: round((r['costo_directo'] * r['porcentaje_gastos_generales'] )/ 100)),
                Field('utilidad', compute=lambda r: round((r['costo_directo'] * r['porcentaje_utilidad']) / 100)),
                Field('sub_total',
                      compute=lambda r: round(r['costo_directo'] + r['gastos_generales'] + r['utilidad'])),
                Field('descuento', compute=lambda r: round((r['sub_total'] * r['porcentaje_descuento'] )/ 100)),
                Field('sub_total_venta', compute=lambda r: round(r['sub_total'] - r['descuento'])),
                Field('impuesto_igv', compute=lambda r: round((r['sub_total_venta'] * r['porcentaje_igv'])/100)),
                Field('total_venta', compute=lambda r: round(r['sub_total'] + r['impuesto_igv'])),
                Field('cotizacion_adjunto', 'upload'),
                Field('observaciones', 'text'),
                Field('nota', 'text'),
                format='%(numero_cotizacion)s')

What should I do automatic calculation, but when processing I get this error:

    
asked by Fernando M. Goycochea 28.01.2016 в 18:35
source

1 answer

4

The problem is the round function, in Python 2 you return a float even if you pass it as a parameter Decimal , as you are using type field decimal(m, n) internally web2py is using a Decimal for represent the amounts of your fields.

Python 2.7.6:

>>> from decimal import Decimal
>>> round(Decimal('99.99'), 2)
99.99
>>> type(round(Decimal('99.99'), 2))
float

Python 3.4.3:

>>> from decimal import Decimal
>>> round(Decimal('99.99'), 2)
Decimal('99.99')
>>> type(round(Decimal('99.99'), 2))
decimal.Decimal

Then as in your calculated field you are using this:

lambda r: round(r['costo_directo'] + r['gastos_generales'] + r['utilidad'])

But at the same time, the field gastos_generales is computed and returns a rounded value (in float ), that's why you get the error when trying to add costo_directo with gastos_generales ( Decimal + float ):

TypeError: unsupported operand type(s) for +: 'Decimal' and 'float'

Well, the above was just to identify the problem, the issue is that to round amounts with decimals, the process is different. I recommend you use quantize :

>>> from decimal import Decimal, ROUND_HALF_UP
>>> Decimal('5.504').quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
Decimal('5.50')
>>> Decimal('5.505').quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
Decimal('5.51')

Ideally, you should have some kind of defined function that you can call to round the amounts:

from decimal import Decimal, ROUND_HALF_UP

def redondear(monto, exponente=Decimal('.01')):
    return monto.quantize(exponente, rounding=ROUND_HALF_UP)

With the previous function you could do something like this:

>>> redondear(Decimal('0.5444444')) # 2 decimales por defecto
Decimal('0.54')
>>> redondear(Decimal('0.5444444'), exponente=Decimal('.001')) # 3 decimales
Decimal('0.544')
>>> redondear(Decimal('0.5444444'), exponente=Decimal('.0001')) # 4 decimales
Decimal('0.5444')

Returning to web2py, you can change the lambda functions of your computed fields by something like this:

lambda r: redondear(r['costo_directo'] + r['gastos_generales'] + r['utilidad'])
    
answered by 28.01.2016 / 20:12
source