def rellenar_tabla(self, facturas): """ Rellena el model con los items de la consulta """ model = self.wids['tv_datos'].get_model() model.clear() self.wids['tv_datos'].freeze_child_notify() self.wids['tv_datos'].set_model(None) for cliente in facturas: padre = model.append(None, (cliente and cliente.nombre or "Sin cliente", "0.00", "", "0.00", cliente and cliente.get_puid() or "")) for factura in facturas[cliente]: formapago = factura.get_forma_de_pago() importe = factura.calcular_total() pdte = factura.calcular_pendiente_de_documento_de_pago() model[padre][1] = utils.float2str(utils._float(model[padre][1]) + importe) model[padre][3] = utils.float2str(utils._float(model[padre][3]) + pdte) model.append(padre, (factura.numfactura, utils.float2str(importe), utils.str_fecha(factura.fecha), utils.float2str(pdte), factura.get_puid())) self.wids['tv_datos'].set_model(model) self.wids['tv_datos'].thaw_child_notify()
def parsear_fraccion(txt): """ Devuelve la cantidad de producto compra y unidad que hay que descontar por cada cantidad de producto venta y unidad (que también se devuelven). Es necesario que venga la cantidadpc aunque en el registro, en el campo "unidad" no aparece. """ import re regexp_float = re.compile("-?\d+[\.,]?\d*") regexp_unidad = re.compile("\w+") cantidades = regexp_float.findall(txt) if len(cantidades) == 1: cantidadpc = cantidades[0] cantidadpv = '1' txt = txt.replace(cantidadpc, "") elif len(cantidades) == 2: cantidadpc, cantidadpv = cantidades[0:2] txt = txt.replace(cantidadpc, "") txt = txt.replace(cantidadpv, "") else: cantidadpc = '1' cantidadpv = '1' txt = txt.replace("/", "") unidadpc, unidadpv = regexp_unidad.findall(txt)[0:2] cantidadpc = utils._float(cantidadpc) cantidadpv = utils._float(cantidadpv) return cantidadpc, unidadpc, cantidadpv, unidadpv
def cell_func(column, cell, model, itr, numcol): """ Si la fila corresponde a un parte de producción no verificado, lo colorea en rojo oscuro, si no, lo hace en verde oscuro. """ consumido = model[itr][1] producido = model[itr][2] try: consumido = utils._float(consumido) producido = utils._float(producido) if consumido < producido: if numcol == 1: color = "red" elif numcol == 2: color = "blue" else: if numcol == 1: color = "blue" else: color = "red" except: color = None if consumido == 0 or producido == 0: color = None cell.set_property("cell-background", "red") else: cell.set_property("cell-background", None) cell.set_property("foreground", color)
def cambiar_existencias(self, cell, path, texto): # Chequeo permiso de escritura para camibar precios, existencias y demás: try: ventana = pclases.Ventana.select(pclases.Ventana.q.fichero == "productos.py")[0] if self.usuario and (not self.usuario.get_permiso(ventana).escritura or self.usuario.nivel >= 2): utils.dialogo_info(titulo = "USUARIO SIN PRIVILEGIOS", texto = "Necesita permiso de escritura para modificar las existencias.", padre = self.wids['ventana']) return except IndexError: if self.usuario and self.usuario.nivel >= 2: utils.dialogo_info(titulo = "USUARIO SIN PRIVILEGIOS", texto = "No tiene permisos suficientes para modificar las existencias.", padre = self.wids['ventana']) return model = self.wids['tv_datos'].get_model() id = model[path][-1] try: existencias = utils._float(texto) except ValueError: utils.dialogo_info(titulo = "ERROR DE FORMATO", texto = "El texto %s no es correcto." % (texto), padre = self.wids['ventana']) else: if "PC" in model[path][0]: producto = pclases.ProductoCompra.get(id) producto.sync() producto.existencias = existencias producto.syncUpdate() model[path][3] = utils.float2str(producto.existencias)
def cambiar_precio_ldp(self, cell, path, texto): """ Cambia el precio de la LDP conforme al texto recibido. """ try: precio = utils._float(texto) except: utils.dialogo_info(titulo = "ERROR", texto = 'El texto "%s" no es un número.' % (texto), padre = self.wids['ventana']) else: model = self.wids['tv_ldps'].get_model() ldp = pclases.LineaDePedido.get(model[path][-1]) if ldp.precio != precio: ldp.precio = precio if ldp.get_lineas_de_venta() != [] \ and utils.dialogo(titulo = "¿CAMBIAR PRECIO PRODUCTOS SERVIDOS?", texto = """ Al cambiar el precio de una parte del presupuesto ofertado, se cambian automáticamente los precios de los pedidos involucrados. También puede cambiar los albaranes y facturas si el pedido ya ha sido servido. ¿Desea cambiar el precio de todos los artículos servidos de este producto? Si lo hace, se cambiará también en la factura en caso de que se haya facturado el albarán o albaranes correspondientes. """, padre = self.wids['ventana']): for ldv in ldp.get_lineas_de_venta(): ldv.precio = ldp.precio self.rellenar_tablas()
def cambiar_peso(self, cell, path, texto): """ Cambia el peso de la bala editada. """ try: peso = utils._float(texto) except ValueError: utils.dialogo_info(titulo = "ERROR", texto = "El valor tecleado %s no es correcto." % (peso), padre = self.wids['ventana']) else: model = self.wids['tv_balas'].get_model() try: bala = pclases.BalaCable.get(model[path][-1]) except: utils.dialogo_info(titulo = "ERROR", texto = "No se pudo acceder a la bala.", padre = self.wids['ventana']) self.actualizar_tabla() else: difpeso = bala.peso - peso bala.peso = peso bala.syncUpdate() model[path][2] = utils.float2str(bala.peso, 1) try: totpantalla = utils.parse_float( self.wids['e_pantalla'].get_text()) except: totpantalla = 0.0 totpantalla -= difpeso self.rellenar_totales(totpantalla)
def seleccionar_cantidad(self, producto): """ Muestra un diálogo para introducir la cantidad. Si el producto es un rollo, se introducirá en metros cuadrados. Si es una bala, se introducirá en kilos. En las LDV se mantendrán también estas unidades ya que el precio por unidad va en función de kilos y rollos en el producto. Cuando se haga el albarán es cuando habrá que calcular cuantos rollos (y cuáles en concreto) y cuántas balas entran. Aunque en realidad es el usuario el que las seleccionará y el programa irá informando si se satisface la LDV o no. """ if isinstance(producto, pclases.ProductoCompra): txt = "Introduzca la cantidad en %s." % (producto.unidad) else: txterror = "presupuestos::seleccionar_cantidad -> ERROR: Producto %s no es producto de compra ni de venta." % (producto) self.logger.error(txterror) cantidad = utils.dialogo_entrada(titulo = 'CANTIDAD', texto = txt, padre = self.wids['ventana']) try: cantidad = utils._float(cantidad) return cantidad except: utils.dialogo_info(titulo = 'ERROR', texto = 'La cantidad introducida no es correcta.', padre = self.wids['ventana']) return None
def parsear_porcentaje(txt): """ Devuelve la cantidad del porcentaje como fracción de 1. """ import re regexp_float = re.compile("^-?\d+[\.,]?\d*") num = regexp_float.findall(txt)[0] return utils._float(num) / 100
def rellenar_tabla(self, res): """ Rellena la tabla de datos con los resultados del diccionario. """ model = self.wids['tv_datos'].get_model() model.clear() if not res: self.wids['e_total'].set_text("") return total = 0.0 productos = {} # Para facturar solo una vez cada producto a # cada cliente. # TO-DO: OJO: Si se deja más de un producto sin # facturar en el mismo periodo de la consulta, solo se # va a ver una vez. for cliente in res: if cliente not in productos: productos[cliente] = [] abuelo = model.append(None, (cliente.nombre, "0.00", cliente.get_puid())) for p in res[cliente]: try: descripcion_producto = p.descripcion puid_producto = p.get_puid() except AttributeError: descripcion_producto = "Sin producto relacionado "\ "(clases sueltas)" puid_producto = "" padre = model.append(abuelo, (descripcion_producto, "0.00", puid_producto)) for a in res[cliente][p]: producto = a.get_or_guess_productoCompra() if producto not in productos[cliente]: importe = a.get_precio(cliente) model.append(padre, (a.get_hora_y_descripcion(), utils.float2str(importe), a.get_puid())) model[padre][1] = utils.float2str( utils._float(model[padre][1]) + importe) model[abuelo][1] = utils.float2str( utils._float(model[abuelo][1]) + importe) total += importe productos[cliente].append(producto) self.wids['e_total'].set_text(utils.float2str(total))
def numerals(nNumero, lFemenino=0, moneda = "", fraccion = "", autoomitir = False): """ numerals(nNumero, lFemenino) --> cLiteral Convierte el número a una cadena literal de caracteres P.e.: 201 --> "doscientos uno" 1111 --> "mil ciento once" <nNumero> Número a convertir <lFemenino> = 'true' si el Literal es femenino P.e.: 201 --> "doscientas una" Los parámetros "moneda" y "fracción" se usarán tras las cantidades enteras y decimales respectivamente. Si por ejemplo moneda == "euros" y fraccion == "céntimos", se obtendrá algo como: dieciséis euros con cincuenta céntimos (Legalmente se acepta tanto la preposición "con" como la conjunción copulativa "y" para expresión en letra de la moneda en España.) Si "autoomitir" es True y la parte fraccionaria es 0, no la devuelve. """ # Nos aseguramos del tipo de <nNumero> # se podría adaptar para usar otros tipos (pe: float) cRes = None if isinstance(nNumero, str): try: nNumero = long(nNumero) except ValueError: nNumero = _float(nNumero) if isinstance(nNumero, int): if nNumero<0: cRes = "menos "+_numerals(-nNumero, lFemenino) elif nNumero==0: cRes = "cero" else: cRes = _numerals(nNumero, lFemenino) # Excepciones a considerar if not lFemenino and nNumero%10 == 1 and nNumero%100!=11: cRes += "o" elif isinstance(nNumero, (float, ffloat)): if nNumero >= 0: parte_entera = numerals(int(nNumero), lFemenino) numparte_decimal = int(round((nNumero % 1) * 100)) if not(numparte_decimal == 0 and autoomitir): parte_decimal = numerals(numparte_decimal, lFemenino) cRes = "%s %s con %s %s" % (parte_entera, moneda, parte_decimal, fraccion) else: cRes = "%s %s" % (parte_entera, moneda) else: nNumero *= -1 parte_entera = numerals(int(nNumero), lFemenino) parte_decimal = numerals(int(round((nNumero % 1) * 100)), lFemenino) cRes = "menos %s %s con %s %s" % (parte_entera, moneda, parte_decimal, fraccion) try: cRes = reduce(lambda x, y: x[-1] == " " and y == " " and x or x+y, cRes) # Elimino los "dobles espacios". except TypeError: pass # cRes es "" (vacío), None (no es iterable) o algo así. return cRes
def add(self, w): if self.producto != None: fecha = self.wids['e_fecha'].get_text() if fecha == '': utils.dialogo_info(titulo = 'SIN FECHA', texto = 'Debe introducir la fecha del resultado de la prueba.') return resultado1 = self.wids['e_resultado1'].get_text() if resultado1 == '': utils.dialogo_info(titulo = 'SIN RESULTADO', texto = 'Debe introducir el resultado de la prueba en resultado1.', padre = self.wids['ventana']) return resultado2 = self.wids['e_resultado2'].get_text() try: mfi = utils._float(self.wids['e_mfi'].get_text()) except: utils.dialogo_info(titulo = "MFI INCORRECTO", texto = "No ha introducido un valor correcto para el MFI del proveedor.", padre = self.wids['ventana']) return try: resultado = float(resultado1) prueba = pclases.PruebaGranza(fecha = utils.parse_fecha(fecha), resultado = resultado, productoCompra = self.producto, fechaEntrada = mx.DateTime.localtime(), silo = self.wids['e_silo'].get_text(), lote = self.wids['e_lote'].get_text(), mfi = mfi) if resultado2: try: resultado = float(resultado2) prueba = pclases.PruebaGranza( fecha = utils.parse_fecha(fecha), resultado = resultado, productoCompra = self.producto, fechaEntrada = mx.DateTime.localtime(), silo = self.wids['e_silo'].get_text(), lote = self.wids['e_lote'].get_text(), mfi = mfi) except Exception, msg: utils.dialogo_info(titulo = 'ERROR', texto = 'Verifique que ha introducido los datos correctamente.\n\n\nInformación de depuración: %s' % (msg), padre = self.wids['ventana']) except Exception, msg: utils.dialogo_info(titulo = 'ERROR', texto = 'Verifique que ha introducido los datos correctamente.\n\n\nInformación de depuración: %s' % (msg), padre = self.wids['ventana']) self.wids['e_fecha'].set_text(utils.str_fecha(time.localtime())) self.wids['e_resultado1'].set_text('') self.wids['e_resultado2'].set_text('') self.wids['e_lote'].set_text('') self.wids['e_silo'].set_text('') self.wids['e_mfi'].set_text('0') self.rellenar_pruebas()
def get_datos_from_tv(tv, filtro_ceros): """ Devuelve una lista de tuplas. Cada tupla contiene los datos de las cells del TreeView para cada fila. Si la fila es padre de otra fila, añade debajo de la misma las filas hijas con espacios a su izquierda y un separador horizontal al final. """ # PLAN: Se pueden volcar también fórmulas simplemente como texto en el csv. # P. ej.: "=A1+B1". El problema es que en los datos extraídos tal cual del # TreeView no sé qué columnas corresponden a sumatorios. Necesitaría un # marcado especial o un cell invisible dentro de la columna o algo así; y # llevar además el control de la columna [A..n] y fila [1..m] para poder # escribir correctamente la expresión. datos = [] model = tv.get_model() numcols = len(tv.get_columns()) for fila in model: filadato = [] for i in xrange(numcols): index_columna = tv.get_column(i).get_data("q_ncol") if index_columna is None: index_columna = i valor = fila[index_columna] if isinstance(valor, bool): dato = valor and u"Sí".encode("iso-8859-15") or "No" else: try: valor = utils._float(valor) valor = str(valor).replace(".", ",") except ValueError: pass # No es flotante ni se puede convertir a él. finally: try: dato = ("%s" % (valor)).replace(";", ",").encode("iso-8859-15") except UnicodeEncodeError: dato = ("%s" % (valor)).replace(";", ",").encode("iso-8859-15", "replace") if i in filtro_ceros: if dato in ("0", "0,0", "0,00", "0,000", 0, "0.0", "0.00", "0.000"): dato = "" # Más rápido que una regexp. filadato.append(dato) datos.append(filadato) if hasattr(fila, 'iterchildren'): filas_hijas = agregar_hijos(fila, numcols, 1, tv) #if filas_hijas != [] and len(datos) > 1 and datos[-2][0] != "---": # datos.insert(-1, ("---", ) * numcols) for fila_hija in filas_hijas: datos.append(fila_hija) #if filas_hijas != []: # datos.append(("---", ) * numcols) return datos
def cambiar_resultado(self, tv, path, texto, columna): texto = texto.replace(" ", "") if texto != "": try: resultado = utils._float(texto) except: utils.dialogo_info('RESULTADO INCORRECTO', 'El número tecleado (%s) no es correcto.' % (texto), padre = self.wids['ventana']) return clase = self.get_clase(columna) columnaid = columna-1 # Porque en los IDS empieza por 0 if clase != None: model = self.wids['tv_pruebas'].get_model() ids = map(int, model[path][-1].split(',')) id = ids[columnaid] if id == 0: if texto != "": fecha = time.strptime(model[path][0], '%d/%m/%Y') try: prueba = clase(fecha = fecha, resultado = resultado, loteCem = self.loteCem, lote = None) except TypeError: # Es prueba de Humedad, no lleva relación con lote de fibra: prueba = clase(fecha = fecha, resultado = resultado, loteCem = self.loteCem) ids[columnaid] = prueba.id model[path][-1] = ','.join(map(str, ids)) model[path][columna] = "%.2f" % resultado else: prueba = clase.get(int(id)) if texto == "": try: prueba.destroySelf() except: utils.dialogo_info(titulo = "ERROR", texto = "El resultado no se pudo eliminar.", padre = self.wids['ventana']) return model[path][columna] = "" ids[columnaid] = 0 model[path][-1] = ','.join(map(str, ids)) self.rellenar_pruebas() # Prefiero esto a comprobar si la fila se ha quedado vacía, etc... else: prueba.resultado = resultado if columna != 6: model[path][columna] = "%.2f" % resultado else: model[path][columna] = "%d" % resultado self.calcular_caracteristicas()
def cambiar_cantidad_ldp(self, cell, path, texto): """ Cambia la cantidad de la LDP conforme al texto recibido. """ try: cantidad = utils._float(texto) except: utils.dialogo_info(titulo = "ERROR", texto = 'El texto "%s" no es un número.' % (texto), padre = self.wids['ventana']) else: model = self.wids['tv_ldps'].get_model() ldp = pclases.LineaDePedido.get(model[path][-1]) ldp.cantidad = cantidad self.rellenar_tablas()
def pedir_cantidad(self): """ Pide una cantidad que debe ser un número float. """ res = utils.dialogo_entrada(titulo = "CANTIDAD", texto = "Introduzca la cantidad a consumir del producto de compra (sin unidades):", padre = self.wids['ventana']) try: res = utils._float(res) except ValueError: utils.dialogo_info(titulo = "CANTIDAD INCORRECTA", texto = "El texto introducido %s no es correcto." % (res), padre = self.wids['ventana']) res = None return res
def editar_precio_srv(self, cell, path, texto): """ Cambia el precio del servicio. """ try: precio = utils._float(texto) except ValueError: utils.dialogo_info(titulo = "ERROR", texto = 'El texto "%s" no es un número.' % (texto), padre = self.wids['ventana']) else: model = self.wids['tv_servicios'].get_model() srv = pclases.Servicio.get(model[path][-1]) srv.precio = precio self.rellenar_tablas()
def cambiar_cantidad(self, cell, path, texto): """ Cambia la cantidad del descuento adicional por producto. """ try: cantidad = utils._float(texto) except ValueError: utils.dialogo_info(titulo = "FORMATO INCORRECTO", texto = "El texto %s no es válido." % (texto), padre = self.wids['ventana']) return model = self.wids['tv_consumos'].get_model() if model[path].parent == None: idconsumo = model[path][-1] consumo = pclases.ConsumoAdicional.get(idconsumo) consumo.cantidad = cantidad self.rellenar_consumos_adicionales_por_producto()
def cambiar_precio_defecto(self, cell, path, texto): # Chequeo permiso de escritura para camibar precios, existencias y demás: try: ventana = pclases.Ventana.select(pclases.Ventana.q.fichero == "productos.py")[0] if self.usuario and (not self.usuario.get_permiso(ventana).escritura or self.usuario.nivel >= 2): utils.dialogo_info(titulo = "USUARIO SIN PRIVILEGIOS", texto = "Necesita permiso de escritura para modificar el precio.", padre = self.wids['ventana']) return except IndexError: if self.usuario and self.usuario.nivel >= 2: utils.dialogo_info(titulo = "USUARIO SIN PRIVILEGIOS", texto = "No tiene permisos suficientes para modificar el precio.", padre = self.wids['ventana']) return model = self.wids['tv_datos'].get_model() id = model[path][-1] try: precio = utils._float(texto) except ValueError: utils.dialogo_info(titulo = "ERROR DE FORMATO", texto = "El texto %s no es correcto." % (texto), padre = self.wids['ventana']) else: if "PC" in model[path][0]: producto = pclases.ProductoCompra.get(id) elif "PV" in model[path][0]: producto = pclases.ProductoVenta.get(id) else: return for numcol in range(5, 5 + len(self.__tarifas)): tarifa = self.__tarifas[numcol - 5] porcentaje = tarifa.get_porcentaje(producto, True) precio_tarifa = precio * (1 + porcentaje) tarifa.asignarTarifa(producto, precio_tarifa) precio_tarifa *= 1.18 porcentaje *= 100.0 model[path][numcol] = "%s (%s %%)" % (utils.float2str(precio_tarifa), utils.float2str(porcentaje, 1)) producto.sync() producto.precioDefecto = precio producto.syncUpdate() model[path][4] = utils.float2str(producto.precioDefecto)
def convertir_a_flotante(valor, vdefecto = 0.0, col = None): """ Devuelve el valor por defecto "vdefecto" en caso de excepción. Antes de lanzarla, prueba como número flotante y como flotante con símbolo de porcentaje. Fuerza la comprobación de porcentaje si col es distinto de None y está entre las columnas a tratar así. """ from utils import _float, parse_porcentaje try: if col != None and col.name in NOMBRES_COLUMNAS_PORCENTAJE: raise Exception, "Forzando comprobación de porcentaje." return _float(valor) except: try: # 5 decimales de precisión es bastante para un porcentaje. Es lo # que uso también para mostrar en pantalla (ver el getter arriba). return round(parse_porcentaje(valor, fraccion = True), 5) except: return vdefecto
def crear_bala(self, boton = None, peso = None): """ Crea una bala del producto mostrado en pantalla e introduce su información en el TreeView. El peso lo solicita en una ventana de diálogo. Si se recibe peso, debe ser un float. """ # TODO: Hacer que el peso lo tome del puerto serie y se le pase a # esta función. producto = utils.combo_get_value(self.wids['cbe_producto']) if producto == None: utils.dialogo_info(titulo = "SELECCIONE UN PRODUCTO", texto = "Debe seleccionar un producto en el desplegable.", padre = self.wids['ventana']) else: if peso == None: peso = utils.dialogo_entrada(titulo = "PESO", texto = "Introduzca peso:", padre = self.wids['ventana'], valor_por_defecto = "0") try: peso = utils._float(peso) except ValueError: utils.dialogo_info(titulo = "ERROR", texto = "El valor tecleado %s no es correcto." % (peso), padre = self.wids['ventana']) peso = 0 nueva_bala = self.crear_objeto_bala(producto, peso) if nueva_bala == None: utils.dialogo_info(titulo = "ERROR", texto = "La bala no se pudo crear. Inténtelo de nuevo.", padre = self.wids['ventana']) else: self.add_nueva_bala_tv(nueva_bala) try: totpantalla = utils.parse_float( self.wids['e_pantalla'].get_text()) except: totpantalla = 0.0 totpantalla += peso self.rellenar_totales(totpantalla)
def cambiar_valor(self, cell, path, text): model = self.wids['tv_datos'].get_model() if model[path][0] == "": if model[path].parent != None: id = model[path].parent[0] clase = model[path].parent[-1] campo = model[path][1] objeto = eval("pclases.%s.get(%d)" % (clase, int(id))) objeto.syncUpdate() try: try: setattr(objeto, campo, text) except Exception, inner_e: try: setattr(objeto, campo, utils._float(text)) except Exception, float_e: try: setattr(objeto, campo, int(text)) except Exception, int_e: raise inner_e model[path][2] = getattr(objeto, campo)
def agregar_hijos(fila, numcols, numespacios, tv): """ Devuelve una lista con los hijos de "fila", y éstos a su vez con sus hijos, etc... en diferentes niveles. numespacios normalmente será el nivel de profundidad de la recursión * 2. """ iterator_hijos = fila.iterchildren() if iterator_hijos == None: return [] else: filas = [] for hijo in iterator_hijos: filahijo = [] for col in xrange(numcols): index_columna = tv.get_column(col).get_data("q_ncol") if index_columna is None: index_columna = col valor = hijo[index_columna] if isinstance(valor, bool): dato = valor and u"Sí".encode("iso-8859-15") or "No" else: try: valor = utils._float(valor) valor = str(valor).replace(".", ",") except ValueError: pass # No es flotante ni se puede convertir a él. finally: dato = ("%s" % valor).replace(";", ",").encode("iso-8859-15", "replace") # Por si acaso trae un entero, un float o algo asina. if index_columna == 0: filahijo.append("%s%s" % ("> " * numespacios, dato)) else: filahijo.append(dato) filas += [filahijo] + agregar_hijos(hijo, numcols, numespacios + 1, tv) return filas
def rellenar_subtabla(filas, w): """ Introduce las filas en el modelo del widget w y devuelve los totales de los campos numéricos en una lista (incluidos los IDs aunque no tenga sentido). """ totales = [] model = w.get_model() model.clear() for fila in filas: filaformateada = [] for valor in fila: try: strv = utils.float2str(valor) except: strv = valor filaformateada.append(strv) model.append(filaformateada) # Actualizo lista de totales con los valores numéricos únicamente offset = 0 for i in range(len(fila)): try: valor = utils._float(fila[i]) except ValueError: if fila[i] == "": # Es cadena vacía para el personal indirecto valor = 0 else: # Si no, definitivamente es un campo no numérico. offset -= 1 # Para saltar las primeras columnas de texto else: idest = i + offset try: totales[idest] += valor except IndexError: while len(totales) < idest: totales.append(0) totales.append(valor) return [totales]
def rellenar_tabla(self, facturas): """ Rellena el model con los items de la consulta """ model = self.wids['tv_datos'].get_model() model.clear() totfact = totsiniva = totbeneficio = 0.0 self.wids['tv_datos'].freeze_child_notify() self.wids['tv_datos'].set_model(None) nodos_formapago = {} for f in facturas: formapago = f.get_forma_de_pago() try: abuelo = nodos_formapago[formapago] except KeyError: abuelo = model.append(None, (formapago, "", "", "0.00", "0.00", "0.00", 0)) nodos_formapago[formapago] = abuelo total = f.calcular_total() iva = f.calcular_total_iva() irpf = f.calcular_total_irpf() siniva = total - iva - irpf beneficio = f.calcular_beneficio() totfact += total totsiniva += siniva totbeneficio += beneficio model[abuelo][3] = utils.float2str(utils._float(model[abuelo][3]) + total) model[abuelo][4] = utils.float2str(utils._float(model[abuelo][4]) + siniva) model[abuelo][5] = utils.float2str(utils._float(model[abuelo][5]) + beneficio) padre = model.append(abuelo, (utils.str_fecha(f.fecha), f.numfactura, f.cliente and f.cliente.nombre or "", utils.float2str(total), utils.float2str(siniva), utils.float2str(beneficio), f.id)) for srv in f.servicios: model.append(padre, (utils.float2str(srv.cantidad, autodec = True), utils.float2str(srv.precio), srv.concepto, utils.float2str(srv.calcular_subtotal() * (1 + f.iva)), utils.float2str(srv.calcular_subtotal()), utils.float2str(srv.calcular_beneficio()), srv.id)) for ldv in f.lineasDeVenta: model.append(padre, (utils.float2str(ldv.cantidad, autodec = True), utils.float2str(ldv.precio), ldv.producto.descripcion, utils.float2str(ldv.calcular_subtotal() * (1 + f.iva)), utils.float2str(ldv.calcular_subtotal()), utils.float2str(ldv.calcular_beneficio()), ldv.id)) self.rellenar_totales(totfact, totsiniva, totbeneficio) self.wids['tv_datos'].set_model(model) self.wids['tv_datos'].thaw_child_notify()
def rellenar_tabla_por_proveedor(self, resultados): """ Rellena el model con los items de la consulta """ model = self.wids['tv_datos'].get_model() model.clear() totfact = totsiniva = totbeneficio = totbeneficio_cobro = 0.0 self.wids['tv_datos'].freeze_child_notify() self.wids['tv_datos'].set_model(None) totcobrado = totpendiente = 0.0 total_costo = total_costo_cobrado = 0.0 padres = {} for material in resultados: for fecha in resultados[material]: for ldv in resultados[material][fecha]: # Para que coincidan los totales con la suma de las # columnas y por coherencia con todas las cifras de la # ventana, es necesario redondear a 2 decimales. subtotal = round(ldv.get_subtotal(iva = True), 2) subtotal_siva = round(ldv.get_subtotal(iva = False), 2) beneficio = round(ldv.calcular_beneficio(), 2) costo = round(ldv.calcular_precio_costo()*ldv.cantidad, 2) if ldv.facturaVenta: fac_alb_tic = ldv.facturaVenta.numfactura cobradofra = ldv.facturaVenta.calcular_cobrado() pendientefra = ldv.facturaVenta.calcular_pendiente_cobro() try: fraccion = cobradofra / (cobradofra + pendientefra) except ZeroDivisionError: fraccion = 1.0 cobrado = subtotal * fraccion pendiente = subtotal - cobrado beneficio_cobro = beneficio * fraccion costo_cobrado = costo * fraccion elif ldv.albaranSalida: fac_alb_tic = ldv.albaranSalida.numalbaran cobrado = 0.0 pendiente = subtotal beneficio_cobro = 0.0 costo_cobrado = 0.0 elif ldv.ticket: fac_alb_tic = "Ticket %d" % ldv.ticket.numticket cobrado = subtotal pendiente = 0.0 beneficio_cobro = beneficio costo_cobrado = costo # Los tickets se asume que se cobran siempre, por # tanto el costo de los productos sobre lo cobrado # es del 100%. else: fac_alb_tic = "" cobrado = pendiente = beneficio_cobro = 0.0 costo_cobrado = 0.0 desc_producto = utils.wrap(ldv.producto.descripcion, 40) try: beneficio_costo = 100.0 * beneficio / costo except ZeroDivisionError: beneficio_costo = 0.0 proveedor = ldv.get_proveedor() try: padre = padres[proveedor] except KeyError: padre = padres[proveedor] = model.append(None, (proveedor and proveedor.nombre or "Sin proveedor", "-", utils.float2str(0.0), utils.float2str(0.0), utils.float2str(0.0), proveedor and proveedor.get_puid() or "" )) model.append(padre, (desc_producto, fac_alb_tic, utils.float2str(subtotal), utils.float2str(subtotal_siva), "%s (%s%%)" % ( utils.float2str(beneficio), utils.float2str( beneficio_costo)), ldv.get_puid() )) # Actualizo totales en memoria y en nodos padre TreeView totfact += subtotal totsiniva += subtotal_siva totbeneficio += beneficio totbeneficio_cobro += beneficio_cobro totcobrado += cobrado totpendiente += pendiente total_costo += costo total_costo_cobrado += costo_cobrado model[padre][2] = utils.float2str( utils._float(model[padre][2]) + subtotal) model[padre][3] = utils.float2str( utils._float(model[padre][3]) + subtotal_siva) model[padre][4] = utils.float2str( utils._float(model[padre][4]) + beneficio) self.rellenar_totales(totfact, totsiniva, totbeneficio, totcobrado, totpendiente, totbeneficio_cobro, total_costo, total_costo_cobrado) self.wids['tv_datos'].set_model(model) self.wids['tv_datos'].thaw_child_notify()
def cambiar_importe_libre(self, cell, path, texto): """ Guarda el plus de no absentismo del empleado en un registro de control de horas dentro del rango de fechas de la consulta. La fecha elegida para guardar el plus es la fecha intermedia entre los dos extremos, así aparecerá el plus de no absentismo en el mes que sea aunque las fechas inicial y final varíen ligeramente. El truco para no tener que buscar el registro elegido en dos consultas ligeramente distintas y así que al cambiar un 100 por 120 no se convierta mágicamente en 220 (100 en un registro y 120 en otro) es guardar siempre la diferencia entre el texto escrito y el nuevo. PLAN: Se podría tratar de usar el campo de nóminas dedicado a este plus, pero se intenta mantener todo lo independiente posible esas tablas, ya que probablemente la ventana de nóminas será DEPRECATED para la siguiente versión. De cualquier forma, siempre se podría tratar de actualizar los valores de la nómina desde esta ventana. También hay que tener en cuenta que las nóminas son para meses completos, mientras que esta consulta está enfocada a granularidad que va desde un solo día a años enteros. """ try: pabs = utils._float(texto) except: utils.dialogo_info(titulo = "ERROR DE FORMATO", texto = "El texto tecleado %s no es un número." % texto, padre = self.wids['ventana']) return model = self.wids['tv_plus'].get_model() id = model[path][-1] if not isinstance(id, int): try: id = int(utils._float(id)) except: txt = ("Identificador %s no se pudo convertir a entero. An" + "ulo edición de importe libre" % id) self.logger.error("%consulta_mensual_nominas.py::cambiar_importe_libre -> %s" % (self.usuario and self.usuario.usuario + ": " or "", txt)) return empleado = pclases.Empleado.get(id) f1 = self.inicio f2 = self.fin fecha = mx.DateTime.DateTimeFromTicks((f1.ticks() + f2.ticks()) / 2) # Por si acaso, aseguro día, mes y año redondos, sin hora. fecha = mx.DateTime.DateTimeFrom(day = fecha.day, month = fecha.month, year = fecha.year) valor_anterior = utils._float(model[path][4]) a_guardar = pabs - valor_anterior try: ch = pclases.ControlHoras.select(pclases.AND( pclases.ControlHoras.q.empleadoID == empleado.id, pclases.ControlHoras.q.fecha == fecha))[0] except IndexError: # Es posible que no haya registro en esa fecha. ch = pclases.ControlHoras(fecha = fecha, empleado = empleado, grupo = None, horasRegulares = 0.0, nocturnidad = False, horasExtraDiaProduccion = 0.0, horasExtraDiaMantenimiento = 0.0, horasExtraDiaAlmacen = 0.0, horasExtraDiaVarios = 0.0, horasExtraNocheProduccion = 0.0, horasExtraNocheMantenimiento = 0.0, horasExtraNocheAlmacen = 0.0, horasExtraNocheVarios = 0.0, horasAlmacen = 0.0, horasVarios = 0.0, varios = '', comentarios = '', bajaLaboral = False, vacacionesYAsuntosPropios = False, festivo = False, plusAbsentismo = 0.0, conceptoLibre = "", importeLibre = 0.0) ch.importeLibre += a_guardar # Por si era el mismo, sumo. ch.syncUpdate() model[path][4] = utils.float2str( empleado.calcular_importe_libre(self.inicio, self.fin)) total = sum([utils._float(model[p][4]) for p in range(len(model))]) self.wids['tv_totales_plus'].get_model()[0][2] = utils.float2str(total)
def buscar(self, boton): # DONE: Faltan los abonos por descontar. fechaini = self.wids["e_fechaini"].get_text().strip() if fechaini: try: fechaini = utils.parse_fecha(fechaini) except (ValueError, TypeError): utils.dialogo_info( titulo="ERROR EN FECHA INICIAL", texto="El texto «%s» no es una fecha correcta." % fechaini, padre=self.wids["ventana"], ) fechaini = None fechafin = self.wids["e_fechafin"].get_text().strip() if fechafin: try: fechafin = utils.parse_fecha(fechafin) except (ValueError, TypeError): utils.dialogo_info( titulo="ERROR EN FECHA FINAL", texto="El texto «%s» no es una fecha correcta." % fechafin, padre=self.wids["ventana"], ) fechafin = None if fechaini and fechafin and fechafin < fechaini: fechaini, fechafin = fechafin, fechaini self.wids["e_fechaini"].set_text(utils.str_fecha(fechaini)) self.wids["e_fechafin"].set_text(utils.str_fecha(fechafin)) if fechafin: FV = pclases.FacturaVenta VC = pclases.VencimientoCobro # Para asegurarme de # que tiene vencimientos. FDA = pclases.FacturaDeAbono C = pclases.Cobro T = pclases.Tarea if fechaini: facturas = FV.select( pclases.AND(FV.q.fecha >= fechaini, FV.q.fecha <= fechafin, VC.q.facturaVentaID == FV.q.id) ) # Busco los abonos (facturas de abono, en realidad, que no # tienen por qué tener la misma fecha) que no hayan sido # incluidos en facturas (porque si no el importe ya se habría # contado en la factura anterior) ni en pagarés (porque # entonces ya estarían en un documento de pago y por tanto # no deberían aparecer en esta consulta) # ... # He decidido que no voy a sacar los abonos. No van a tener # ventana de seguimiento y no se le pueden relacionar tareas. # Además, esta ventana era para reclamar pagos, no para pagar. # abonos = FDA.select(pclases.AND( # FDA.q.fecha >= fechaini, # FDA.q.fecha <= fechafin)) abonos = [] else: facturas = FV.select(pclases.AND(FV.q.fecha <= fechafin, VC.q.facturaVentaID == FV.q.id)) # abonos = FDA.select(FDA.q.fecha <= fechafin) abonos = [] # No me queda otra que filtrar así aunque sea lento: abonos_pendientes = [] for a in abonos: if not a.abono: continue # ¿Error de borrado de un abono? Mmm... mal rollo. if a.abono.facturasVenta: continue if a.cobros: # Cada cobro de abono está relacionado # con un pagaré (o con lo que sea en un # posible futuro, el caso es que no # estaría pendiente). continue abonos_pendientes.append(a) from ventana_progreso import VentanaProgreso vpro = VentanaProgreso(padre=self.wids["ventana"]) vpro.mostrar() txtvpro = "Buscando facturas sin documento de pago..." nodos_clientes = {} total = 0.0 i = 0.0 vpro.set_valor(i, txtvpro) model = self.wids["tv_datos"].get_model() model.clear() facturas_added = [] # Aprovecho para chequear alarmas automáticas. pclases.Alarma.crear_alarmas_automaticas(facturas) for f in facturas: # Aquí voy a hacer un segundo filtro usando la cantidad # pendiente de cobro de cada factura. # pendiente = f.calcular_pendiente_cobro() pendiente = f.calcular_pendiente_de_documento_de_pago() pendiente = round(pendiente, 2) if pendiente and f not in facturas_added: total += pendiente cliente = f.cliente if cliente not in nodos_clientes: tmp_nodo = model.append(None, (cliente.nombre, "", "", "", "0", "", cliente.id)) nodos_clientes[cliente] = {"nodo_cliente": tmp_nodo} obra = f.obra nodo_cliente_padre = nodos_clientes[cliente]["nodo_cliente"] if obra not in nodos_clientes[cliente]: nodos_clientes[cliente][obra] = model.append( nodo_cliente_padre, (obra and obra.nombre or "Sin obra", "", "", "", "0", "", obra and obra.id or 0), ) fechas_vto = f.vencimientosCobro[:] fechas_vto.sort(lambda v1, v2: (v1.fecha < v2.fecha and -1) or (v1.fecha > v2.fecha and 1) or 0) fechas_vto = [utils.str_fecha(v.fecha) for v in f.vencimientosCobro] vtos = "; ".join(fechas_vto) nodo_padre = nodos_clientes[cliente][obra] last_evento = f.get_last_evento() if last_evento: last_evento = "[%s] %s" % (utils.str_fechahora(last_evento.fechahora), last_evento.texto) else: last_evento = "" model.append( nodo_padre, ( "", # (f.cliente.nombre, f.numfactura, utils.str_fecha(f.fecha), vtos, utils.float2str(pendiente), last_evento, f.id, ), ) model[nodo_padre][4] = utils.float2str(utils._float(model[nodo_padre][4]) + pendiente) model[nodo_cliente_padre][4] = utils.float2str( utils._float(model[nodo_cliente_padre][4]) + pendiente ) facturas_added.append(f) i += 1 vpro.set_valor(i / (facturas.count() + len(abonos_pendientes)), txtvpro) for a in abonos_pendientes: pendiente = a.calcular_importe_total() # O está descontada # entera o no lo está. Con los abonos no hay pagos parciales. pendiente = round(pendiente, 2) if pendiente: total += pendiente vtos = utils.str_fecha(a.fecha) # Tampoco tiene # vencimientos. La obligación nace desde el mismo día # en que el abono se convierte en factura de abono. try: cliente = a.cliente except AttributeError: txt = ( "crm_seguimiento_impagos.py::buscar" " -> FacturaDeAbono %d sin Abono o Cliente." " Ignorando. " % a.id ) self.logger.error(txt) continue if cliente not in nodos_clientes: tmp_nodo = model.append(None, (cliente.nombre, "", "", "", "0", "", cliente.id)) nodos_clientes[cliente] = {"nodo_cliente": tmp_nodo} obra = a.obra nodo_cliente_padre = nodos_clientes[cliente]["nodo_cliente"] if obra not in nodos_clientes[cliente]: nodos_clientes[cliente][obra] = model.append( nodo_cliente_padre, (obra and obra.nombre or "Sin obra", "", "", "", "0", "", obra and obra.id or 0), ) nodo_padre = nodos_clientes[cliente][obra] model.append( nodo_padre, # (a.cliente.nombre, ( "", a.numfactura, utils.str_fecha(a.fecha), vtos, utils.float2str(pendiente), "", # Facturas de abono no tienen anotaciones. -a.id, ), ) # Para distinguirlo de las facturas. model[nodo_padre][4] = utils.float2str(utils._float(model[nodo_padre][4]) + pendiente) model[nodo_cliente_padre][4] = utils.float2str( utils._float(model[nodo_cliente_padre][4]) + pendiente ) i += 1 vpro.set_valor(i / (facturas.count() + len(abonos_pendientes)), txtvpro) vpro.ocultar() self.wids["e_total"].set_text(utils.float2str(total)) self.buscar_todos() self.buscar_anotaciones() self.buscar_alertas()
def rellenar_tabla(self, items): """ Rellena el model con los items de la consulta """ model = self.wids['tv_datos'].get_model() model.clear() total = totben = totpdte = 0.0 clientes = {} for f in items: cliente = f.cliente if cliente not in clientes: clientes[cliente] = model.append(None, (cliente and cliente.nombre or "Sin cliente", "", "", "0.0", "0.0", "0.0", cliente and cliente.id or 0)) nodo_padre = clientes[cliente] totalpfra = f.importeTotal beneficio = f.calcular_beneficio() pdte = f.calcular_pendiente_cobro() model.append(nodo_padre, ("", f.numfactura, utils.str_fecha(f.fecha), utils.float2str(totalpfra), utils.float2str(beneficio), utils.float2str(pdte), f.id)) model[nodo_padre][3] = utils.float2str( utils._float(model[nodo_padre][3]) + totalpfra) model[nodo_padre][4] = utils.float2str( utils._float(model[nodo_padre][4]) + beneficio) model[nodo_padre][5] = utils.float2str( utils._float(model[nodo_padre][5]) + pdte) total += totalpfra totben += beneficio totpdte += pdte self.wids['e_total'].set_text("%s" % (utils.float2str(total))) self.wids['e_total_kilos'].set_text("%s" % (utils.float2str(totben))) self.wids['e_totpdte'].set_text("%s" % (utils.float2str(totpdte))) # Y ahora la gráfica. datachart = [["Total", total], ["Beneficio", totben], ["Pendiente", totpdte]] try: import gtk, gobject, cairo, copy, math except ImportError: return # No se pueden dibujar gráficas. # TODO: Temporal. try: import charting except ImportError: import sys, os sys.path.append(os.path.join("..", "utils")) import charting try: oldchart = self.wids['eventbox_chart'].get_child() if oldchart != None: #self.wids['eventbox_chart'].remove(oldchart) chart = oldchart else: chart = charting.Chart(orient = "horizontal") self.wids['eventbox_chart'].add(chart) chart.plot(datachart) self.wids['eventbox_chart'].show_all() except Exception, msg: txt = "consulta_ventas_prefacturas.py::rellenar_tabla -> "\ "Error al dibujar gráfica (charting): %s" % msg print txt self.logger.error(txt)
def rellenar_tabla(self, resultados): """ Rellena el model con los items de la consulta """ model = self.wids['tv_datos'].get_model() model.clear() totfact = totsiniva = totbeneficio = totbeneficio_cobro = 0.0 self.wids['tv_datos'].freeze_child_notify() self.wids['tv_datos'].set_model(None) totcobrado = totpendiente = 0.0 total_costo = total_costo_cobrado = 0.0 for material in resultados: if material != None: nombre_mat = material.descripcion else: nombre_mat = "" padre_mat = model.append(None, (nombre_mat, "", "0", "0", "0", "M:%d" % (material and material.id or -1))) for fecha in resultados[material]: if fecha: str_fecha = utils.str_fecha(fecha) else: str_fecha = "" padre_fec = model.append(padre_mat, (str_fecha, "", "0", "0", "0", "")) for ldv in resultados[material][fecha]: subtotal = ldv.get_subtotal(iva = True) subtotal_siva = ldv.get_subtotal(iva = False) beneficio = ldv.calcular_beneficio() costo = ldv.calcular_precio_costo() * ldv.cantidad if ldv.facturaVenta: fac_alb_tic = ldv.facturaVenta.numfactura cobradofra = ldv.facturaVenta.calcular_cobrado() pendientefra = ldv.facturaVenta.calcular_pendiente_cobro() try: fraccion = cobradofra / (cobradofra + pendientefra) except ZeroDivisionError: fraccion = 1.0 cobrado = subtotal * fraccion pendiente = subtotal - cobrado beneficio_cobro = beneficio * fraccion costo_cobrado = costo * fraccion elif ldv.albaranSalida: fac_alb_tic = ldv.albaranSalida.numalbaran cobrado = 0.0 pendiente = subtotal beneficio_cobro = 0.0 costo_cobrado = 0.0 elif ldv.ticket: fac_alb_tic = "Ticket %d" % ldv.ticket.numticket cobrado = subtotal pendiente = 0.0 beneficio_cobro = beneficio costo_cobrado = costo # Los tickets se asume que se cobran siempre, por # tanto el costo de los productos sobre lo cobrado # es del 100%. else: fac_alb_tic = "" cobrado = pendiente = beneficio_cobro = 0.0 costo_cobrado = 0.0 desc_producto = utils.wrap(ldv.producto.descripcion, 40) try: beneficio_costo = 100.0 * beneficio / costo except ZeroDivisionError: beneficio_costo = 0.0 model.append(padre_fec, (desc_producto, fac_alb_tic, utils.float2str(subtotal), utils.float2str(subtotal_siva), "%s (%s%%)" % ( utils.float2str(beneficio), utils.float2str( beneficio_costo)), "LDV:%d" % ldv.id)) # Actualizo totales en memoria y en nodos padre TreeView totfact += subtotal totsiniva += subtotal_siva totbeneficio += beneficio totbeneficio_cobro += beneficio_cobro totcobrado += cobrado totpendiente += pendiente total_costo += costo total_costo_cobrado += costo_cobrado model[padre_fec][2] = utils.float2str( utils._float(model[padre_fec][2]) + subtotal) model[padre_fec][3] = utils.float2str( utils._float(model[padre_fec][3]) + subtotal_siva) model[padre_fec][4] = utils.float2str( utils._float(model[padre_fec][4]) + beneficio) model[padre_mat][2] = utils.float2str( utils._float(model[padre_mat][2]) + subtotal) model[padre_mat][3] = utils.float2str( utils._float(model[padre_mat][3]) + subtotal_siva) model[padre_mat][4] = utils.float2str( utils._float(model[padre_mat][4]) + beneficio) self.rellenar_totales(totfact, totsiniva, totbeneficio, totcobrado, totpendiente, totbeneficio_cobro, total_costo, total_costo_cobrado) self.wids['tv_datos'].set_model(model) self.wids['tv_datos'].thaw_child_notify()
def guardar(self, widget): """ Guarda el contenido de los entry y demás widgets de entrada de datos en el objeto y lo sincroniza con la BD. """ producto = self.objeto codigo = self.wids['e_codigo'].get_text() if len(codigo) < 12: utils.dialogo_info(titulo = "CÓDIGO DEMASIADO CORTO", texto = "El código debe tener 13 dígitos para ajustarse al formato EAN-13.\nSe va a completar con ceros.", padre = self.wids['ventana']) codigo += "0" * (12 - len(codigo)) # Completo hasta 12 para que después se le haga la suma de control. if len(codigo) == 12: codigo = codigo + self.calcular_digitoc(codigo) if len(codigo) > 13: utils.dialogo_info(titulo = "CÓDIGO INCORRECTO", texto = "Código EAN-13 incorrecto. Debe tener exactamente 13 dígitos.\nPuede introducir 12 y dejar que se calcule atomáticamente el dígito de control, si le es más cómodo.\n\nA continuación se va a recortar el código a 13 dígitos y se intentará comprobar la suma de control.", padre = self.wids['ventana']) codigo = codigo[:13] if len(codigo) == 13: digitocontrol = self.calcular_digitoc(codigo[:12]) if codigo[12] != digitocontrol: utils.dialogo_info(titulo = "FALLÓ SUMA DE COMPROBACIÓN", texto = "El dígito de control no es correcto. Se corregirá automáticamente.", padre = self.wids['ventana']) codigo = codigo[:12] + digitocontrol descripcion = self.wids['e_descripcion'].get_text() # idlinea Un producto pertenece a una línea, no se puede cambiar nombre = self.wids['e_nombre'].get_text() arancel = self.wids['e_arancel'].get_text() try: precio = float(self.wids['e_precio'].get_text()) except ValueError: precio = producto.preciopordefecto try: minimo = float(self.wids['e_minimo'].get_text()) except ValueError: minimo = producto.minimo try: stock = utils._float(self.wids['e_stock'].get_text()) except ValueError: stock = producto.camposEspecificosEspecial.stock try: existencias = int(self.wids['e_existencias'].get_text()) except: existencias = producto.camposEspecificosEspecial.existencias # TODO: Falta ajustar los almacenes a la nueva cantidad en existencias. unidad = self.wids['e_unidad'].get_text() observaciones = self.wids['e_observaciones'].get_text() # Desactivo el notificador momentáneamente producto.notificador.activar(lambda: None) producto.camposEspecificosEspecial.notificador.activar(lambda: None) # Actualizo los datos del objeto try: producto.codigo = codigo except pclases.psycopg_ProgrammingError: try: producto_asignado = pclases.ProductoVenta.select(pclases.ProductoVenta.q.codigo == codigo)[0] producto_asignado = producto_asignado.descripcion except IndexError: producto_asignado = "?" utils.dialogo_info(titulo = "CÓDIGO DUPLICADO", texto = "El código EAN %s no se encuentra disponible.\nActualmente está asignado a: %s" % (codigo, producto_asignado), padre = self.wids['ventana']) producto.descripcion = descripcion producto.nombre = nombre producto.preciopordefecto = precio producto.minimo = minimo producto.arancel = arancel producto.camposEspecificosEspecial.stock = stock producto.camposEspecificosEspecial.unidad = unidad producto.camposEspecificosEspecial.observaciones = observaciones producto.camposEspecificosEspecial.existencias = existencias # Fuerzo la actualización de la BD y no espero a que SQLObject lo haga por mí: producto.syncUpdate() # Vuelvo a activar el notificador producto.notificador.activar(self.aviso_actualizacion) producto.camposEspecificosEspecial.notificador.activar(self.aviso_actualizacion) self.actualizar_ventana() self.wids['b_guardar'].set_sensitive(False)