def comparar_campo(self, col, type_col): """ Compara el contenido del widget cuyo nombre coincide con "col" con el valor del objeto para el campo "col". "type_col" es el tipo SQLObject del atributo, se usa para determinar cómo comparar los valores. """ res = False if isinstance(type_col, pclases.SOStringCol): # Cadena: el widget es un entry res = self.comparar_string(col) elif isinstance(type_col, pclases.SOIntCol): # Entero: el widget es un entry res = self.comparar_int(col) elif isinstance(type_col, pclases.SOBoolCol): # Boolean: el widget es un checkbox res = self.comparar_bool(col) elif isinstance(type_col, pclases.SOForeignKey): # Entero-clave ajena: el widget es un comboboxentry res = self.comparar_ajena(col) elif isinstance(type_col, pclases.SOCol): # Clase base, casi seguro Float: el widget es un entry res = self.comparar_float(col) else: txterr = "empleados.py: No se pudo determinar el tipo de datos "\ "del campo %s." % col myprint(txterr) self.logger.error(txterr) return res
def __init__(self, user=None, passwd=None, fconfig=None): """ user: Usuario. Si es None se solicitará en la ventana de autentificación. passwd: Contraseña. Si es None, se solicitaré en la ventana de autentificación. Si user y passwd son distintos a None, no se mostrará la ventana de autentificación a no ser que sean incorrectos. """ self.fconfig = fconfig from formularios import gestor_mensajes, autenticacion login = autenticacion.Autenticacion(user, passwd) if not login.loginvalido(): sys.exit(1) self.logger = login.logger pclases.logged_user = self.usuario = login.loginvalido() # Informes de error por correo: install_bug_hook(self.usuario) # Continúo con el gestor de mensajes y resto de ventana menú. if pclases.VERBOSE: myprint("Cargando gestor de mensajes...") self.__gm = gestor_mensajes.GestorMensajes(self.usuario) # DONE: Dividir la ventana en expansores con los módulos del programa # (categorías) y dentro de ellos un IconView con los iconos de cada # ventana. Poner también en lo alto del VBox el icono de la aplicación. # (Ya va siendo hora de un poquito de eyecandy). if pclases.VERBOSE: myprint("Cargando menú principal...") self.construir_ventana() utils.escribir_barra_estado(self.statusbar, "Menú iniciado", self.logger, self.usuario.usuario)
def nueva_partida(self, codigo): """ Crea una nueva partida de carga y la hace activa en la ventana. """ if self.usuario: try: ventana = pclases.Ventana.select( pclases.Ventana.q.fichero == os.path.basename(__file__))[0] except IndexError: txt = "consumo_balas_partida::comprobar_permisos ->"\ " Ventana no encontrada en BD." self.logger.error(txt) myprint(txt) else: permiso = self.usuario.get_permiso(ventana) if not permiso.nuevo and self.usuario.nivel > 2: utils.dialogo_info(titulo="NO TIENE PERMISOS", texto="No tiene permisos suficientes" " para crear partidas.", padre=self.wids['ventana']) else: try: numpartida = int(codigo.upper().replace("PC", "")) except ValueError: utils.dialogo_info(titulo="ERROR NÚMERO PARTIDA", texto="El número de partida debe" " ser un entero.", padre=self.wids['ventana']) return partida = pclases.PartidaCarga(numpartida=numpartida, codigo="PC%d" % (numpartida)) pclases.Auditoria.nuevo(partida, self.usuario, __file__) self.objeto = partida self.actualizar_ventana()
def set_valor(self, w, nombrecampo, tipocampo): # valor = self.objeto._SO_getValue(nombrecampo) get_valor = getattr(self.objeto, "_SO_get_%s" % (nombrecampo)) valor = get_valor() if isinstance(tipocampo, pclases.SOStringCol): # Cadena: el widget es # un entry if valor is not None: w.set_text(valor) else: w.set_text("") elif isinstance(tipocampo, pclases.SOIntCol): # Entero: el widget es # un entry try: w.set_text("%d" % valor) except TypeError: w.set_text("0") elif isinstance(tipocampo, pclases.SOBoolCol): # Boolean: el widget # es un checkbox w.set_active(valor) elif isinstance(tipocampo, pclases.SOForeignKey): # Entero-clave # ajena: el widget es un comboboxentry utils.combo_set_from_db(w, valor) elif isinstance(tipocampo, pclases.SOCol): # Clase base, casi # seguro Float: el widget es un entry if valor is not None: try: w.set_text(utils.float2str(valor)) except ValueError: w.set_text('0') else: txt = "empleados.py: No se pudo establecer el valor %s "\ "para %s." % (valor, w) myprint(txt) self.logger.error(txt)
def crear_vencimientos_por_defecto(self): """ Crea e inserta los vencimientos por defecto definidos por el cliente en la factura actual. Elimina los que tuviera previamente. """ # Primero se intenta tirando del pedido. El último que tenga. try: pedido = self.get_pedidos()[-1] pedido.sync() except IndexError: vtos = None else: try: vtos = [pedido.formaDePago.plazo] except AttributeError: vtos = None # Si no, recurrimos a los vencimientos por defecto del cliente. cliente = self.cliente cliente.sync() if not vtos: if cliente.vencimientos != None and cliente.vencimientos != '': try: vtos = cliente.get_vencimientos(self.fecha) except Exception, msg: vtos = [] # Los vencimientos no son válidos o no tiene. if DEBUG: myprint("pclases::superfacturaventa -> Excepción" "capturada al determinar vencimientos del" "cliente:\n", msg, "\nSe asume que no tiene datos suficientes.")
def enviar_correo_error_ventana(self): myprint("Se ha detectado un error") texto = '' for e in sys.exc_info(): texto += "%s\n" % e tb = sys.exc_info()[2] texto += "Línea %s\n" % tb.tb_lineno info = MetaF() traceback.print_tb(tb, file = info) texto += "%s\n" % info enviar_correo(texto, self.get_usuario())
def comprobar_permisos(self): """ Comprueba si el usuario tiene permiso para escritura o nuevo y deshabilita o habilita los botones según sea el caso. """ permiso = False # 20171122 FIX initialization bug if self.usuario: try: ventana = pclases.Ventana.select( pclases.Ventana.q.fichero == os.path.basename(__file__))[0] except IndexError: txt = "consumo_balas_partida::comprobar_permisos ->"\ " Ventana no encontrada en BD." self.logger.error(txt) myprint(txt) else: permiso = self.usuario.get_permiso(ventana) wids = ("b_add_partida_gtx", "b_drop_partida_gtx", "b_add_balas", "b_add_producto", "b_drop_bala", "b_phaser", "b_albaran", "e_fecha", "b_fecha", "b_from_pdp", "b_from_albaran") if not permiso or (not permiso.escritura and self.usuario.nivel > 2): for wid in wids: self.wids[wid].set_sensitive(False) else: for wid in wids: self.wids[wid].set_sensitive(True) self.wids['b_partida'].set_sensitive(bool(permiso and not(not permiso.nuevo and not permiso.lectura))) if (self.usuario.nivel > 2 and permiso and permiso.nuevo and pclases.PartidaCarga.select().count() > 0 and self.objeto == pclases.PartidaCarga.select( orderBy="-id")[0]): for wid in wids: self.wids[wid].set_sensitive(True) if self.usuario.nivel <= 2: for wid in wids: self.wids[wid].set_sensitive(True) self.wids['b_albaran'].set_sensitive(bool( self.objeto and len(self.objeto.get_balas_sin_albaran_interno()) > 0 and permiso and (permiso.nuevo or permiso.escritura))) else: self.wids['b_albaran'].set_sensitive( self.objeto and len(self.objeto.get_balas_sin_albaran_interno()) > 0)
def graficar_por_producto(self, partes): """ Construye un gráfico de rendimiento por producto. Agrupa los partes de un mismo producto y calcula su productividad conjunta. Esos datos los envía a un gráfico para ver los productos que mejor le van a la línea. """ vpro = VentanaProgreso(padre = self.wids['ventana']) vpro.mostrar() i = 0.0 tot = len(partes) productos = defaultdict(lambda: []) for pdp in partes: i += 1 vpro.set_valor(i / tot, "Analizando rendimiento por producto...") producto = pdp.productoVenta if not producto: txterr = "consulta_productividad.py::graficar_por_producto ->"\ " %s:%s sin producto de venta." % (pdp.puid, pdp.get_info()) myprint(txterr) self.logger.warning(txterr) else: productos[producto].append(pdp) data = OrderedDict() tot += len(productos) productos_ordenados = productos.keys() productos_ordenados.sort(key = lambda prod: prod.descripcion) for producto in productos_ordenados: i += 1 vpro.set_valor(i / tot, "Analizando rendimiento por producto...") pdps = productos[producto] data[producto.descripcion] = calcular_productividad_conjunta( tuple(pdps)) try: plot = GtkCairoPlot(HORIZONTAL, data, self.wids['ventana']) except ValueError: plot = gtk.Label("<big>No hay datos suficientes.</big>") plot.set_use_markup(True) parent = self.wids['grafico'].parent parent.add(plot) parent.remove(self.wids['grafico']) self.wids['grafico'] = plot parent.show_all() vpro.ocultar()
def get_str_estado(self, cache={}, fecha=mx.DateTime.today()): """ Si la factura está en el diccionario de caché recibido (por su puid) entonces no consulta el estado en la BD. """ ESTADOS = ("No documentada", "Documentada no vencida", "Impagada", "Cobrada", "Pendiente de abonar") try: estado = cache[self.puid] if VERBOSE: myprint("pclases.py::SuperFacturaVenta.get_str_estado -> HIT!") except KeyError: estado = self.get_estado(fecha) str_estado = ESTADOS[estado] return str_estado
def comparar_bool(self, col): try: valor_ventana = self.wids[col].get_active() except KeyError: txt_error = "empleados.py: No se pudo obtener el valor de la"\ " ventana para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_ventana = False try: valor_campo = self.objeto._SO_getValue(col) except KeyError: txt_error = "empleados.py: No se pudo obtener el valor del objeto"\ " para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_campo = False res = valor_ventana == valor_campo return res
def main(): # Si hay ficheros de estilo gtk, los cargo por orden: General de la # aplicación y específico del usuario en WIN y UNIX. Se machacan opciones # por ese orden. GTKRC2 = ".gtkrc-2.0" # Depende de la versión... GTKRC = "gtkrc" gtk.rc_parse(os.path.join( os.path.dirname(__file__), "..", GTKRC)) gtk.rc_parse(os.path.join( os.path.dirname(__file__),"..", GTKRC2)) # Si no existe se ignora # de manera silenciosa. if "HOME" in os.environ: gtk.rc_parse(os.path.join(os.environ["HOME"], GTKRC)) gtk.rc_parse(os.path.join(os.environ["HOME"], GTKRC2)) if "HOMEPATH" in os.environ: gtk.rc_parse(os.path.join(os.environ["HOMEPATH"], GTKRC)) gtk.rc_parse(os.path.join(os.environ["HOMEPATH"], GTKRC2)) # Ver http://www.pygtk.org/docs/pygtk/class-gtkrcstyle.html para la # referencia de estilos. Ejemplo: # bogado@cpus006:~/Geotexan/geotexinn02/formularios$ cat ../gtkrc # style 'blanco_y_negro' { bg[NORMAL] = '#FFFFFF' # fg[NORMAL] = '#000000' # base[NORMAL] = '#FFFFFF' # text[NORMAL] = '#000000' # } # class '*' style 'blanco_y_negro' ## user, passwd, modulo, clase, fconfig, verbose, debug, obj_puid = parse_params() #salida = MetaF() #sys.stdout = salida errores = MetaF() sys.stderr = errores m = Menu(user, passwd, fconfig) m.mostrar() if not errores.vacio() and not errores.contains( ["Logged from file menu.py", "Bad file descriptor"]): # Me quito de en medio los errores de volcado a log (IOError 9) que # aparecen a veces por... ¿Samba? ¿Clientes Windows? No lo sé. myprint("Se han detectado algunos errores en segundo plano durante " "la ejecución.") enviar_correo('Errores en segundo plano. La stderr contiene:\n%s' % (errores), m.get_usuario())
def abrir_ventana_modulo_python(self, archivo, clase): try: self.ventana.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) while gtk.events_pending(): gtk.main_iteration(False) # HACK: Debe haber una forma mejor de hacerlo. De momento me # aprovecho de que el mainloop no va a atender al # timeout aunque se cumpla el tiempo, ya que está # ocupado en abrir la ventana, con lo que el cursor # sale del "busy" justo cuando debe, al abrirse la # ventana. v = None gobject.timeout_add(5000, self.volver_a_cursor_original) # NOTA: OJO: TODO: Usuario harcoded. Cambiar en cuanto sea # posible. if ((self.get_usuario().usuario == "geotextil" or self.get_usuario().usuario == "fibra" or self.get_usuario().nivel >= 3) and "partes_de_fabricacion" in archivo and self.get_usuario().usuario != "cemento"): exec "import %s" % archivo v = eval('%s.%s' % (archivo, clase)) try: v(permisos = "rx", usuario = self.get_usuario()) except TypeError: # La ventana no soporta el modelo # antiguo de permisos. v(usuario = self.get_usuario()) #v.wids['ventana'].set_icon_from_filename(icowindow) else: try: #raise NotImplementedError, \ # "Lanzador multiproceso en desarrollo..." self.lanzar_ventana(archivo, clase) except Exception, e: myprint(e) sys.stderr.write(`e`) self._lanzar_ventana(archivo, clase) except: self.ventana.window.set_cursor(None) utils.escribir_barra_estado(self.statusbar, "Error detectado. Iniciando informe por correo.", self.logger, self.usuario.usuario) self.enviar_correo_error_ventana()
def comparar_ajena(self, col): try: valor_ventana = utils.combo_get_value(self.wids[col]) except KeyError: txt_error = "empleados.py: No se pudo obtener el valor de la"\ " ventana para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_ventana = None try: valor_campo = self.objeto._SO_getValue(col) # Es un ID -es decir, # un entero-, no un objeto sqlobject. except KeyError: txt_error = "empleados.py: No se pudo obtener el valor del objeto"\ " para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_campo = None res = valor_ventana == valor_campo return res
def comparar_string(self, col): res = False # @UnusedVariable try: valor_ventana = self.wids[col].get_text() except KeyError: txt_error = "empleados.py: No se pudo obtener el valor de la"\ " ventana para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_ventana = "" try: valor_campo = self.objeto._SO_getValue(col) except KeyError: txt_error = "empleados.py: No se pudo obtener el valor del"\ " objeto para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_campo = "" res = valor_ventana == valor_campo return res
def get_valor(self, w, nombrecampo, tipocampo): res = None if isinstance(tipocampo, pclases.SOStringCol): # Cadena: el widget es # un entry res = w.get_text() elif isinstance(tipocampo, pclases.SOIntCol): # Entero: el widget es # un entry res = w.get_text() try: res = int(res) except ValueError: txt = "El valor «%s» no es correcto. Introduzca un número"\ " entero." % (res) utils.dialogo_info(titulo="ERROR DE FORMATO", texto=txt, padre=self.wids['ventana']) res = 0 elif isinstance(tipocampo, pclases.SOBoolCol): # Boolean: el widget es un checkbox res = w.get_active() elif isinstance(tipocampo, pclases.SOForeignKey): # Entero-clave ajena: el widget es un comboboxentry res = utils.combo_get_value(w) elif isinstance(tipocampo, pclases.SOCol): # Clase base, casi seguro Float: el widget es un entry res = w.get_text() try: res = utils._float(res) except ValueError: txt = "El valor «%s» no es correcto. Introduzca un número." % ( res) utils.dialogo_info(titulo="ERROR DE FORMATO", texto=txt, padre=self.wids['ventana']) res = 0.0 else: txt = "empleados.py: No se pudo obtener el valor de %s para "\ "%s <%s>." % (w, nombrecampo, tipocampo) myprint(txt) self.logger.error(txt) return res
def comparar_int(self, col): try: valor_ventana = self.wids[col].get_text() except KeyError: txt_error = "empleados.py: No se pudo obtener el valor de la "\ "ventana para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_ventana = "" try: valor_ventana = int(valor_ventana) except ValueError: txt_error = "empleados.py: No se pudo convertir %s a entero." % col myprint(txt_error) self.logger.error(txt_error) valor_ventana = 0 try: valor_campo = self.objeto._SO_getValue(col) except KeyError: txt_error = "empleados.py: No se pudo obtener el valor del"\ " objeto para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_campo = 0 res = valor_ventana == valor_campo return res
def comparar_float(self, col): res = 0.0 try: valor_ventana = self.wids[col].get_text() except KeyError: txt_error = "empleados.py: No se pudo obtener el valor de la "\ "ventana para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_ventana = 0.0 try: valor_ventana = utils.parse_float(valor_ventana) except ValueError: txt_error = "empleados.py: No se pudo convertir %s(%s) a "\ "float." % (col, valor_ventana) myprint(txt_error) self.logger.error(txt_error) valor_ventana = 0.0 try: valor_campo = self.objeto._SO_getValue(col) if not isinstance(valor_campo, type(0.0)): # Porque es posible # que el SOCol no contenga un float. El SOCol es la clase # base. Se asume flotante por eliminación. O, escucha que te # diga, o que sea un decimal.Decimal try: valor_campo = utils._float(valor_campo) except TypeError: res = False res = valor_ventana == valor_campo except KeyError: txt_error = "empleados.py: No se pudo obtener el valor del "\ "objeto para %s." % col myprint(txt_error) self.logger.error(txt_error) valor_campo = 0.0 return res
def rellenar_widgets(self): """ Redimensiona «t» y crea dentro los widgets necesarios para mostrar y editar los campos de «objeto». """ if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> Ready...") import time antes = time.time() # HACK: Es para evitar el campo precio hora extra, que ya no se usa. # Nómina ahora es el sueldo base a sumar al cálculo de la nomina # mensual. d = {} if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> Steady...", time.time() - antes) antes = time.time() for c in self.objeto.sqlmeta.columns: if c != 'preciohora': d[c] = self.objeto.sqlmeta.columns[c] self.objeto.sqlmeta.columns = d # END OF HACK if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> GO! ", time.time() - antes) antes = time.time() numcampos = len(self.objeto.sqlmeta.columns) if numcampos % 2 != 0: numwidgets = numcampos + 1 else: numwidgets = numcampos for child in self.wids['t'].get_children(): child.destroy() if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> ", time.time() - antes) antes = time.time() self.wids['t'].resize(numwidgets / 2, 4) icol = 0 irow = 0 if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> ", time.time() - antes) antes = time.time() for col in self.objeto.sqlmeta.columns: if not isinstance(self.objeto.sqlmeta.columns[col], pclases.SOBoolCol): # Los checkboxes llevan su propio label. label = self.build_label(col) self.wids['t'].attach(label, icol, icol+1, irow, irow+1) icol += 1 child = self.build_child(col, self.objeto.sqlmeta.columns[col]) self.set_valor(child, col, self.objeto.sqlmeta.columns[col]) self.wids['t'].attach(child, icol, icol+1, irow, irow+1) icol += 1 if icol == 4: icol = 0 irow += 1 if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> ", time.time() - antes) antes = time.time() self.wids['t'].show_all() self.objeto.make_swap() # Añadido: Si el empleado no tiene alta como trabajador, deshabilito # el botón de permisos. self.wids['b_ausencias'].set_sensitive(self.objeto.activo) self.wids['b_categoria'].set_sensitive( self.objeto.categoriaLaboral != None) if pclases.DEBUG: myprint("empleados.py::rellenar_widgets -> WINTER!", time.time() - antes) antes = time.time()
def rellenar_tabla(self, partes, solobalas): """ Rellena el model con los partes rescatados de la BD en `buscar`. PRERREQUISITO: Los partes vienen en una lista y deben estar ordenados por fecha y hora de inicio. """ # Lo primero de todo. Si está activado el DEBUG, muestro las columnas. tv = self.wids['tv_datos'] for ncol in range(7, len(tv.get_columns())): tv.get_column(ncol).set_property("visible", pclases.DEBUG) self.huecos = [] # Lista de los últimos partes tratados por línea. self.incidencias_horaini = [] self.incidencias_horafin = [] self.tiempo_faltante = mx.DateTime.TimeDeltaFrom(0) vpro = VentanaProgreso(padre = self.wids['ventana']) model = self.wids['tv_datos'].get_model() model.clear() total = 0 kilosproducidos = 0 kilosgranza = 0 vector = [] tot = len(partes) i = 0.0 vpro.mostrar() e_personasturno = 0 tiempototal = 0 tiempo_sin_incidencias = 0 # Por si inicia una consulta sin resultados metrosproducidos = 0 kilosconsumidos = kilosconsumidos_partidas_completas = 0 tiempototaltotal = mx.DateTime.DateTimeDelta(0) #selffin = utils.parse_fecha('/'.join(self.fin.split('/')[::-1])) selffin = self.fin # ... WTF? Esto está hecho como el culo. ¡REFACTORIZAR! # XXX Primer intento de acelerar los treeview self.wids['tv_datos'].freeze_child_notify() self.wids['tv_datos'].set_model(None) # XXX personashora = [] dia_actual = "" # Estos tres parámetros son para medir la # producción y productividad por día. produccion_actual = 0.0 #productividad_actual = 0.0 t_teorico_por_dia = 0 t_real_por_dia = 0 pdps_dia = [] # Ojo. Los partes deben venir ordenados para que # esta lista solo contenga los partes del día tratado en la # iteración por la que vaya. Se limpia al cambiar de un día al # siguiente mientras relleno los datos. #nodos_hijos_por_dia = 0 balas_consumidas = [] # Lista con las balas consumidas por # todos los partes, así evito contar la misma dos veces si en dos # partes se ha usado la misma partida o dos partidas de la misma # partida de carga. padre = None # En una de las iteraciones se espera que padre se haya # instanciado en el final de la anterior. Inicializo para evitar # errores y errores en análisis sintáctico de algunos IDE en la # línea 342. for pdp in partes: if pdp.se_solapa(): texto_warning = "%sconsulta_productividad::rellenar_tabla"\ " -> El parte ID %d se solapa con otros de la misma línea"\ ". Si estaba verificado, lo desbloqueo para que se vuelva"\ " a revisar y lo ignoro para el cálculo actual." % ( self.usuario and self.usuario.usuario + ": " or "", pdp.id) self.logger.warning(texto_warning) myprint(texto_warning) pdp.bloqueado = False continue vpro.set_valor(i/tot, 'Añadiendo parte %s...' % ( utils.str_fecha(pdp.fecha))) delta_entre_partes, parte_anterior = detectar_hueco(pdp, self.huecos) if delta_entre_partes: self.marcar_hueco(parte_anterior, pdp) self.tiempo_faltante += delta_entre_partes kilosgranza_del_parte = 0.0 (tiempototal, tiempo_sin_incidencias) = self.calcular_tiempo_trabajado(pdp) tiempototaltotal += tiempototal #productividad = pdp.calcular_productividad() # CWT: Ahora no se calcula así, sino como el antiguo rendimiento. productividad = pdp.calcular_rendimiento() # Tratamiento especial para partes de balas try: str_producto = pdp.productoVenta.descripcion except AttributeError: str_producto = "Sin producción" if (self.wids['r_balas'].get_active() or self.wids['r_ambos'].get_active()): # Sólo balas o todos. if pdp.es_de_fibra(): balas = [a.bala for a in pdp.articulos if a.balaID != None] bigbags = [a.bigbag for a in pdp.articulos if a.bigbagID != None] for b in balas: kilosproducidos += b.pesobala for b in bigbags: kilosproducidos += b.pesobigbag kilosgranza_del_parte = pdp.get_granza_consumida() kilosgranza += kilosgranza_del_parte if pdp.get_produccion()[0] - kilosgranza_del_parte > 1000: # XXX DEBUG self.logger.warning("El parte ID %d (%s) ha consumido " "(%s) mucha menos granza que fibra ha " "producido (%s) !!!" % (pdp.id, utils.str_fecha(pdp.fecha), utils.float2str(kilosgranza_del_parte), utils.float2str(pdp.get_produccion()[0]))) # XXX DEBUG if (self.wids['r_cemento'].get_active() or self.wids['r_ambos'].get_active()): kilosproducidos += pdp.get_produccion()[0] kilosconsumidos += sum([bb.pesobigbag for bb in pdp.bigbags]) if (self.wids['r_rollos'].get_active() or self.wids['r_ambos'].get_active()): # Sólo rollos o todos. if pdp.es_de_geotextiles(): superficies_defectuosos = [a.superficie for a in pdp.articulos if a.es_rollo_defectuoso()] metrosproducidos += sum(superficies_defectuosos) rollos = [a.rollo for a in pdp.articulos if a.rollo != None] if len(rollos) > 0: # Para que no intente hacer el # cálculo con partes que tengan balas y/o no rollos. pv = pdp.articulos[0].productoVenta cer = pv.camposEspecificosRollo metrosproducidos += (len(rollos) * cer.metros_cuadrados) if pdp.articulos[0].es_rollo(): partida = pdp.articulos[0].rollo.partida elif pdp.articulos[0].es_rollo_defectuoso(): partida = pdp.articulos[0].rolloDefectuoso.partida else: partida = None if partida != None: contar_kilosconsums_partidas_completas \ = partida.entra_en_cota_superior(selffin) for b in partida.balas: if b not in balas_consumidas: # Evito contar # la misma bala dos veces. bpesobala = b.pesobala kilosconsumidos += bpesobala if contar_kilosconsums_partidas_completas: kilosconsumidos_partidas_completas \ += bpesobala balas_consumidas.append(b) empleados = len(pdp.horasTrabajadas) e_personasturno += empleados tiempoparte = pdp.get_duracion().hours if tiempoparte > 0: personashora.append(empleados/tiempoparte) else: personashora.append(0.0) vector.append(productividad) produccion = pdp.get_produccion() # Resúmenes por día como nodos padre del TreeView. if not dia_actual: pdps_dia.append(pdp) if dia_actual == utils.str_fecha(pdp.fecha): pdps_dia.append(pdp) else: if dia_actual != "": # Actualizo el padre if not self.wids['r_ambos'].get_active(): model[padre][4] = "%s %s" % ( utils.float2str(produccion_actual), produccion[1]) else: # Evito mezclar kilos con metros model[padre][4] = "-" productividad_actual = calcular_productividad_conjunta( tuple(pdps_dia)) model[padre][5] = "%s %%" % ( utils.float2str(productividad_actual)) model[padre][6] = min(productividad_actual, 100) model[padre][7] = t_teorico_por_dia model[padre][8] = t_real_por_dia pdps_dia = [pdp] # He cambiado de día. Limpio. t_teorico_por_dia = 0 t_real_por_dia = 0 dia_actual = utils.str_fecha(pdp.fecha) produccion_actual = 0.0 productividad_actual = 0.0 #nodos_hijos_por_dia = 0 padre = model.append(None, (dia_actual, "", "", "", "", "%s %%" % (utils.float2str(productividad_actual)), min(productividad_actual, 100), "", # t. teórico "", # t. real 0)) t_teorico_pdp = pdp.calcular_tiempo_teorico() t_teorico_por_dia += t_teorico_pdp t_real_pdp = pdp.calcular_tiempo_real() t_real_por_dia += t_real_pdp model.append(padre, (utils.str_fecha(pdp.fecha), str(pdp.horainicio)[:5], str(pdp.horafin)[:5], str_producto, "%s %s" % (utils.float2str(produccion[0]), produccion[1]), "%s %%" % (utils.float2str(productividad)), min(productividad, 100), t_teorico_pdp, t_real_pdp, pdp.id)) produccion_actual += produccion[0] i+=1 # Actualizo el padre de los últimos nodos: if dia_actual != "": # Actualizo el padre if not self.wids['r_ambos'].get_active(): model[padre][4] = "%s %s" % ( utils.float2str(produccion_actual), produccion[1]) else: # Evito mezclar kilos con metros model[padre][4] = "-" productividad_actual = calcular_productividad_conjunta( tuple(pdps_dia)) model[padre][5] = "%s %%" % (utils.float2str(productividad_actual)) model[padre][6] = min(productividad_actual, 100) # Campos de depuración model[padre][7] = t_teorico_por_dia model[padre][8] = t_real_por_dia vpro.ocultar() # XXX Primer intento de acelerar los treeview self.wids['tv_datos'].set_model(model) self.wids['tv_datos'].thaw_child_notify() # XXX if partes != []: total = calcular_productividad_conjunta(tuple(partes)) # Campos especiales de "Sólo balas" if self.wids['r_balas'].get_active(): self.wids['label7'].set_text('Kilos producidos:') self.wids['label8'].set_text('Kilos granza consumidos:') self.wids['label9'].set_text('Kilos / Hora producción:') self.wids['e_kilosproducidos'].set_text( "%s kg" % (utils.float2str(kilosproducidos))) self.wids['e_kilosgranza'].set_text( "%s kg" % (utils.float2str(kilosgranza))) try: e_kiloshora = (kilosproducidos /float(tiempototaltotal.hours)) except ZeroDivisionError: self.logger.warning( "consulta_productividad.py::rellenar_tabla" " -> tiempototaltotal = 0") e_kiloshora = 0 # Cero por poner algo. Porque un parte # de tiempo transcurrido=0... se las trae. self.wids['e_kiloshora'].set_text("%s kg/h" % ( utils.float2str(e_kiloshora))) elif self.wids['r_rollos'].get_active(): tips = gtk.Tooltips() tips.set_tip(self.wids['e_kilosgranza'], "Kilogramos consumidos contando partidas " "completas por defecto y por exceso.\n(En un caso" " se cuentan solo partidas de carga cuyas " "partidas de geotextiles se hayan completado " "estrictamente antes o en la cota superior.\n" "En el otro, por exceso, se contabilizan " "partidas completas aunque parte de su " "producción se salga del rango de fechas.\n" "En ambos casos el límite inferior es flexible " "-por compensación-.)") tips.enable() self.wids['label7'].set_text('m² producidos:') self.wids['label8'].set_text('Kg fibra consumidos:') self.wids['label9'].set_text('m²/h producción:') try: e_kiloshora = (metrosproducidos/float(tiempototaltotal.hours)) except ZeroDivisionError: self.logger.warning( "consulta_productividad.py::rellenar_tabla -> " "tiempototaltotal = 0") e_kiloshora = 0 self.wids['e_kilosproducidos'].set_text("%s m²" % ( utils.float2str(metrosproducidos))) if kilosconsumidos_partidas_completas != kilosconsumidos: self.wids['e_kilosgranza'].set_text("%s kg ~ %s kg" % ( utils.float2str(kilosconsumidos_partidas_completas), utils.float2str(kilosconsumidos))) else: self.wids['e_kilosgranza'].set_text("%s kg" % ( utils.float2str(kilosconsumidos))) self.wids['e_kiloshora'].set_text("%s m²/h" % ( utils.float2str(e_kiloshora))) else: self.wids['label7'].set_text('Producido:') self.wids['label8'].set_text('Consumido:') self.wids['label9'].set_text('Producción combinada a la hora:') try: e_kiloshora = (kilosproducidos/float(tiempototaltotal.hours)) metros_hora = (metrosproducidos/float(tiempototaltotal.hours)) except ZeroDivisionError: self.logger.warning("consulta_productividad.py::" "rellenar_tabla -> tiempototaltotal = 0") e_kiloshora = 0 metros_hora = 0 self.wids['e_kilosproducidos'].set_text("%s m²; %s kg" % ( utils.float2str(metrosproducidos), utils.float2str(kilosproducidos))) self.wids['e_kilosgranza'].set_text( "%s kg fibra; %s kg granza" % (utils.float2str( kilosconsumidos), utils.float2str(kilosgranza))) self.wids['e_kiloshora'].set_text("%s m²/h; %s kg/h" % ( utils.float2str(metros_hora), utils.float2str(e_kiloshora))) # Fin campos especiales de "Sólo balas" self.wids['curva'].set_range(0, len(vector), 0, 100) self.wids['curva'].set_vector(vector) self.wids['curva'].set_curve_type(gtk.CURVE_TYPE_SPLINE) # self.wids['curva'].set_curve_type(gtk.CURVE_TYPE_LINEAR) self.wids['pb'].set_fraction(max(min(total, 100), 0) / 100.0) else: self.wids['curva'].set_range(0, 2, 0, 100) self.wids['curva'].set_vector((0, 0)) self.wids['pb'].set_fraction(1 / 100.0) self.wids['e_kilosproducidos'].set_text('') self.wids['e_kilosgranza'].set_text('') self.wids['e_kiloshora'].set_text('') self.wids['e_total'].set_text("%s %%" % (utils.float2str(total))) try: personashora = sum(personashora)/len(personashora) except ZeroDivisionError: personashora = 0 try: e_personasturno = float(e_personasturno) / len(partes) except ZeroDivisionError: e_personasturno = 0 self.wids['e_personasturno'].set_text("%s (%s personas/h)" % ( utils.float2str(e_personasturno), utils.float2str(personashora))) self.wids['e_tiempo_faltante'].set_text( self.tiempo_faltante and (str_horas(self.tiempo_faltante) + " h") or "") self.wids['e_tiempo_partes'].set_text( str_horas(tiempototaltotal) + " h")
def rellenar_tabla(self, partidas_carga): """ Rellena el model con los items de la consulta. partidas_carga es un diccionario... (mirar abajo, en "buscar"). """ model = self.wids['tv_datos'].get_model() model.clear() datachart = [] for pc in partidas_carga: abuelo = model.append(None, (pc.codigo, utils.float2str(partidas_carga[pc]['kilos_consumidos']), utils.float2str(partidas_carga[pc]['kilos_producidos']), utils.float2str(partidas_carga[pc]['kilos_teoricos']), str(partidas_carga[pc]['balas']), str(partidas_carga[pc]['rollos']), utils.float2str(partidas_carga[pc]['metros']), pc.id)) # Actualizo totales conforme relleno el _model_ pcpc = partidas_carga[pc] self.totales['e_total_kg_consumidos'] += pcpc['kilos_consumidos'] self.totales['e_total_kg_prod_real'] += pcpc['kilos_producidos'] self.totales['e_total_kg_prod_teorico'] += pcpc['kilos_teoricos'] self.totales['e_total_balas_consumidas'] += pcpc['balas'] self.totales['e_total_rollos_producidos'] += pcpc['rollos'] self.totales['e_total_m2_producidos'] += pcpc['metros'] # Y sigo con el gráfico y tal datachart.append([pc.codigo, partidas_carga[pc]['kilos_consumidos']]) padre = model.append(abuelo, ("Partidas de geotextiles producidas", '', '', '', '', '', '', '')) for pgtx in partidas_carga[pc]['partidas']: model.append(padre, ("%s: %s" % (pgtx['código'], pgtx['producto']), "", utils.float2str(pgtx['kilos']), utils.float2str(pgtx['kilos_teoricos']), "", str(pgtx['rollos']), utils.float2str(pgtx['metros']), '')) padre = model.append(abuelo, ("Lotes de fibra consumidos", '', '', '', '', '', '', '')) for lote in partidas_carga[pc]['lotes']: model.append(padre, ("%s: %s" % (lote['código'], lote['producto']), utils.float2str(lote['kilos']), '', '', str(lote['balas']), '', '', '', )) # Y ahora la gráfica. from lib 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() self.wids['eventbox_chart'].add(chart) datachart.sort(lambda fila1, fila2: (fila1[0] < fila2[0] and -1) or (fila1[0] > fila2[0] and 1) or 0) for data in datachart: data.append(6) # Barras de color rojo. chart.plot(datachart) self.wids['eventbox_chart'].show_all() except Exception, msg: txt = "consumo_fibra_por_partida_gtx.py::rellenar_tabla -> "\ "Error al dibujar gráfica (charting): %s" % msg myprint(txt) self.logger.error(txt)
# 1.9b: Cuando cierre "todas" las incidencias, bugs y etiquetas TO-DO|FIX-ME. # (versión estable) # 1.9.impar : Inestables. # 1.9.par : Estables. # 2.0rc: Versión 2.0 release candidate coincidiendo con la puesta en # producción. # 2.0: Versión 2.0 cuando acabe fase de pruebas. # 2.0.1: Tal vez debería hacer un fork para nuevas funcionalidades generales # (tickets, IRPF...) mientras congelo la RC, pero prefiero una sola # rama de código, que los merge de CVS son muy duros. # os.environ['LANG'] = "es_ES" # os.environ['LANGUAGE'] = 'es_ES' # os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) if pclases.DEBUG: myprint(os.environ) myprint(os.getcwd()) myprint(os.path.realpath(sys.argv[0])) class MetaF: """ "Metafichero" para almacenar la salida de errores y poder enviar informes de error. """ def __init__(self): self.t = '' def write(self, t): errores_a_ignorar = ("GtkWarning", "with become", "PangoWarning", "main loop already active",
def calcular_total(self, iva=True, redondeo=2): """ Calcula el total de la factura, con descuentos, IVA y demás incluido. Devuelve un FixedPoint (a casi todos los efectos, se comporta como un FLOAT. De todas formas, pasa bien por el utils.float2str). """ subtotal = self.calcular_subtotal() tot_dto = self.calcular_total_descuento(subtotal) abonos = sum([pa.importe for pa in self.pagosDeAbono]) if iva: tot_iva = self.calcular_total_iva(subtotal, tot_dto, self.cargo, abonos) else: tot_iva = 0.0 irpf = self.irpf * subtotal if redondeo is False: # No redondea ANTES de hacer la suma. Pero el # resultado, al implicar un FixedPoint que viene del IVA, será # también un FixedPoint de 2 decimales. total = (subtotal + float(self.cargo) # Porque es de tipo Decimal + tot_dto + tot_iva + abonos + irpf) else: subtotales = (subtotal, float(self.cargo), tot_dto, tot_iva, abonos, irpf) if DEBUG: myprint("pclases.py::SuperFacturaVenta.calcular_total " "(antes de redondeo) -> ", subtotales) if redondeo == 2: # El viejo truco, que no por viejo es impreciso. Por ejemplo: # In [33]: nums= (13425.705, 0.705, 1.705, 2.705, 3.705, 5.705, 425.705) # In [34]: for n in nums: # ....: print round(n, 2), myround(n) # ....: # 13425.7 13425.71 # 0.7 0.71 # 1.71 1.71 # 2.71 2.71 # 3.71 3.71 # 5.71 5.71 # 425.7 425.71 # myround = lambda x: int((float(x) + 0.005) * 100) / 100.0 from formularios.utils import myround # testcase # i = (1.0, 1.001, 1.004, 1.049, 1.005, 1.006, 1.009, 1.0091, 1.949, 1.985, 1.994, 1.995, 2.0, 2.001, 2.675) # o = (1.0, 1.0, 1.0, 1.05, 1.01, 1.01, 1.01, 1.01, 1.95, 1.99, 1.99, 2.0, 2.0, 2.0, 2.68) # for x, y in zip(i, o): # print x, "->", myround(x), "=", y # assert myround(x) == y subtotales = map(lambda x: myround(x), subtotales) else: subtotales = map(lambda x: round(round(x, 6), redondeo), subtotales) # La ley, que no es muy precisa, viene a decir que los cálculos # internos de precios y tal se hagan a 6 decimales los totales a 2. # También dice que TOTAL=BASE IMPONIBLE+IVA. Se supone que ya todo # bien redondeado al céntimo. El atajo de BI*1.21 es incorrecto. # Premature optimization is the root of all Evil, el demonio está # en los pequeños detalles y todas esas cosas se me han venido # encima de golpe. # El doble redondeo es por un caso curiosísimo: # Por calculadora, 0.6275*26730=16773.075. Sin embargo... # In [30]: 0.6275*26730 # Out[30]: 16773.074999999997 # In [35]: round(0.6275*26730, 2) # Out[35]: 16773.07 # In [36]: round(round(0.6275*26730, 3), 2) # Out[36]: 16773.08 # Al redondear a 2 decimales debería dar 16773.08 (0.75 -> 0.8) # Esto ya es conocido. Ver # https://docs.python.org/2/library/functions.html#round if DEBUG: myprint("pclases.py::SuperFacturaVenta.calcular_total " "(tras redondeo) -> ", subtotales) total = sum(subtotales) return total
def buscar(self, boton): """ Dadas fecha de inicio y de fin, busca los partes de producción de geotextiles entre esas fechas. Para cada parte de producción extrae las partidas de geotextiles fabricadas y las agrupa por partida de carga. Finalmente, de cada partida de carga cuenta los kilogramos de fibra y número de balas en ella. También divide las balas por lotes y cuenta los kg y bultos de cada lote que ha entrado en cada carga de cuarto. OJO: El criterio finalmente queda: en cada mes se cuenta el consumo y la producción de las partidas cuyas partidas de carga hayan sido terminadas. Una partida que comienza en un fin de mes (por ejemplo) y acaba en el mes siguiente (cualquier fecha, en general) cuenta para el mes siguiente. Esto es así porque hay que tomar las partidas de carga como una unidad, como un todo, si se quiere discernir bien los kg producidos y consumidos, ya que no hay relación directa entre rollo y bala consumida (se pierde definición al haber una tabla de una relación muchos a muchos entre medio). """ self.inicio = utils.parse_fecha(self.wids['e_fechainicio'].get_text()) self.fin = utils.parse_fecha(self.wids['e_fechafin'].get_text()) self.totales = {'e_total_kg_consumidos': 0.0, 'e_total_kg_prod_real': 0.0, 'e_total_kg_prod_teorico': 0.0, 'e_total_balas_consumidas': 0, 'e_total_rollos_producidos': 0, 'e_total_m2_producidos': 0.0} PDP = pclases.ParteDeProduccion if not self.inicio: pdps = PDP.select(""" fecha < '%s' AND observaciones NOT LIKE '%%;%%;%%;%%;%%;%%' """ % ( self.fin.strftime('%Y-%m-%d'))) else: pdps = PDP.select(""" fecha >= '%s' AND fecha < '%s' AND observaciones NOT LIKE '%%;%%;%%;%%;%%;%%' """ % ( self.inicio.strftime('%Y-%m-%d'), self.fin.strftime('%Y-%m-%d'))) vpro = ventana_progreso.VentanaProgreso(padre = self.wids['ventana']) tot = pdps.count() pdps = pdps.orderBy("fecha") i = 0.0 vpro.mostrar() partidas_carga = {} # "Constantes" (nótense las comillas). PESOREAL = 1 PESOTEORICO = 2 PESOSIN = 3 if self.wids['rb_pesoreal'].get_active(): metodo = PESOREAL elif self.wids['rb_teorico'].get_active(): metodo = PESOTEORICO elif self.wids['rb_pesosin'].get_active(): metodo = PESOSIN else: txt = "consumo_fibra_por_partida_gtx.py::buscar -> ¡NO HAY SELEC"\ "CIONADO NINGÚN RADIOBUTTON DEL GRUPO!" self.logger.error(txt) myprint(txt) return for pdp in pdps: vpro.set_valor(i/tot, 'Analizando producción %s...' % ( utils.str_fecha(pdp.fecha))) if pdp.articulos: articulo = pdp.articulos[0] if articulo.es_rollo(): partida = articulo.rollo.partida elif articulo.es_rollo_defectuoso(): partida = articulo.rolloDefectuoso.partida else: partida = None if partida and partida.entra_en_cota_superior(self.fin): partida_carga = partida.partidaCarga if partida_carga == None: # Partidas sin partida de carga # ... eso es malo. txt = "consumo_fibra_por_partida_gtx.py::buscar -> ¡P"\ "artida %s no tiene partida de carga!" % ( partida.get_info()) myprint(txt) self.logger.error("%s%s" % ( self.usuario and self.usuario.usuario + ": " or "", txt)) else: if partida_carga not in partidas_carga: # Aquí ya se # asegura que no se contará dos veces la misma # bala -partida de carga-. partidas_carga[partida_carga] = { 'partidas': [], 'lotes': [], 'kilos_consumidos': 0.0, 'balas': 0, 'kilos_producidos': 0.0, 'kilos_teoricos': 0.0, 'rollos': 0, 'metros': 0.0, } for partida in partida_carga.partidas: if metodo == PESOSIN: kilos_producidos = partida.get_kilos() elif metodo == PESOTEORICO: kilos_producidos \ = partida.get_kilos_teorico() elif metodo == PESOREAL: kilos_producidos \ = partida.get_kilos_totales() else: txt = "consumo_fibra_por_partida_gtx.py "\ "-> No se pudo determinar el métod"\ "o de cálculo. Se usará el peso te"\ "órico." self.logger.warning(txt) myprint(txt) kilos_producidos \ = partida.get_kilos_teorico() kilos_teoricos = partida.get_kilos_teorico() metros_producidos = partida.get_metros() producto = partida.get_producto() partidas_carga[partida_carga]\ ['partidas'].append( {'código': partida.codigo, 'kilos': kilos_producidos, 'kilos_teoricos': kilos_teoricos, 'metros': metros_producidos, 'rollos': len(partida.rollos), 'producto': producto and producto.descripcion or "SIN PRODUCCIÓN" }) partidas_carga[partida_carga]\ ['kilos_producidos'] += kilos_producidos partidas_carga[partida_carga]\ ['kilos_teoricos'] += kilos_teoricos partidas_carga[partida_carga]\ ['rollos'] += len(partida.rollos) partidas_carga[partida_carga]\ ['metros'] += metros_producidos lotes = partida_carga.get_lotes() for lote in lotes: balas = pclases.Bala.select(pclases.AND( pclases.Bala.q.loteID == lote.id, pclases.Bala.q.partidaCargaID == partida_carga.id)) kilos_consumidos = sum( [b.pesobala for b in balas]) partidas_carga[partida_carga]['lotes'].append( {'código': lote.codigo, 'kilos': kilos_consumidos, 'balas': balas.count(), 'producto': balas[0].articulo.productoVenta.descripcion }) # NOTA: el get_lotes() ya asegura que al # menos hay una bala en la consulta "balas" partidas_carga[partida_carga]\ ['kilos_consumidos'] += kilos_consumidos partidas_carga[partida_carga]\ ['balas'] += balas.count() i += 1 vpro.ocultar() self.partidas_carga = partidas_carga self.rellenar_tabla(partidas_carga) self.rellenar_totales()
def build_child(self, nombrecampo, tipocampo): """ Construye el widget correspondiente al tipo de campo recibido y establece su valor por defecto. """ res = gtk.Label('ERROR: N/A') if isinstance(tipocampo, pclases.SOStringCol): # Cadena: el widget es # un entry res = gtk.Entry() if (tipocampo.default is not None and tipocampo.default != sqlobject.sqlbuilder.NoDefault): res.set_text("%s" % (tipocampo.default)) elif isinstance(tipocampo, pclases.SOIntCol): # Entero: el widget es # un entry res = gtk.Entry() if (tipocampo.default is not None and tipocampo.default != sqlobject.sqlbuilder.NoDefault): res.set_text("%s" % (tipocampo.default)) elif isinstance(tipocampo, pclases.SOBoolCol): # Boolean: el widget es # un checkbox label = self.build_label(nombrecampo) res = gtk.CheckButton(label=label.get_text()) if tipocampo.default: res.set_active(True) elif isinstance(tipocampo, pclases.SOForeignKey): # Entero-clave # ajena: el widget es un comboboxentry res = gtk.ComboBoxEntry() ajena = tipocampo.foreignKey clase = getattr(pclases, ajena) COLUMNATEXTO = 'nombre' # Cambiar si no tiene una columna # "nombre" try: if clase == pclases.CategoriaLaboral: # HACK: Las categorías laborales ahora van con fechas y es # una movida. contenido = [(r.id, # r._SO_getValue(COLUMNATEXTO)) r.get_info()) for r in clase.select(orderBy='id') if not r.fecha] else: contenido = [(r.id, r._SO_getValue(COLUMNATEXTO)) for r in clase.select(orderBy='id')] except KeyError: COLUMNATEXTO = 'puesto' # Cambiar si no tiene una # columna "puesto" contenido = [(r.id, r._SO_getValue(COLUMNATEXTO)) for r in clase.select(orderBy='id')] utils.rellenar_lista(res, contenido) elif isinstance(tipocampo, pclases.SOCol): # Clase base, casi # seguro Float: el widget es un entry res = gtk.Entry() if (tipocampo.default is not None and tipocampo.default != sqlobject.sqlbuilder.NoDefault): res.set_text(utils.float2str("%s" % tipocampo.default)) else: txt = "empleados.py: No se pudo construir el widget para %s." % ( nombrecampo) myprint(txt) self.logger.error(txt) res.set_name(nombrecampo) self.wids[nombrecampo] = res return res
def create_menu(self): model = gtk.ListStore(str, gtk.gdk.Pixbuf) modulos = {} usuario = self.get_usuario() if pclases.VERBOSE: myprint("Analizando permisos (1/2)...") if pclases.VERBOSE: i = 0 tot = pclases.Modulo.select().count() for m in pclases.Modulo.select(orderBy = "nombre"): if pclases.VERBOSE: i += 1 myprint("Analizando permisos (1/2)... (%d/%d)" % (i, tot)) modulos[m] = [] if pclases.VERBOSE: i = 0 tot = pclases.Permiso.select( pclases.Permiso.q.usuarioID == usuario.id).count() if pclases.VERBOSE: myprint("Analizando permisos (2/2)...") for permusu in usuario.permisos: if pclases.VERBOSE: i += 1 myprint("Analizando permisos (2/2)... (%d/%d)" % (i, tot)) if permusu.permiso: v = permusu.ventana m = v.modulo if m != None: modulos[m].append(v) modulos_sorted = modulos.keys() def fsortalfabeticamente(m1, m2): if m1.nombre < m2.nombre: return -1 if m1.nombre > m2.nombre: return 1 return 0 modulos_sorted.sort(fsortalfabeticamente) for modulo in modulos_sorted: if modulos[modulo]: fichicono = os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', 'imagenes', modulo.icono) pixbuf = gtk.gdk.pixbuf_new_from_file(fichicono) model.append([modulo.nombre, pixbuf]) # Módulo favoritos pixbuf = gtk.gdk.pixbuf_new_from_file( os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', 'imagenes', "favoritos.png")) iterfav = model.append(("Favoritos", pixbuf)) contenedor = gtk.ScrolledWindow() icon_view = gtk.IconView(model) icon_view.set_text_column(0) icon_view.set_pixbuf_column(1) icon_view.set_orientation(gtk.ORIENTATION_VERTICAL) icon_view.set_selection_mode(gtk.SELECTION_SINGLE) icon_view.connect('selection-changed', self.on_select, model) icon_view.set_columns(1) icon_view.set_item_width(110) icon_view.set_size_request(140, -1) contenedor.add(icon_view) self.content_box = gtk.HBox(False) self.content_box.pack_start(contenedor, fill=True, expand=False) #icon_view.select_path((0,)) icon_view.select_path(model.get_path(iterfav)) # Al seleccionar una categoría se creará el frame # Sanity check. if hasattr(icon_view, "scroll_to_path"): # Si pygtk >= 2.8 icon_view.scroll_to_path(model.get_path(iterfav), False, 0, 0) else: # ¿No hay equivalente en pyGTK 2.6? pass return self.content_box
def imprimir(self, boton): """ Prepara la vista preliminar para la impresión del informe """ if self.partidas_carga != {}: if self.wids['rb_pesoreal'].get_active(): metodo = "reales tomados en la línea (incluyendo embalajes)." elif self.wids['rb_teorico'].get_active(): metodo = "teóricos." # @UnusedVariable elif self.wids['rb_pesosin'].get_active(): metodo = "reales tomados en la línea (sin embalajes)." else: txt = "consumo_fibra_por_partida_gtx.py::imprimir -> "\ "¡NO HAY SELECCIONADO NINGÚN RADIOBUTTON DEL GRUPO!" self.logger.error(txt) myprint(txt) return partidas_carga = self.partidas_carga from formularios import reports datos = [] total_kilos_consumidos = 0.0 total_kilos_producidos = 0.0 total_kilos_teoricos = 0.0 total_balas_consumidas = 0 total_rollos_producidos = 0 total_metros_producidos = 0.0 def cmp_codigo(pc1, pc2): if pc1.codigo < pc2.codigo: return -1 if pc1.codigo > pc2.codigo: return 1 return 0 claves = partidas_carga.keys() claves.sort(cmp_codigo) for pc in claves: datos.append((pc.codigo, utils.float2str(partidas_carga[pc]['kilos_consumidos']), utils.float2str(partidas_carga[pc]['kilos_producidos']), utils.float2str(partidas_carga[pc]['kilos_teoricos']), str(partidas_carga[pc]['balas']), str(partidas_carga[pc]['rollos']), utils.float2str(partidas_carga[pc]['metros']), )) total_kilos_producidos += partidas_carga[pc]['kilos_producidos'] total_kilos_teoricos += partidas_carga[pc]['kilos_teoricos'] total_kilos_consumidos += partidas_carga[pc]['kilos_consumidos'] total_metros_producidos += partidas_carga[pc]['metros'] total_balas_consumidas += partidas_carga[pc]['balas'] total_rollos_producidos += partidas_carga[pc]['rollos'] padre = datos.append((" >>> Lotes de fibra consumidos", '', '', '', '', '', '')) # @UnusedVariable for lote in partidas_carga[pc]['lotes']: datos.append((" %s: %s" % (lote['código'], lote['producto']), utils.float2str(lote['kilos']), '', '', str(lote['balas']), '', '', )) padre = datos.append((" >>> Partidas de geotextiles producidas:", '', '', '', '', '', '')) # @UnusedVariable for pgtx in partidas_carga[pc]['partidas']: datos.append((" %s: %s" % (pgtx['código'], pgtx['producto']), "", utils.float2str(pgtx['kilos']), utils.float2str(pgtx['kilos_teoricos']), "", str(pgtx['rollos']), utils.float2str(pgtx['metros']), )) datos.append(("---", ) * 7) datos.append(("", ) * 7) datos.append(("===", ) * 7) datos.append((" TOTALES: ", utils.float2str(total_kilos_consumidos), utils.float2str(total_kilos_producidos), utils.float2str(total_kilos_teoricos), str(total_balas_consumidas), str(total_rollos_producidos), utils.float2str(total_metros_producidos) )) datos.append(("", "", "", "", "", "", "")) datos.append(("", "", "", "", "", "", "")) if (self.inicio) == None: fechaInforme = 'Hasta '+utils.str_fecha(self.fin) else: fechaInforme = "%s - %s" % (utils.str_fecha(self.inicio), utils.str_fecha(self.fin)) if datos != []: reports.abrir_pdf( geninformes.consumo_fibra_produccion_gtx(datos, fechaInforme))