def guardar(self, widget): """ Marca como "enviada" cada visita del model y envía un correo electrónico para alertar de que el parte está completo. """ confirmadas = [] model = self.wids['tv_visitas'].get_model() for row in model: visita = pclases.getObjetoPUID(row[-1]) if not visita.enviada: model_clientes = self.wids['tv_visitas'].get_column( 1).get_cell_renderers()[0].completion.get_model() cliente = None for nombre, puid in model_clientes: if nombre == visita.nombrecliente: cliente = pclases.getObjetoPUID(puid) break visita.cliente = cliente if (not visita.cliente and not visita.observaciones and not visita.motivoVisita): pass # No confirmo visitas vacías. Pero tampoco doy el # coñazo con diálogos. Más adelante se podrá # afinar para no confirmas visitas sin cliente o # sin motivo, etc. else: visita.enviada = True confirmadas.append(visita) visita.syncUpdate() pclases.Auditoria.modificado(visita, self.usuario, __file__, "Visita ID %d confirmada." % visita.id) self.actualizar_ventana() self.enviar_correo_visitas_confirmadas(confirmadas)
def cambiar_concepto(self, cell, path, value): model = self.wids['tv_datos'].get_model() puid = model[path][-1] o = pclases.getObjetoPUID(puid) if isinstance(o, pclases.ConceptoPresupuestoAnual): # FIXME: ¿Y si es un concepto de primer nivel creado por el # usuario? ¿No lo puede cambiar o qué? if o.proveedor: utils.dialogo_info(titulo = "PROVEEDOR NO MODIFICABLE", texto = "El nombre del proveedor no se puede cambiar \n" "desde esta ventana, use la de proveedores.\n" "Si lo que quiere es usar otro proveedor, \n" "elimine la línea e introduzca un nuevo \n" "proveedor en su lugar.", padre = self.wids['ventana']) elif o.inmutable: utils.dialogo_info(titulo = "CONCEPTO NO MODIFICABLE", texto = "El concepto seleccionado no se puede \n" "modificar. Corresponde a cálculos \n" "predefinidos por la aplicación. \n" "Cree un nuevo concepto con el nuevo \n" "nombre si lo necesita.", padre = self.wids['ventana']) else: o.descripcion = value o.syncUpdate() model[path][0] = o.descripcion
def borrar(self, widget): """ Elimina una visita marcada en el Treeview por el usuario. """ sel = self.wids['tv_visitas'].get_selection() model, paths = sel.get_selected_rows() if paths and utils.dialogo('¿Eliminar las visitas seleccionadas?', 'BORRAR', padre = self.wids['ventana']): paths_a_borrar = [] for path in paths: visita = pclases.getObjetoPUID(model[path][-1]) dia = visita.fechahora.day if (not visita.enviada or (visita.enviada and self.usuario and self.usuario.nivel <= NIVEL_SUPERVISOR)): visita.destroy(ventana = __file__) paths_a_borrar.append(path) #self.actualizar_ventana() paths_a_borrar.sort(reverse = True) for path in paths_a_borrar: model.remove(model.get_iter(path)) visitas_del_dia = self.refresh_commit_button() if visitas_del_dia: self.wids['calendario'].mark_day(dia) else: self.wids['calendario'].unmark_day(dia) if len(paths_a_borrar) < len(paths): utils.dialogo_info(titulo = "VISITAS NO ELIMINADAS", texto = "Algunas visitas no se eliminaron al" " encontrarse ya enviadas.", padre = self.wids['ventana'])
def cambiar_concentracion(self, cell, path, texto): model = self.wids['tv_concentracion'].get_model() try: concentracion = utils._float(texto) except (ValueError, TypeError): utils.dialogo_info("ERROR EN FORMATO", "Introduzca la concentración como fracción de 1." "\n(P. ej. 20% = 0.2)", padre = self.wids['ventana']) else: puid = model[path][-1] cr = pclases.getObjetoPUID(puid) #if (sum([c.concentracion # for c in self.objeto.concentracionesRemesa if c != cr]) # + concentracion) > 1.0: # utils.dialogo_info(titulo = "EXCESO CONCENTRACIÓN", # texto = "La concentración total no puede superar el 100%." # "\nSe corregirá.", # padre = self.wids['ventana']) # concentracion = 1.0 - sum([c.concentracion # for c in self.objeto.concentracionesRemesa # if c != cr]) cr.concentracion = concentracion cr.syncUpdate() model[path][1] = utils.float2str(cr.concentracion)
def ver_adjunto(self, boton): """ Intenta abrir el adjunto seleccionado. """ from multi_open import open as mopen if self.wids['tv_adjuntos'].parent.get_property("visible"): model,iter=self.wids['tv_adjuntos'].get_selection().get_selected() else: paths = self.wids['iv_adjuntos'].get_selected_items() model = self.wids['iv_adjuntos'].get_model() iter = paths[0] if iter != None: docid = model[iter][-1] adjunto = pclases.getObjetoPUID(docid) self.wids['ventana'].window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) while gtk.events_pending(): gtk.main_iteration(False) try: if not mopen(adjunto.get_ruta_completa()): utils.dialogo_info(titulo = "NO SOPORTADO", texto = "La aplicación no conoce cómo abrir el tipo de fichero.", padre = self.wids['ventana']) except: utils.dialogo_info(titulo = "ERROR", texto = "Se produjo un error al abrir el archivo.\nLa plataforma no está soportada, no se conoce el tipo de archivo o no hay un programa asociado al mismo.", padre = self.wids['ventana']) import gobject gobject.timeout_add(2000, lambda *args, **kw: self.wids['ventana'].window.set_cursor(None))
def abrir_objeto(self, tv, path, view_column): """ Abre el presupuesto, producto o cliente según corresponda. """ model = tv.get_model() puid = model[path][-1] objeto = pclases.getObjetoPUID(puid) if isinstance(objeto, pclases.Cliente): from formularios.clientes import Clientes as NuevaVentana elif isinstance(objeto, pclases.Presupuesto): from formularios.presupuestos import Presupuestos as NuevaVentana elif isinstance(objeto, pclases.ProductoVenta): if objeto.es_rollo(): from formularios.productos_de_venta_rollos \ import ProductosDeVentaRollos as NuevaVentana elif objeto.es_fibra(): from formularios.productos_de_venta_balas \ import ProductosDeVentaBalas as NuevaVentana else: # PLAN: Y ya iré contemplando más casos si van haciendo falta. return elif isinstance(objeto, pclases.ProductoCompra): from formularios.productos_compra \ import ProductosCompra as NuevaVentana elif isinstance(objeto, pclases.LineaDePresupuesto): objeto = objeto.presupuesto from formularios.presupuestos import Presupuestos as NuevaVentana else: return # Si no es nada de lo que pueda abrir, pasando del temita. v = NuevaVentana(usuario = self.usuario, objeto = objeto)
def cambiar_hora(self, cell, path, newtext): """ Cambia la hora de visita y acutaliza el cell. Solo se permite cambiarla si el usuario tiene nivel suficiente. """ model = self.wids['tv_visitas'].get_model() ide = model[path][-1] visita = pclases.getObjetoPUID(ide) if not visita.enviada or (self.usuario and self.usuario.nivel <= NIVEL_SUPERVISOR): try: dtdelta = utils.parse_hora(newtext) visita.fechahora = mx.DateTime.DateTimeFrom( year = visita.fechahora.year, month = visita.fechahora.month, day = visita.fechahora.day, hour = dtdelta.hour, minute = dtdelta.minute, second = dtdelta.second) visita.sync() model[path][0] = visita.fechahora.strftime('%H:%M') except IndexError: utils.dialogo_info(titulo = "ERROR", texto = 'El texto "%s" no respeta el formato ' 'horario (H:MM).' % newtext, padre = self.wids['ventana']) except ValueError: utils.dialogo_info(titulo = "ERROR", texto = 'El texto "%s" no respeta el formato ' 'horario (H:M).' % newtext, padre = self.wids['ventana']) else: utils.dialogo_info(titulo = "NO SE PUEDE MODIFICAR", texto = "Visita confirmada. No puede modificarla.", padre = self.wids['ventana'])
def abrir_objeto(tv, path, view_column, usuario=None): """ Trata de determinar la ventana principal que maneja el tipo de objeto recibido y la instancia con el usuario especificado. """ auditline_puid = tv.get_model()[path][-1] auditline = pclases.getObjetoPUID(auditline) puid = auditline.dbpuid objeto = pclases.getObjetoPUID(puid) ventana = determinar_clase_ventana(objeto) if ventana: v = ventana(usuario = usuario, objeto = objeto) else: utils.dialogo_info(titulo = "AUDITVIEWER: ERROR AL ABRIR OBJETO", texto = "No se pudo abrir el objeto «%s». No sé con qué " "ventana hacerlo." % puid, padre = self.wids['ventana'])
def abrir_adjunto_from_iview(iview, path): # Código para adjuntos. """ Abre el adjunto con el programa asociado al mime-type del mismo. """ model = iview.get_model() puid = model[path][-1] adjunto = pclases.getObjetoPUID(puid) from multi_open import open as mopen mopen(adjunto.get_ruta_completa())
def drop_concentracion(self, boton): if not utils.dialogo(titulo = "¿BORRAR?", texto = "Se eliminará la fila seleccionada. ¿Continuar?", padre = self.wids['ventana']): return model,path=self.wids['tv_concentracion'].get_selection().get_selected() puid = model[path][-1] cr = pclases.getObjetoPUID(puid) cr.destroy(usuario = self.usuario, ventana = __file__) self.rellenar_tabla_concentraciones()
def drop_docpago(self, boton): """ Elimina el documento de pago seleccionado. """ sel = self.wids["tv_documentos_pago"].get_selection() model, itr = sel.get_selected() if itr: objeto = pclases.getObjetoPUID(model[itr][-1]) objeto.destroy_en_cascada(ventana = __file__) model.remove(itr)
def enviar_correo_visitas_confirmadas(self, confirmadas = []): """ Igual que en ofertas, se envía un correo a quien corresponde con el resumen del día, destacando las recién confirmadas. """ dests = select_correo_validador(self.usuario, copia_a_dircomercial = True) if not isinstance(dests, (list, tuple)): dests = [dests] servidor = self.usuario.smtpserver smtpuser = self.usuario.smtpuser smtppass = self.usuario.smtppassword rte = self.usuario.email year, month, day = self.wids['calendario'].get_date() fecha = datetime.date(year = year, month = month, day = day) texto = "Resumen de visitas de %s para el día %s:\n\n" % ( self.objeto.get_nombre_completo(), utils.str_fecha(fecha)) # TODO: Empepinar el correo usando una tabla HTML. No es tan difícil. for row in self.wids['tv_visitas'].get_model(): visita = pclases.getObjetoPUID(row[-1]) if not visita.enviada: continue texto += "%s%s\t%s%s\t%s\t%s\t%s%s\n" % ( visita in confirmadas and "*" or "", utils.str_hora_corta(visita.fechahora), visita.nombrecliente, visita.cliente and "" or " (+)", visita.lugar and visita.lugar or "", visita.motivoVisita and visita.motivoVisita.motivo or "", visita.observaciones, visita in confirmadas and "*" or "" ) ok = utils.enviar_correoe(rte, dests, "Parte de visitas confirmado (%s)" % utils.str_fecha(fecha), texto, servidor = servidor, usuario = smtpuser, password = smtppass) if ok: self.to_log( "Usuario %s envió correo de confirmación de visitas " "para el día %s." % (self.usuario and self.usuario.usuario or "¡NADIE!", utils.str_fecha(fecha)), nivel = 3) # info else: self.to_log( "Falló envío de correo de solicitud de validación de la " "oferta %s del usuario %s." % (utils.str_fecha(fecha), self.usuario and self.usuario.usuario or "¡NADIE!"), nivel = 1) # error
def abrir_notas(algo, tv, clase, usuario): seleccion = tv.get_selection() model, paths = seleccion.get_selected_rows() itr = model.get_iter(paths[0]) ide = model[itr][-1] try: objeto = clase.get(ide) except ValueError: # Es un PUID from framework import pclases objeto = pclases.getObjetoPUID(ide) ventana = Postomatic(objeto, usuario) # @UnusedVariable
def confirmar_efecto(self, cell, path): model = self.wids['tv_efectos'].get_model() model[path][0] = not model[path][0] a_confirmar = [] itr = model.get_iter_first() while itr: if model[itr][0]: a_confirmar.append(pclases.getObjetoPUID(model[itr][-1])) itr = model.iter_next(itr) self.wids['e_importe_seleccionado'].set_text( utils.float2str(sum([o.cantidad for o in a_confirmar])))
def cell_func(column, cell, model, itr, i): try: presupuesto = pclases.getObjetoPUID(model[itr][-1]) except (AttributeError, pclases.SQLObjectNotFound): color = None else: if presupuesto.rechazado: color = "Indian Red" else: color = None # TODO: ¿Tal vez un color por cada motivo de no validación automática? cell.set_property("cell-background", color)
def imprimir(self, boton): """ Imprime una hoja de ruta por cada laborante. Si se ha seleccionado alguno, entonces solo imprime su hoja de ruta. """ model, iter = self.wids['tv_laborantes'].get_selection().get_selected() if not iter: # Imprimir para todos: laborantes = [] for fila in model: puid = fila[-1] laborante = pclases.getObjetoPUID(puid) laborantes.append(laborante) else: puid = model[iter][-1] laborante = pclases.getObjetoPUID(puid) laborantes = [laborante] dia = self.get_fecha_seleccionada() for laborante in laborantes: abrir_hoja_de_ruta(laborante, dia)
def asignar(self, boton): model, iter = self.wids['tv_laborantes'].get_selection().get_selected() if not iter: utils.ui.dialogo_info(titulo = "SELECCIONE UN LABORANTE", texto = "Debe seleccionar un laborante al que asignar las " "peticiones de recogida de material.", padre = self.wids['ventana']) else: empleado = pclases.getObjetoPUID(model[iter][-1]) sel = self.wids['tv_sin_asignar'].get_selection() sel.selected_foreach(self.asiganda, empleado) self.actualizar_ventana()
def abrir_efecto(self, tv, path, view_column): model = tv.get_model() puid = model[path][-1] objeto = pclases.getObjetoPUID(puid) if objeto.pagareCobro: from formularios import pagares_cobros v = pagares_cobros.PagaresCobros(objeto.pagareCobro, # @UnusedVariable usuario = self.usuario) elif objeto.confirming: from formularios import confirmings v = confirmings.Confirmings(objeto.confirming, # @UnusedVariable usuario = self.usuario)
def abrir_objeto(self, tv, path, column): """ Abre la visita a la que se le ha hecho doble clic en una ventana nueva. """ model = tv.get_model() dbpuid = model[path][-1] objeto = pclases.getObjetoPUID(dbpuid) # Se le pasa un comercial o una visita concreta. La ventana destino # decide qué hacer en cada caso. from formularios import partes_de_visita ventanapartes = partes_de_visita.PartesDeVisita(objeto = objeto, usuario = self.usuario)
def cambiar_cliente(self, cell, path, text, model, ncolmodel, model_tv): """Cambia el nombre del cliente de la visita.""" model = model_tv ide = model[path][-1] visita = pclases.getObjetoPUID(ide) visita.nombrecliente = text # No se hace comprobación de si el cliente existe en la base de datos. # Eso se hará en el commit. OJO: Si el cliente se crea a posteriori, # el cliente no queda asociado a la visita. It's not a bug, IT'S A # FEATURE: permite hacer consultas sobre visitas a un cliente cuando # aún no era cliente. visita.syncUpdate() model[path][ncolmodel] = visita.nombrecliente
def cambiar_lugar(self, cell, path, text): model = self.wids['tv_visitas'].get_model() ide = model[path][-1] visita = pclases.getObjetoPUID(ide) if not visita.enviada or (self.usuario and self.usuario.nivel <= NIVEL_SUPERVISOR): visita.lugar = text # PLAN: ¿GIS? visita.syncUpdate() model[path][4] = visita.lugar else: utils.dialogo_info(titulo = "NO SE PUEDE MODIFICAR", texto = "Visita confirmada. No puede modificarla.", padre = self.wids['ventana'])
def confirmar(self, boton): if self.objeto and self.objeto.aceptada: self.desconfirmar(boton) else: efectos_a_confirmar = [] model = self.wids['tv_efectos'].get_model() itr = model.get_iter_first() while itr: if model[itr][0]: efectos_a_confirmar.append( pclases.getObjetoPUID(model[itr][-1])) itr = model.iter_next(itr) if not efectos_a_confirmar: continuar = utils.dialogo(titulo = "REMESA VACÍA", texto = "No ha seleccionado ningún efecto de cobro.\n" "Se devolverán todos a cartera. ¿Está seguro?", padre = self.wids['ventana'], defecto = False) if not continuar: return # Los efectos no marcados vuelven a cartera. for e in self.objeto.efectos: if e not in efectos_a_confirmar: self.objeto.removeEfecto(e) # Hay que desvincular los efectos de las demás remesas for e in efectos_a_confirmar: for r in e.remesas: if r != self.objeto: e.removeRemesa(r) # PLAN: ¿Cómo activo el notificador de otras posibles remesas # abiertas tras este cambio? # Y confirmar la actual marcando el campo "aceptada" y fecha # de cobro. self.objeto.aceptada = True self.objeto.fechaCobro = mx.DateTime.localtime() self.objeto.syncUpdate() self.actualizar_ventana() self.objeto.sync() if not self.objeto.codigo: self.wids['e_codigo'].set_text('Inserte código aquí.') utils.dialogo_info(titulo = "COMPRUEBE DATOS", texto = "Introduzca el código de remesa " "facilitado por el banco.", padre = self.wids['ventana']) if not self.objeto.fechaPrevista: self.wids['e_fecha'].set_text( utils.str_fecha(mx.DateTime.localtime() + (mx.DateTime.oneDay * 2))) utils.dialogo_info(titulo = "COMPRUEBE DATOS", texto = "Corrija la fecha prevista de ingreso.", padre = self.wids['ventana'])
def parse_options(): """ Instancia las opciones de arranque (parámetros de línea de comando) para la ventana de la clase invocadora. """ from optparse import OptionParser parser = OptionParser() parser.add_option("-u", "--usuario", dest = "usuario", help = _("Nombre o identificador del usuario")) parser.add_option("-d", "--debug", dest = "debug", action="store_true", help = _("Activar información de depuración")) parser.add_option("-p", "--puid", dest = "puid", help = _("Identificador de pclases (puid) " "del objeto a mostrar en ventana inicialmente")) parser.add_option("-c", "--config", dest = "fichconfig", help = _("Usa una configuración alternativa " "almacenada en FICHERO"), metavar = "FICHERO") parser.add_option("-v", "--verbose", dest = "verbose", action = "store_true", help = _("Activar el modo verboso")) (options, args) = parser.parse_args() params = [] opt_params = {} if len(args) != 0: params = args[0] # FIXME: Como pclases se importa en ventana_generica y en cualquier otra # ventana (a excepción del menú) antes de poder parsear la opción "-c", no # hay manera -inmediata, se entiende, sin reescribir mucho- de usar una # configuración alternativa que no sea la que está en ginn.conf :( fconfig = options.fichconfig if fconfig: config = ConfigConexion() config.set_file(fconfig) config.DEBUG = options.debug config.VERBOSE = options.verbose # Como no se puede cambiar al vuelo la conexión a la BD, el cambio del # fichero de configuración hay que hacerlo ANTES de "cargar" pclases. from framework import pclases pclases.DEBUG = options.debug pclases.VERBOSE = options.verbose if options.usuario: opt_params["usuario"] = options.usuario if options.puid: puid = options.puid objeto = pclases.getObjetoPUID(puid) opt_params["objeto"] = objeto return params, opt_params
def actualizar_mapa(self, sel, track = True, flag = True): model, paths = sel.get_selected_rows() for path in paths: puid = model[path][-1] peticion = pclases.getObjetoPUID(puid) d = peticion.direccion if not d: d = peticion.obra.direccion try: self.mapa.centrar_mapa(d.lat, d.lon, zoom = 12, track = track, flag = flag) except AttributeError, e: # print e pass # La obra/peticion no tiene dirección asignada.
def borrar(self, widget): """ Elimina los datos de la fila seleccionada o la fila completa. """ model, itr = self.wids['tv_datos'].get_selection().get_selected() if itr: puid = model[itr][-1] o = pclases.getObjetoPUID(puid) if (isinstance(o, pclases.ConceptoPresupuestoAnual) and utils.dialogo(titulo = "ELIMINAR CONCEPTO", texto = "Se eliminará la fila completa. ¿Continuar?", padre = self.wids['ventana'])): o.destroy_en_cascada(ventana = __file__) self.actualizar_ventana(None)
def drop_adjunto(self, boton): """ Elimina el adjunto seleccionado. """ # TODO: FIXME: No funciona en la vista de iconos. model, iter = self.wids['tv_adjuntos'].get_selection().get_selected() if iter != None and utils.ui.dialogo(titulo = "BORRAR DOCUMENTO", texto = '¿Borrar documento adjunto seleccionado?', padre = self.wids['ventana']): docid = model[iter][-1] adjunto = pclases.getObjetoPUID(docid) from utils import fichero fichero.mover_a_tmp(adjunto.get_ruta_completa()) adjunto.destroySelf() self.rellenar_adjuntos()
def abrir_producto(self, tv, path, column): """ Abre el producto al que se le ha hecho doble clic en una ventana nueva. """ model = tv.get_model() puid = model[path][-1] producto = pclases.getObjetoPUID(puid) if producto.es_rollo(): from formularios import productos_de_venta_rollos V = productos_de_venta_rollos.ProductosDeVentaRollos ventana_producto = V(producto, usuario = self.usuario) # @UnusedVariable elif producto.es_bala() or producto.es_bigbag() or producto.es_bolsa(): from formularios import productos_de_venta_balas V = productos_de_venta_balas.ProductosDeVentaBalas ventana_producto = V(producto, usuario = self.usuario) # @UnusedVariable
def cell_func(column, cell, model, itr, i): try: presupuesto = pclases.getObjetoPUID(model[itr][-1]) except (AttributeError, pclases.SQLObjectNotFound): color = None else: if presupuesto.rechazado: color = "Indian Red" elif presupuesto.validado: # En ofertas de estudio, básicamente es si el cliente # está dado de alta o no. color = "light yellow" else: color = None cell.set_property("cell-background", color)
def abrir_objeto(self, tv, path, column): """ Abre el factura al que se le ha hecho doble clic en una ventana nueva. """ model = tv.get_model() dbpuid = model[path][-1] objeto = pclases.getObjetoPUID(dbpuid) if isinstance(objeto, pclases.Proveedor): from formularios import proveedores ventanaproveedor = proveedores.Proveedores(objeto = objeto, usuario = self.usuario) else: from formularios import facturas_compra ventanafactura = facturas_compra.FacturasDeEntrada(objeto = objeto, usuario = self.usuario)
def cambiar_precio_minimo(self, cell, path, texto): if texto.strip() == "": precio = None else: try: precio = utils._float(texto) except: return model = self.wids['tv_precio_minimo'].get_model() ldp = pclases.getObjetoPUID(model[path][-1]) antes = ldp.precioMinimo ldp.precioMinimo = precio pclases.Auditoria.modificado(ldp, self.usuario, __file__, "Precio mínimo de %s cambiado de %s a %s" % ( ldp.nombre, antes, ldp.precioMinimo)) self.rellenar_precio_minimo()