Plan de Cuentas con Treeview

Hola!

Pido la ayuda al foro para el siguiente caso.

Una Tabla CTAS_CO, en la cual están todos los registros del Plan de Cuentas, esta tabla contiene un Maestro de Empresas, a fin de diferenciar por cada Empresa.

Necesito presentar el listado de las cuentas en un Treeview, sé que puedo utilizar el objeto visor de árbol, de hecho así lo tengo montado en una aplicación anterior en la V6x, solamente que atendiendo la potencia que representa poder utilizar javascript me gustaría utilizarlo.

Ya tengo montado la estructura de las columnas, las cabeceras del objeto, TopLevelItem, etc., pero tropiezo con la complejidad que representa crear cada uno de los nodos hijo que se desprende y sus siguientes items en forma recursiva, por ejemplo

1 ACTIVO
11 ACTIVO CORRIENTE
111 DISPONIBILIDADES
11101 CAJA
1110101 Recaudaciones a Depositar
1110102 Caja Moneda extranjera

De acuerdo a la representación anterior puede incrementarse la cantidad de nodos de acuerdo a la complejidad del Plan de cuentas y sus respectivos auxiliares.

He buscado en varios artículos anteriores y no puedo encontrar la solución a este planteamiento.

Desde ya muy agradecido a todos por su ayuda de siempre.

Buenos dias:

Seguramente se puede hacer con ese objeto pero no será precisamente sencillo.

En cambio hacerlo con una tabla arbolada y un árbol visor de tablas es un juego de niños…

¿Porqué no pruebas esa opción?

Saludos. Ramiro

Hola AROJAS.

Como dice Ramiro con el árbol visor de tablas es coser y cantar.

Sin embargo, si eres un intrépido puedes probar con el TreeView.
Prueba el siguiente ejemplo:

  • La tabla arbolada tiene la siguiente estructura: CUENTAS(ID alfa40(8), NAME alfa256(100), PADRE alfa40(8))
  • El campo PADRE contiene el ID de la rama Padre de cada registro. Si isEmpty(PADRE) entonces el registro es un TopLevel
var oArbol = theRoot.dataView().control("CTR_ARBOL")
// Columnas del árbol
var nColID = 0
var nColName = 1
var nColPadre = 2
oArbol.clear()
// El doble click se reserva para editar la Rama
oArbol.expandsOnDoubleClick = false
oArbol.wordWrap = true
oArbol.textElideMode = VTreeWidget.ElideRight
oArbol.indentation = 10
oArbol.animated = true
oArbol.rootIsDecorated = true
oArbol.uniformRowHeights = true
oArbol.alternatingRowColors = true
var cTabla = "MiProyecto_dat/CUENTAS"
// Fuentes para decorar
var oFuenteNegrita = new VFont("courier")
oFuenteNegrita.setPixelSize(12)
oFuenteNegrita.setBold(true)
var oFuenteNormal = new VFont("consolas")
var oFuenteCourier = new VFont("courier")
// Cabeceras
oArbol.headerHidden = false
oArbol.setHeaderLabel("Cuentas contables")   // No aparece cuando usamos setHeaderLabels()
oArbol.setHeaderLabels(["Cuenta", "Nombre", "Padre"])

// Cargamos la lista de Cuentas ordenada por el ID para ir rellenando desde arriba hacia abajo
var oLisCuentas = new VRegisterList(theRoot)
oLisCuentas.setTable(cTabla)
oLisCuentas.load("ID",[])

var oItemPadre = null, oItemHija = null, oItemPadreScroll = null
// Recorremos la Lista y vamos insertando las Ramas en su Padre correspondiente
for (var i = 0 ; i < oLisCuentas.size(); ++i) {
	var oItem = oLisCuentas.readAt(i)
	var cID = oItem.fieldToString("ID")		// ID único de cada rama
	var cNombre = oItem.fieldToString("NAME")	// Nombre de la Rama
	var cIdPadre = oItem.fieldToString("PADRE")	// ID del Padre de la rama
	// Añadimos un Item
	if (cIdPadre === "") {
		// El Item es TopLevel
		var oItemPadre = oArbol.addTopLevelItem()
		oItemPadre.setText(nColID, cID)
		oItemPadre.setText(nColName, cNombre)
		oItemPadre.setText(nColPadre, cIdPadre)
		oItemPadre.setFont(nColID, oFuenteNegrita)
		oItemPadre.setForegroundColor(nColName,139,0,0,255)
		oItemPadre.setBackgroundColor(nColName,240,230,140,255)
		// CID_PADRE_SCROLL guarda el ID de la rama TopLevel selecionada al inicio
		if (theRoot.varToString("CID_PADRE_SCROLL") == cID) {
			oItemPadreScroll = oItemPadre
		}
	}
	else {
		// La Opción es hija.
		// Comprobamos si la rama padre actual oItemPadre es la correcta, de lo contrario
		// 	tenemos que buscar la Rama padre de forma recursiva
		if (oItemPadre && (oItemPadre.text(nColID) != cIdPadre)) {
			// No podemos buscar un Item directamente en el árbol TreeWidget por su ID
			// 	Tenemos que buscar el Item Padre de forma recursiva recorriendo las ramas
			oItemPadre = buscarPadre_Arbol(cIdPadre, oArbol)
		}
		// Mostramos solo los Valores asignados si lSoloConValor = true
		if (oItemPadre) {
			oItemHija = oItemPadre.addChild()
			oItemHija.setText(nColID, cID)
			oItemHija.setText(nColName, cNombre)
			oItemHija.setText(nColPadre, cIdPadre)
			oItemHija.setFont(nColID, oFuenteCourier)
			oItemHija.setFont(nColName, oFuenteNormal)
			oItemHija.setForegroundColor(nColName,105,105,105,255)
			oItemHija.setBackgroundColor(nColName,255,248,220,255)
		}
	}
}

