def buscar_cod_libre(self): """ Devuelve un código de tres cifras como cadena que no esté ya en la tabla de productos como producto terminado. Siempre devolverá el primer código libre más cercano a 000. Si no quedan más mostrará un mensaje de error por pantalla y devolverá 999. """ prods = pclases.ProductoVenta.select(pclases.AND( pclases.OR( pclases.ProductoVenta.q.camposEspecificosBalaID !=None, pclases.ProductoVenta.q.camposEspecificosRolloID !=None, pclases.ProductoVenta.q.camposEspecificosEspecialID !=None), pclases.ProductoVenta.q.codigo.startswith('843603219'))) # Incluyo todo tipo de productos porque aunque la fibra esté limitada # al rango 300~400, el resto no, y al crearlos ha podido pillar uno # de esos números. codsproducto = [int(p.codigo[-4:-1]) for p in prods] codsproducto.sort() for i in xrange(300, 400): # OJO: Códigos hardcoded. Para balas se # han asignado los códigos 300 a 399. try: if not i in codsproducto: return "%03d" % i except IndexError: # No hay o me pasé de rango return "%03d" % i utils.dialogo_info('NO QUEDAN CÓDIGOS DISPONIBLES', 'Todos los códigos EAN13 fueron asignados.', padre = self.wids['ventana']) return '999'
def pedir_producto_compra(self): """ Devuelve UN producto de compra obtenido a partir de una búsqueda, etc. """ producto = None a_buscar = utils.dialogo_entrada(titulo = "BUSCAR MATERIAL", texto = "Introduzca texto a buscar en productos de compra:", padre = self.wids['ventana']) if a_buscar != None: resultados = utils.buscar_productos_compra(a_buscar) if resultados.count() > 1: ## Refinar los resultados: filas_res = [] for r in resultados: filas_res.append((r.id, r.codigo, r.descripcion)) idproducto = utils.dialogo_resultado(filas_res, titulo = 'Seleccione producto', cabeceras = ('ID Interno', 'Código', 'Descripción'), padre = self.wids['ventana']) if idproducto < 0: return producto = pclases.ProductoCompra.get(idproducto) # id es clave primaria, esta comprensión debería devolver un único producto elif resultados.count() < 1: ## La búsqueda no produjo resultados. utils.dialogo_info('SIN RESULTADOS', 'La búsqueda no produjo ningún resultado.\nIntente una ' 'búsqueda menos restrictiva usando un texto más corto.', padre = self.wids['ventana']) return None else: producto = resultados[0] return producto
def drop_consumo_por_producto(self, boton): """ Elimina el consumo o consumos seleccionados en el TreeView. """ texto = """ Si ha seleccionado un consumo se eliminará el consumo completo. Si seleccionó uno o varios productos, se eliminarán del consumo al que pertenece, por lo que ya no empleará el material relacionado cuando se fabriquen artículos del mismo. ¿Está seguro de querer continuar? """ model, paths = self.wids['tv_consumos'].get_selection().get_selected_rows() if paths and utils.dialogo(titulo = "¿ELIMINAR?", texto = texto, padre = self.wids['ventana']): for path in paths: if model[path].parent == None: id_consumo = model[path][-1] consumo_adicional_por_producto = pclases.ConsumoAdicional.get(id_consumo) try: for p in consumo_adicional_por_producto.productosVenta: consumo_adicional_por_producto.removeProductoVenta(p) consumo_adicional_por_producto.destroySelf() except pclases.psycopg_ProgrammingError, msg: utils.dialogo_info(titulo = "ERROR: INFORME A LOS DESARROLLADORES", texto = "Ocurrió un error al eliminar el consumo.\nDEBUG: Traza de la excepción:\n%s" % (msg), padre = self.wids['ventana']) else: id_consumo = model[path].parent[-1] idproductov = model[path][-1] consumo_adicional_por_producto = pclases.ConsumoAdicional.get(id_consumo) productov = pclases.ProductoVenta.get(idproductov) consumo_adicional_por_producto.removeProductoVenta(productov) self.rellenar_consumos_adicionales_por_producto()
def buscar_pedido(self, numero): """ Muestra una ventana de búsqueda y a continuación los resultados. El objeto seleccionado se hará activo en la ventana a no ser que se pulse en Cancelar en la ventana de resultados. """ if numero != None: resultados = pclases.PedidoVenta.select(pclases.PedidoVenta.q.numpedido.contains(numero)) if resultados.count() > 1: ## Refinar los resultados idpedido = self.refinar_resultados_busqueda(resultados) if idpedido == None: return resultados = [pclases.PedidoVenta.get(idpedido)] # Se supone que la comprensión de listas es más rápida que hacer un nuevo get a SQLObject. # Me quedo con una lista de resultados de un único objeto ocupando la primera posición. # (Más abajo será cuando se cambie realmente el objeto actual por este resultado.) elif resultados.count() < 1: ## Sin resultados de búsqueda utils.dialogo_info('SIN RESULTADOS', 'La búsqueda no produjo resultados.\nPruebe a cambiar el texto buscado o déjelo en blanco para ver una lista completa.\n(Atención: Ver la lista completa puede resultar lento si el número de elementos es muy alto)', padre = self.wids['ventana']) return ## Un único resultado return resultados[0] else: return None
def pedir_producto(self): """ Solicita un código de producto, muestra una ventana de resultados coincidentes con la búsqueda de ese código y devuelve un objeto producto seleccionado de entre los resultados o None si se cancela o no se encuentra. """ producto = None codigo = utils.dialogo_entrada(texto = 'Introduzca código o descripción del producto.', titulo = 'PRODUCTO', padre = self.wids['ventana']) if codigo != None: prods = pclases.ProductoCompra.select(pclases.AND(pclases.OR(pclases.ProductoCompra.q.descripcion.contains(codigo), #@UndefinedVariable pclases.ProductoCompra.q.codigo.contains(codigo)), #@UndefinedVariable pclases.ProductoCompra.q.controlExistencias == True)) #@UndefinedVariable prods = tuple(prods) mens_error = 'No se encontró ningún producto con ese código o descripción.' if len(prods) > 1: idproducto = self.refinar_busqueda_productos(prods) if idproducto != None: prods = [pclases.ProductoCompra.get(idproducto)] else: return None elif len(prods) < 1: utils.dialogo_info(titulo = 'CÓDIGO NO ENCONTRADO', texto = mens_error, padre = self.wids['ventana']) return None producto = prods[0] return producto
def buscar_usuario(self, widget): """ Muestra una ventana de búsqueda y a continuación los resultados. El objeto seleccionado se hará activo en la ventana a no ser que se pulse en Cancelar en la ventana de resultados. """ usuario = self.objeto a_buscar = utils.dialogo_entrada("Introduzca nombre de usuario o nombre real:") if a_buscar != None: resultados = pclases.Usuario.select(sqlobject.OR(pclases.Usuario.q.usuario.contains(a_buscar), pclases.Usuario.q.nombre.contains(a_buscar))) if resultados.count() > 1: ## Refinar los resultados idusuario = self.refinar_resultados_busqueda(resultados) if idusuario == None: return resultados = [pclases.Uduario.get(idusuario)] elif resultados.count() < 1: ## Sin resultados de búsqueda utils.dialogo_info('SIN RESULTADOS', 'La búsqueda no produjo resultados.\nPruebe a cambiar el texto buscado o déjelo en blanco para ver una lista completa.\n(Atención: Ver la lista completa puede resultar lento si el número de elementos es muy alto)') return ## Un único resultado # Primero anulo la función de actualización if usuario != None: usuario.notificador.desactivar() # Pongo el objeto como actual usuario = resultados[0] # Y activo la función de notificación: usuario.notificador.activar(self.aviso_actualizacion) self.objeto = usuario self.actualizar_ventana()
def nuevo(self, widget): """ Función callback del botón b_nuevo. Pide los datos básicos para crear un nuevo objeto. Una vez insertado en la BD hay que hacerlo activo en la ventana para que puedan ser editados el resto de campos que no se hayan pedido aquí. """ silo = self.objeto nombre = utils.dialogo_entrada('Introduzca el nombre del silo:', 'NOMBRE', padre = self.wids['ventana']) if nombre != None: capacidad = utils.dialogo_entrada(titulo = "CAPACIDAD", texto = "Introduzca la capacidad del silo:", padre = self.wids['ventana']) try: capacidad = float(capacidad) except: utils.dialogo_info(titulo = "VALOR INCORRECTO", texto = "El valor %s no es correcto. Inténtelo de nuevo.\nAsegúrese de no introducir unidades.", padre = self.wids['ventana']) return observaciones = utils.dialogo_entrada(titulo = "OBSERVACIONES", texto = "Introduzca observaciones si lo desea:", padre = self.wids['ventana']) if observaciones != None: silo = pclases.Silo(nombre = nombre, capacidad = capacidad, observaciones = observaciones) self.rellenar_widgets()
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. """ albaran = self.objeto # Campos del objeto que hay que guardar: numalbaran = self.wids['e_numalbaran'].get_text() fecha = self.wids['e_fecha'].get_text() # Desactivo el notificador momentáneamente albaran.notificador.set_func(lambda: None) # Actualizo los datos del objeto albaran.numalbaran = numalbaran albaran.proveedor = utils.combo_get_value(self.wids['cmbe_proveedor']) albaran.almacen = utils.combo_get_value(self.wids['cbe_almacenID']) try: albaran.fecha = utils.parse_fecha(fecha) except: albaran.fecha = time.localtime() utils.dialogo_info(titulo = "ERROR GUARDANDO FECHA", texto = "La fecha %s no es correcta." % (fecha), padre = self.wids['ventana']) # Fuerzo la actualización de la BD y no espero a que SQLObject lo haga por mí: albaran.syncUpdate() # Vuelvo a activar el notificador albaran.notificador.set_func(self.aviso_actualizacion) self.actualizar_ventana() self.wids['b_guardar'].set_sensitive(False)
def salir(self, boton, event=None, mostrar_ventana=False): """ Muestra una ventana de confirmación y sale de la ventana cerrando el bucle local de gtk_main. Si mostrar_ventana es False, sale directamente sin preguntar al usuario. """ try: b_guardar = self.wids['b_guardar'] except KeyError: b_guardar = None if b_guardar != None and b_guardar.get_property('sensitive'): # Hay cambios pendientes de guardar. if utils.dialogo('Hay cambios pendientes de guardar.\n¿Desea hace' 'rlo ahora?', '¿GUARDAR CAMBIOS?', padre = self.wids['ventana'], icono = gtk.STOCK_SAVE, #defecto = "Sí"): # CWT: A veces en BlancoPe # abren la ventana de productos para buscar # un precio y le dan al botón antes de que se # carguen los datos, provocando que se detecte # como un falso cambio de datos de la ventana # (todo blanco) respecto a la caché (datos # reales de la BD). Al darle a ENTER, # responden Sí por defecto -hasta ahora- y # sobreescriben con blanco todos los datos del # registro en la base de datos. defecto = "No"): try: self.guardar(None) except: utils.dialogo_info(titulo = 'NO SE PUDO GUARDAR', texto = 'Los cambios no se pudieron gua' 'rdar automáticamente.\nDebe ha' 'cerlo de forma manual', padre = self.wids['ventana']) return True # Si devuelvo False, None, etc... continúa la # cadena de eventos y destruye la ventana. # Devuelvo True para cancelar el cierre de la # ventana. if event == None: # Me ha invocado el botón if not mostrar_ventana or \ utils.dialogo('¿Desea salir de la ventana actual?', 'SALIR', padre = self.wids['ventana'], icono = gtk.STOCK_QUIT): self.wids['ventana'].destroy() return False else: return True else: if not mostrar_ventana or not \ utils.dialogo('¿Desea salir de la ventana actual?', 'SALIR', padre=self.wids['ventana'], icono=gtk.STOCK_QUIT): return False else: return True
def pedir_transportista(self, widget): """ Solicita un número de pedido, muestra una ventana de resultados coincidentes con la búsqueda de ese número y devuelve un objeto pedido seleccionado de entre los resultados o None si se cancela o no se encuentra. """ global transportista codigo = utils.dialogo_entrada(texto = 'Introduzca nombre del transportista', titulo = 'TRANSPORTISTA', padre = self.wids['ventana']) if codigo != None: trans = pclases.Transportista.select(pclases.Transportista.q.nombre.contains(codigo)) trans = [p for p in trans] mens_error = 'No se encontró ningún transportista con ese nombre.' if len(trans) > 1: idtrans = refinar_busqueda_transportista(trans) if idtrans != None: trans = [p for p in trans if p.id == idtrans] else: return None elif len(trans) < 1: utils.dialogo_info('TRANSPORTISTA NO ENCONTRADO', mens_error, padre = self.wids['ventana']) return None transportista = trans[0]
def cambiar_precio(self, cell, path, texto): try: precio = float(texto) except: utils.dialogo_info(titulo = "ERROR FORMATO", texto = "El texto %s no es un número." % texto, padre = self.wids['ventana']) else: model = self.wids['tv_ldvs'].get_model() ldc = pclases.LineaDeCompra.get(model[path][-1]) ldc.precio = precio # CWT: Si el precio cambia al alza, respetar porcentaje. Si meto # un precio inferior, entonces respetar el precio final de las # tarifas y adaptar el porcentaje a ese precio de forma que nunca # venda más barato al cambiar el precio del proveedor. pc = ldc.productoCompra pc.sync() if precio > pc.precioDefecto: # Si el precio sube, actualizo tarifas respetando porcentajes. for t in pclases.Tarifa.select(orderBy = "nombre"): if t.vigente: porcentaje = t.get_porcentaje(pc, fraccion = True) nuevo_precio = precio * (1 + porcentaje) t.asignarTarifa(pc, nuevo_precio) # Si el precio ha bajado o se mantiene, respeto precio final. La # ventana ya calculará el porcentaje que sea. pc.precioDefecto = precio pc.syncUpdate() self.actualizar_ventana()
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 borrar(self, widget): """ Elimina la cuenta de la tabla pero NO intenta eliminar ninguna de sus relaciones, de forma que si se incumple alguna restricción de la BD, cancelará la eliminación y avisará al usuario. """ if not utils.dialogo('¿Eliminar %s?'%self.clase.sqlmeta.table.lower(), 'BORRAR', padre = self.wids['ventana']): return self.objeto.notificador.desactivar() try: self.objeto.destroySelf() except Exception, e: self.logger.error("grupos_alumnos.py::borrar -> %s ID %d no se " "pudo eliminar. Excepción: %s." % ( self.objeto.sqlmeta.table, self.objeto.id, e)) utils.dialogo_info( titulo = "%s NO BORRADO" % self.clase.sqlmeta.table.upper(), texto = "%s no se pudo eliminar.\n\nSe generó un informe de " "error en el «log» de la aplicación." % ( self.clase.sqlmeta.table.title()), padre = self.wids['ventana']) self.actualizar_ventana() return
def borrar_producto(self, widget): """ Elimina el producto de la tabla pero NO intenta eliminar ninguna de sus relaciones, de forma que si se incumple alguna restricción de la BD, cancelará la eliminación y avisará al usuario. """ producto = self.objeto if not utils.dialogo('¿Eliminar el producto?', 'BORRAR'): return if producto.articulos != [] or producto.precios != []: utils.dialogo_info('PRODUCTO NO ELIMINADO', 'El producto está implicado en operaciones que impiden su borrado.', padre = self.wids['ventana']) else: producto.notificador.desactivar() campos = producto.camposEspecificosBala try: producto.destroySelf() campos.destroySelf() except: utils.dialogo_info(titulo = "NO SE PUEDE ELIMINAR", texto = "El producto no se puede eliminar. Verifique que no\ntiene ventas o producción relacionada.", padre = self.wids['ventana']) else: self.objeto = None self.ir_a_primero()
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 buscar(self, boton): """ Dadas fecha de inicio y de fin, lista todos los albaranes pendientes de facturar. """ idcliente = utils.combo_get_value(self.wids['cmbe_cliente']) if idcliente == None: utils.dialogo_info(titulo = 'ERROR', texto = 'Seleccione un cliente', padre = self.wids['ventana']) else: idcliente = utils.combo_get_value(self.wids['cmbe_cliente']) self.cliente = pclases.Cliente.get(idcliente) cliente = self.cliente if self.inicio == None: pedidos = pclases.PedidoVenta.select(pclases.AND( pclases.PedidoVenta.q.fecha <= self.fin, pclases.PedidoVenta.q.clienteID == cliente.id), orderBy = 'fecha') else: pedidos = pclases.PedidoVenta.select(pclases.AND( pclases.PedidoVenta.q.fecha >= self.inicio, pclases.PedidoVenta.q.fecha <= self.fin, pclases.PedidoVenta.q.clienteID == cliente.id), orderBy='fecha') self.resultado = pedidos self.rellenar_tabla(self.resultado)
def expandir(self, tv, iter, path): model = tv.get_model() child = model[path].iterchildren().next() if child[0] == "" and \ child[1] == "" and \ child[2] == "" and \ child[3] == "": model.remove(child.iter) id = int(model[path][0]) clase = model[path][-1] # print clase, id try: objeto = eval('pclases.%s.get(%d)' % (clase, id)) except pclases.SQLObjectNotFound: utils.dialogo_info(titulo = "ERROR", texto = "El objeto %s con ID %d no existe." % (clase, id)) padre = model.get_iter(path) try: objeto.sync() except pclases.SQLObjectNotFound: model[path][0] = "" model[path][2] = "" model[path][3] = "" self.insertar_campos(objeto, padre, model) self.insertar_ajenos(objeto, padre, model) self.insertar_multiples(objeto, padre, model) tv.expand_row(path, False)
def enviar_a_laboratorio(self, parametro): # NOTA: Ni idea de qué es lo que traerá el parámetro, sólo me interesa # el parte que está seleccionado en el treeview. model, iter = self.wids['tv_datos'].get_selection().get_selected() if iter == None: utils.dialogo_info(titulo = "PARTE NO SELECCIONADO", texto = "Seleccione un parte para enviar una muestra de su\nlote o partida al laboratorio.", padre = self.wids['ventana']) else: idparte = model[iter][-1] if idparte > 0: parte = pclases.ParteDeProduccion.get(idparte) if not parte.articulos: utils.dialogo_info(titulo = "PARTE VACÍO", texto = "En el parte seleccionado no hubo producción.", padre = self.wids['ventana']) else: a = parte.articulos[0] # Al menos tiene 1 artículo. Con el primero me vale. if parte.es_de_balas(): try: lote = a.bala.lote loteCem = None except AttributeError: lote = None loteCem = a.bigbag.loteCem partida = None else: lote = None partida = a.rollo.partida self.crear_muestra(lote, partida, loteCem)
def __init__(self, objeto = None, usuario = None): """ Constructor. objeto puede ser un objeto de pclases con el que comenzar la ventana (en lugar del primero de la tabla, que es el que se muestra por defecto). """ Ventana.__init__(self, 'ventana_usuario.glade', objeto) connections = {'b_salir/clicked': self.salir, 'b_actualizar/clicked': self.actualizar_ventana, 'b_guardar/clicked': self.guardar, 'b_nuevo/clicked': self.crear_nuevo_usuario, 'b_borrar/clicked': self.borrar_usuario, 'b_buscar/clicked': self.buscar_usuario, 'b_enviar/clicked': self.enviar_mensaje, 'b_passw/clicked': self.nueva_contrasenna, 'b_drop/clicked': self.borrar_mensaje } self.add_connections(connections) self.inicializar_ventana() if self.objeto == None: self.ir_a_primero() utils.dialogo_info(titulo = "NO SE PUDO DETERMINAR SU USUARIO", texto = "No se pudo determinar su nombre de usuario.\nAsegúrese de haber iniciado sesión correctamente y vuelva a intentarlo.", padre = self.wids['ventana']) self.logger.error("ventana_usuario::__init__:No se pudo determinar nombre de usuario.") else: self.ir_a(objeto) self.wids['ventana'].maximize() gtk.main()
def ir_a_siguiente(self,widget): """ Hace que el siguiente registro (ordenados por ID interno) sea el objeto activo """ if self.wids['b_guardar'] != None and self.wids['b_guardar'].get_property('sensitive'): # Hay cambios pendientes de guardar. if utils.dialogo('Hay cambios pendientes de guardar.\n¿Desea hacerlo ahora?', '¿GUARDAR CAMBIOS?'): try: self.guardar(None) except: utils.dialogo_info(titulo = 'NO SE PUDO GUARDAR', texto = 'Los cambios no se pudieron guardar automáticamente.\nDebe hacerlo de forma manual', padre = self.wids['ventana']) return producto = self.objeto try: siguiente = pclases.ProductoVenta.select(pclases.AND(pclases.ProductoVenta.q.camposEspecificosEspecialID != None, pclases.ProductoVenta.q.id > producto.id),orderBy='id')[0] except IndexError: utils.dialogo_info(texto = "El elemento seleccionado es el último registrado en el sistema", titulo="ERROR", padre = self.wids['ventana']) return self.objeto = siguiente self.actualizar_ventana()
def agregar_a_pedido(self, widget): """ Asigna la linea sin pedido a un pedido ya creado """ model, iter = self.wids['tv_tipos'].get_selection().get_selected() if iter != None: idtipo = model[iter][-1] linea_sin_pedido = pclases.LineaDeVenta.get(idtipo) else: utils.dialogo_info('ERROR','Seleccione una línea para asignarla al pedido') return numpedido = utils.dialogo_entrada(titulo = 'NÚMERO DE PEDIDO', texto = 'Introduzca el número del pedido al que desea añadir la línea seleccionada') pedido = self.buscar_pedido(numpedido) if pedido == None: utils.dialogo_info(texto = 'No se encontró ningún pedido con ese número', titulo = 'ERROR') return linea_sin_pedido.pedidoVenta = pedido ldc = pclases.LineaDePedido(presupuesto = None, pedidoVenta = pedido, productoVenta = linea_sin_pedido.productoVenta, productoCompra = linea_sin_pedido.productoCompra, fechahora = mx.DateTime.localtime(), cantidad = linea_sin_pedido.cantidad, precio = linea_sin_pedido.precio, descuento = linea_sin_pedido.descuento, fechaEntrega = None, textoEntrega='') self.rellenar_tabla() import pedidos_de_venta pedidos_de_venta.PedidosDeVenta(objeto = pedido, usuario = self.usuario)
def buscar_cod_libre(self): """ Devuelve un código de tres cifras como cadena que no esté ya en la tabla de productos como producto terminado. Siempre devolverá el primer código libre más cercano a 000. Si no quedan más mostrará un mensaje de error por pantalla y devolverá 999. """ prods = pclases.ProductoVenta.select(pclases.AND( pclases.OR( pclases.ProductoVenta.q.camposEspecificosBalaID !=None, pclases.ProductoVenta.q.camposEspecificosRolloID !=None, pclases.ProductoVenta.q.camposEspecificosEspecialID !=None), pclases.ProductoVenta.q.codigo.startswith('843603219'))) codsproducto = [p.codigo[-4:-1] for p in prods] codsproducto.sort() for i in xrange(1000): # Si el subcódigo de producto del EAN-13 está # duplicado esto no funcionará. Añadida restricción UNIQUE en la # BD para evitarlo. # print i, int(codsproducto[i]) try: if i != int(codsproducto[i]): return "%03d" % i except IndexError: # No hay o me pasé de rango return "%03d" % i utils.dialogo_info('NO QUEDAN CÓDIGOS DISPONIBLES', 'Todos los códigos EAN13 fueron asignados.', padre = self.wids['ventana']) return '999'
def nuevo(self, widget): """ Función callback del botón b_nuevo. Pide los datos básicos para crear un nuevo objeto. Una vez insertado en la BD hay que hacerlo activo en la ventana para que puedan ser editados el resto de campos que no se hayan pedido aquí. """ anterior = self.objeto if anterior != None: anterior.notificador.desactivar() factura = self.buscar_factura() # Si lo que va a pagar es un LOGIC, que lo haga desde la ventana de vencimientos_pendientes. if factura != None: importe = factura.get_importe_primer_vencimiento_pendiente() # Devuelve 0 si no quedan. try: nuevo = pclases.Pago(cuentaOrigen = pclases.CuentaOrigen.select(orderBy = "-id")[0], fecha = mx.DateTime.localtime(), facturaCompra = factura, proveedor = factura.proveedor, importe = importe) except IndexError: utils.dialogo_info(titulo = "ERROR CREANDO TRANSFERENCIA", texto = "Se produjo un error al crear una nueva transferencia.\nProbablemente no existan cuentas en la aplicación desde donde realizar transferencias.", padre = self.wids['ventana']) else: utils.dialogo_info('NUEVA TRANSFERENCIA CREADA', 'Se ha creado una transferencia nueva.\nA continuación complete la información de la misma y guarde los cambios.', padre = self.wids['ventana']) self.objeto = nuevo self.objeto.notificador.activar(self.aviso_actualizacion) self.activar_widgets(True) self.actualizar_ventana(objeto_anterior = anterior)
def crear_nuevo_producto(self, widget): """ Función callback del botón b_nuevo. Pide los datos básicos para crear un nuevo objeto. Una vez insertado en la BD hay que hacerlo activo en la ventana para que puedan ser editados el resto de campos que no se hayan pedido aquí. """ producto = self.objeto if producto != None: producto.notificador.desactivar() producto.camposEspecificosEspecial.notificador.desactivar() campos = pclases.CamposEspecificosEspecial(unidad = '', observaciones = '', stock = 0.0, existencias = 0) producto = pclases.ProductoVenta(lineaDeProduccion = None, camposEspecificosBala = None, camposEspecificosRollo = None, camposEspecificosEspecial = campos, codigo = self.generar_codigo_ean(), nombre = '', descripcion = '', preciopordefecto = 0, minimo = 0, arancel = '') self._objetoreciencreado = producto utils.dialogo_info('PRODUCTO CREADO', 'Se ha creado un producto nuevo.\n\n\nA continuación complete la información del producto y guarde los cambios.\n', padre = self.wids['ventana']) producto.notificador.activar(self.aviso_actualizacion) producto.camposEspecificosEspecial.notificador.activar(self.aviso_actualizacion) self.objeto = producto self.activar_widgets(True) self.actualizar_ventana()
def borrar(self, widget): """ Elimina la cuenta de la tabla pero NO intenta eliminar ninguna de sus relaciones, de forma que si se incumple alguna restricción de la BD, cancelará la eliminación y avisará al usuario. """ cuenta = self.objeto if not utils.dialogo('¿Eliminar la cuenta?', 'BORRAR', padre = self.wids['ventana']): return if cuenta.pagos != []: utils.dialogo_info('CUENTA NO ELIMINADA', 'La cuenta está implicada en operaciones que impiden su borrado.', padre = self.wids['ventana']) else: cuenta.notificador.desactivar() try: cuenta.destroySelf() except Exception, e: self.logger.error("cuentas_destino::borrar -> Cuenta ID %d no se pudo eliminar. Excepción: %s." % (cuenta.id, e)) utils.dialogo_info(titulo = "CUENTA NO BORRADA", texto = "La cuenta no se pudo eliminar.\n\nSe generó un informe de error en el «log» de la aplicación.", padre = self.wids['ventana']) self.actualizar_ventana() return self.objeto = None self.ir_a_primero()
def borrar_producto(self, widget): """ Elimina el producto de la tabla pero NO intenta eliminar ninguna de sus relaciones, de forma que si se incumple alguna restricción de la BD, cancelará la eliminación y avisará al usuario. """ producto = self.objeto if not utils.dialogo('¿Eliminar el producto?', 'BORRAR', padre = self.wids['ventana']): return if producto.articulos != [] or producto.precios != []: utils.dialogo_info('PRODUCTO NO ELIMINADO', 'El producto está implicado en operaciones que impiden su borrado.', padre = self.wids['ventana']) else: producto.notificador.desactivar() producto.camposEspecificosEspecial.notificador.desactivar() cee = producto.camposEspecificosEspecial producto.camposEspecificosEspecial = None try: producto.destroySelf() except: producto.camposEspecificosEspecial = cee utils.dialogo_info(titulo = "PRODUCTO NO BORRADO", texto = "El producto no se pudo eliminar", padre = self.wids['ventana']) self.actualizar_ventana() return cee.destroySelf() self.objeto = None self.ir_a_primero()
def add_producto_a_consumo(self, boton): """ Añade un producto de venta a un consumo existente. """ model, paths = self.wids['tv_consumos'].get_selection().get_selected_rows() if paths != []: productos = self.pedir_productos_venta() if productos: for path in paths: if model[path].parent == None: id_consumo = model[path][-1] else: id_consumo = model[path].parent[-1] consumo_adicional_por_producto = pclases.ConsumoAdicional.get(id_consumo) for producto in productos: if producto not in consumo_adicional_por_producto.productosVenta: consumo_adicional_por_producto.addProductoVenta(producto) else: utils.dialogo_info(titulo = "YA EXISTE", texto = "El producto %s ya consume según la fórmula de %s.\n\nPulse «Aceptar» para continuar." % (producto.descripcion, consumo_adicional_por_producto.nombre), padre = self.wids['ventana']) self.rellenar_consumos_adicionales_por_producto() else: utils.dialogo_info(titulo = "SELECCIONE UN CONSUMO", texto = "Debe seleccionar un consumo existente.", padre = self.wids['ventana'])
def set_partida(self, w): numpartida = utils.dialogo_entrada(titulo = 'Nº PARTIDA', texto = 'Introduzca número de partida:') if numpartida != None: partidas = pclases.Partida.select(pclases.Partida.q.numpartida.contains(numpartida)) if partidas.count() == 0: utils.dialogo_info(titulo = 'PARTIDA NO ENCONTRADA', texto = 'No se encontró ninguna partida %s.' % numpartida) return elif partidas.count() > 1: filas = [(l.id, l.numpartida, l.codigo, l.longitudinal, l.transversal, l.compresion, l.perforacion, l.permeabilidad, l.poros, l.espesor) for l in partidas] idpartida = utils.dialogo_resultado(filas, titulo = 'SELECCIONE PARTIDA', cabeceras = ('ID', 'Número', 'Código', 'Longitudinal', 'Transversal', 'CBR', 'Perforación', 'Permeabilidad', 'Poros', 'Espesor')) if idpartida < 0: return partida = pclases.Partida.get(idpartida) else: partida = partidas[0] if len(partida.rollos) == 0: utils.dialogo_info(titulo = 'PARTIDA VACÍA', texto = 'La partida no contiene rollos, no puede\nrealizar pruebas sobre una partida vacía.') self.partida = None return self.partida = partida self.actualizar_ventana()
def pedir_producto(self): """ Solicita una descripción de producto, muestra una ventana de resultados coincidentes con la búsqueda de ese código y devuelve un objeto producto seleccionado de entre los resultados o None si se cancela o no se encuentra. """ producto = None codigo = utils.dialogo_entrada(texto = 'Introduzca descripción del producto.', titulo = 'PRODUCTO', padre = self.wids['ventana']) if codigo == None: # Ha cancelado producto = self.producto if codigo != None: prods = pclases.ProductoCompra.select(pclases.ProductoCompra.q.descripcion.contains(codigo)) prods = [p for p in prods] mens_error = 'No se encontró ningún producto con esa descripción.' if len(prods) > 1: idproducto = self.refinar_busqueda_productos(prods) if idproducto != None: prods = [p for p in prods if p.id == idproducto] else: return None elif len(prods) < 1: utils.dialogo_info('CÓDIGO NO ENCONTRADO', mens_error) return None producto = prods[0] return producto
def anular_carga_y_crear_nueva(self, ldc, silo): """ Busca la carga asociada a la línea de compra, la anula y crea una nueva relacionada con el silo recibido. Devuelve True si se anuló la carga creando la nueva. """ ldc.sync() if ldc.cargaSilo: try: carga_anterior = ldc.cargaSilo ldc.cargaSilo = None ldc.silo = None carga_anterior.destroySelf() except: utils.dialogo_info(titulo = "OPERACIÓN NO PERMITIDA", texto="No puede cambiar el silo de carga.", padre = self.wids['ventana']) ldc.cargaSilo = carga_anterior ldc.silo = carga_anterior.silo return False ldc.silo = silo carga = pclases.CargaSilo(silo = silo, fechaCarga = ldc.albaranEntrada.fecha, cantidad = ldc.cantidad, productoCompra = ldc.productoCompra) ldc.cargaSilo = carga return True