function buscarPadre_Arbol(cIdPadre, oArbol) {
	// Recorremos cada rama de los topLevelItem
	var oItemPadre = null
	for(var t = 0 ; t < oArbol.topLevelItemCount; ++t) {
		oItemPadre = buscarPadre_Rama(cIdPadre, oArbol.topLevelItem(t))
		if (oItemPadre) break
	}
	return oItemPadre
}

function buscarPadre_Rama(cIdPadre, oItemRama) {
	var oItemPadre = null
	// Recorremos las subramas para buscar el ID del padre
	for(var x = 0 ; x < oItemRama.childCount(); ++x) {
		// Comprobamos la columna del ID
		var oItem = oItemRama.child(x)
		if (oItem.text(nColID) == cIdPadre) {
			// Se ha encontrado el Padre, lo retorna y termina el bucle
			return oItem
		}
		else {
			// Buscamos de forma recursiva
			oItemPadre = buscarPadre_Rama(cIdPadre, oItem)
		}
	}
	return oItemPadre
}

// Ocultamos la columna con el ID del Padre
oArbol.hideColumn(nColPadre)
// Expandimos las ramas
oArbol.expandToDepth(3)
// Damos ancho fijo a la 1ª columna
oArbol.setColumnWidth(nColID, 150)
// oArbol.resizeColumnToContents(nColID)

// Activamos y mostramos el Item escogido
if (oItemPadreScroll){
	oArbol.scrollToItem(oItemPadreScroll, VTreeWidget.PositionAtCenter)
	oArbol.setCurrentItem(oItemPadreScroll, 0)
}

Como muchos objetos en Velneo, el Widget TreeView no es una excepción y está bastante incompleto, con algún que otro bug (por ejemplo el menú contextual) que por alguna oscura razón lleva años sin solución.

De todas formas lo que hay funciona bastante bien.
Adjunto imagen con el resultado del ejemplo.

Saludos
Paco Satué

Paco, muchas gracias por este aporte.

Estoy iniciando con esto de javascript, asi que es necesario ir paso a paso para ganar conocimiento y sin duda el codigo que amablemente facilitas de gran ayuda.

Saludos

Muy amable por el aporte, me parece muy interesante

Hola
Hice algo parecido a lo que tu quieres
problema: en servidor local va muy bien, pero en cloud va muy lento

salu2
miguel
ciberideas

Hola Miguel.

¿Por qué va lento en Cloud?
¿No cacheas previamente la tabla de Cuentas antes de abrir el árbol?

Saludos
Paco Satué

Hola Paco, te agradezco mucho el aporte, sólo una cuestión, la tabla CUENTAS es de tipo Maestra arbolada o simplemente Maestra?

Gracias

Hola nandosanchez.

Es Maestra arbolada, el campo ID es Alfa40(8).
Aunque para el ejemplo del TreeView con javascript no es necesario que sea arbolada, ya que uso el campo PADRE para establecer las subramas del árbol y podrían ser campos numéricos perfectamente.

La ventaja de las tablas arboladas es solo para Velneo porque disponemos del objeto Visor tablas arboladas que nos construye un árbol él solito.

Saludos
Paco Satué

Estimado Paco , ahora si me sorprendiste , ¿como haces para que de manera intencional cachear una tabla?

Hola arturomiranda.

Muy sencillo.

Ejecutas en Segundo Plano y en el PRE_INI del Autoexec un comando Cargar lista y Ordenar lista.
Con esto consigues tener en caché la tabla, y el primer acceso es mucho más rápido.

Se deberían precargar en caché todas las tablas de la aplicación que vayan a ser de solo lectura.

Saludos
Paco Satué