bdJerarquia.bas

  1. Attribute VB_Name = "bdJerarquia"
  2. Option Compare Database
  3. Option Explicit
  4.  
  5. '================================================
  6. ' Jerarquia - Gestión de la jerarquía
  7. ' 2009-IV-11 <fco@proinf.net>
  8. ' Version: 1.1
  9. ' Tablas: jerarquias, jerarquias_niveles, jerarquias_tipos, temp_jerarquias
  10. '================================================
  11.  
  12. 'Caracteres posibles: / > » —>
  13. Const SEPARADOR_RUTA = " - "
  14.  
  15. 'Caracteres posibles: \ < « <—
  16. Const SEPARADOR_RUTA_INVERSA = " « "
  17.  
  18. Const SEPARADOR_RUTA_UNIX = "/"
  19.  
  20. '------------------------------------------------
  21. ' LAS TABLAS
  22. '
  23. ' create table jerarquias (
  24. ' id int auto_increment,
  25. ' jerarquia varchar(50) not null,
  26. ' padre_id int,
  27. ' nivel_id int,
  28. '
  29. ' orden int, -- Ordenación al imprimir árbol completo
  30. ' tabulador int, -- Número de tabuladores: 0, 1, 2, etc.
  31. ' hermano int, -- Número de hermano: 1, 2, 3, etc.
  32. ' ruta varchar(255), -- Desde el ancestro hasta el descendiente: Abuelo - Padre - Hijo
  33. ' ruta_inversa varchar(255), -- Desde el descendiente hasta el ancestro: Hijo - Padre - Abuelo
  34. ' ruta_unix varchar(255), -- /Abuelo/Padre/Hijo
  35. ' numeracion varchar(50), -- Del tipo 2.3.4.
  36. ' rama varchar(50), -- Unicode box drawings characters:
  37. '
  38. ' constraint pk_jerarquia primary key (id),
  39. ' constraint fk_nivel foreign key (nivel_id) references jerarquias_niveles(id),
  40. ' constraint fk-padre foreign key (padre_id) references jerarquias(id)
  41. ' );
  42. ' create table jerarquias_niveles (
  43. ' id int auto_increment,
  44. ' nivel varchar(50) not null,
  45. ' tipo_id int,
  46. ' constraint pk_jerarquia_nivel primary key (id),
  47. ' constraint fk_tipo foreign key (tipo_id) references jerarquias_tipos(id)
  48. ' );
  49. ' create table jerarquias_tipos (
  50. ' id int auto_increment,
  51. ' tipo varchar(50) not null,
  52. ' constraint pk_jerarquia_tipo primary key (id)
  53. ' );
  54. ' create table temp_jerarquias (
  55. ' jerarquia_id int,
  56. ' elemento varchar(50),
  57. ' orden int auto_increment,
  58. ' constraint fk_jerarquia foreign key (jerarquia_id) references jerarquias(id)
  59. ' );
  60. '
  61.  
  62. '------------------------------------------------
  63. ' Analizar jerarquia
  64. '------------------------------------------------
  65.  
  66. Public Function jerarquiaCalcular() As Boolean
  67. ' Actualiza el valor de los siguientes campos pertenecientes a la tabla jerarquias
  68. ' orden: Ordenación al imprimir árbol completo
  69. ' tabulador: identación del elemento según la profundidad a la que se encuentre
  70. ' hermano: número de hermano dentro del mismo nivel
  71. ' ruta: ruta desde el elemento padre hasta el hijo
  72. ' ruta_inversa: ruta desde el hijo hasta el padre
  73. ' ruta_unix: ruta tipo carpetas en UNIX
  74. ' numeracion: Por ejemplo: 1. 1.1. 1.2. 1.2.1. etc.
  75. ' rama: caracteres de rama para representación arborescente
  76. '
  77. '2009-IV-11 <fco@proinf.net>
  78. '2009-IV-13 rama
  79. '2009-IV-13 hermano, ruta_unix, numeracion
  80.  
  81.  
  82. '1ª fase. Calcular: orden, tabulador, ruta, ruta_inversa
  83.  
  84. Dim rsTipos As DAO.Recordset
  85. Dim rsNiveles As DAO.Recordset
  86. Dim rsElementos As DAO.Recordset
  87. Dim filtroTipo As String
  88. Dim filtroNivel As String
  89. Dim orden As Long
  90. Dim tabulador As Integer
  91. Dim hermano As Integer
  92. Dim ruta As String
  93. Dim rutaInversa As String
  94. Dim rutaUnix As String
  95. Dim numeracion As String
  96.  
  97. Set rsElementos = CurrentDb.OpenRecordset("select * from jerarquias order by jerarquia", dbOpenDynaset)
  98. Set rsNiveles = CurrentDb.OpenRecordset("select * from jerarquias_niveles order by id", dbOpenSnapshot)
  99. Set rsTipos = CurrentDb.OpenRecordset("select * from jerarquias_tipos", dbOpenForwardOnly)
  100.  
  101. Do Until rsTipos.EOF
  102. filtroTipo = "tipo_id=" & rsTipos("id")
  103. rsNiveles.FindFirst filtroTipo
  104.  
  105. orden = 0
  106. tabulador = 0
  107. hermano = 0
  108. ruta = ""
  109. rutaInversa = ""
  110. rutaUnix = ""
  111. numeracion = ""
  112.  
  113. Do Until rsNiveles.NoMatch
  114. filtroNivel = "nivel_id=" & rsNiveles("id")
  115. rsElementos.FindFirst filtroNivel
  116. Do Until rsElementos.NoMatch
  117.  
  118. hermano = hermano + 1
  119. numeracion = hermano & "."
  120. ruta = rsElementos("jerarquia")
  121. rutaInversa = rsElementos("jerarquia")
  122. rutaUnix = SEPARADOR_RUTA_UNIX & rsElementos("jerarquia")
  123.  
  124. Call guardarElemento(rsElementos, orden, tabulador, hermano, ruta, rutaInversa, rutaUnix, numeracion)
  125. Call recursivoHijos(rsElementos("id"), orden, tabulador + 1, ruta, rutaInversa, rutaUnix, numeracion)
  126.  
  127. rsElementos.FindNext filtroNivel
  128. Loop
  129. Exit Do
  130. Loop
  131. rsTipos.MoveNext
  132. Loop
  133.  
  134. rsTipos.Close: Set rsTipos = Nothing
  135. rsNiveles.Close: Set rsNiveles = Nothing
  136. rsElementos.Close: Set rsElementos = Nothing
  137.  
  138. '2ª fase. Calcular: rama
  139.  
  140. Call jerarquiaCalcularRamas
  141.  
  142. End Function
  143.  
  144. Private Function recursivoHijos( _
  145. ByVal padre_id As Long, _
  146. ByRef orden As Long, ByVal tabulador As Integer, _
  147. ByVal rutaOrigen As String, ByVal rutaInversaOrigen As String, ByVal rutaUnixOrigen As String, _
  148. ByVal numeracionOrigen As String _
  149. ) As Boolean
  150.  
  151. Dim filtro As String
  152. Dim rsHijos As DAO.Recordset
  153. Dim hermano As Integer
  154. Dim ruta As String
  155. Dim rutaInversa As String
  156. Dim rutaUnix As String
  157. Dim numeracion As String
  158.  
  159. Set rsHijos = CurrentDb.OpenRecordset("select * from jerarquias order by jerarquia", dbOpenDynaset)
  160.  
  161. filtro = "padre_id=" & padre_id
  162. rsHijos.FindFirst filtro
  163. Do Until rsHijos.NoMatch
  164.  
  165. hermano = hermano + 1
  166. ruta = rutaOrigen & SEPARADOR_RUTA & rsHijos("jerarquia")
  167. rutaInversa = rsHijos("jerarquia") & SEPARADOR_RUTA_INVERSA & rutaInversaOrigen
  168. rutaUnix = rutaUnixOrigen & SEPARADOR_RUTA_UNIX & rsHijos("jerarquia")
  169. numeracion = numeracionOrigen & hermano & "."
  170.  
  171. Call guardarElemento(rsHijos, orden, tabulador, hermano, ruta, rutaInversa, rutaUnix, numeracion)
  172. Call recursivoHijos(rsHijos("id"), orden, tabulador + 1, ruta, rutaInversa, rutaUnix, numeracion)
  173.  
  174. rsHijos.FindNext filtro
  175. Loop
  176.  
  177. rsHijos.Close: Set rsHijos = Nothing
  178.  
  179. End Function
  180.  
  181. Private Function guardarElemento( _
  182. rs As Recordset, _
  183. ByRef orden As Long, ByVal tabulador As Integer, ByVal hermano As Integer, _
  184. ByVal ruta As String, ByVal rutaInversa As String, ByVal rutaUnix As String, _
  185. ByVal numeracion As String _
  186. ) As Boolean
  187.  
  188. orden = orden + 1
  189.  
  190. rs.Edit
  191. rs("orden") = orden
  192. rs("tabulador") = tabulador
  193. rs("hermano") = hermano
  194. rs("ruta") = ruta
  195. rs("ruta_inversa") = rutaInversa
  196. rs("ruta_unix") = rutaUnix
  197. rs("numeracion") = numeracion
  198. rs.Update
  199.  
  200. End Function
  201.  
  202. '------------------------------------------------
  203. ' Caracteres unicode para las ramas
  204. '------------------------------------------------
  205.  
  206. Private Function jerarquiaCalcularRamas()
  207. ' Actualiza el valor del campo rama de la tabla jerarquias
  208. ' U+2514 box drawings light up and right (parecido a una L) [+]
  209. ' U+251C box drawings light vertical and right (como una T girada un cuarto) [+]
  210. ' U+2502 box drawings light vertical (barra vertical) [|]
  211. ' U+2500 box drawings light horizontal (barra horizontal) [-]
  212. ' U+252C box drawings light down and horizontal (en forma de T)
  213. ' Algoritmo:
  214. ' Por cada elemento desde el último hasta el primero:
  215. ' Crear tantos espacios como indique tabulador+1+1
  216. ' Por cada espacio:
  217. ' Si es el último:
  218. ' Si no había esta posición o era espacio la última vez: BOX UP RIGHT
  219. ' sino: BOX VERTICAL RIGHT
  220. ' Si es un nivel menos que antes: BOX DOWN HORIZONTAL
  221. ' sino: BOX HORIZONTAL
  222. ' sino es el último:
  223. ' Si no había esta posición o era espacio la última vez: SPACE
  224. ' sino: BOX VERTICAL
  225. '
  226. ' 2009-IV-12 <fco@proinf.net>
  227.  
  228. 'Unicode box drawings
  229. Dim boxHorizontal As String: boxHorizontal = ChrW(&H2500)
  230. Dim boxVertical As String: boxVertical = ChrW(&H2502)
  231. Dim boxUpRight As String: boxUpRight = ChrW(&H2514)
  232. Dim boxVerticalRight As String: boxVerticalRight = ChrW(&H251C)
  233. Dim boxDownHorizontal As String: boxDownHorizontal = ChrW(&H252C)
  234.  
  235. Dim rsTipos As DAO.Recordset
  236. Dim rsElementos As DAO.Recordset
  237. Dim sql As String
  238. Dim rama As String, ramita As String, largoRama As Integer
  239. Dim ramaAnterior As String, ramitaAnterior As String, largoRamaAnterior As Integer
  240. Dim unicode As String
  241. Dim indice As Integer
  242.  
  243. Set rsTipos = CurrentDb.OpenRecordset("select * from jerarquias_tipos", dbOpenSnapshot)
  244.  
  245. rsTipos.MoveFirst
  246. Do Until rsTipos.EOF
  247.  
  248. rama = ""
  249. ramaAnterior = ""
  250. largoRamaAnterior = 0
  251.  
  252. sql = "select * from jerarquias where nivel_id in (select id from jerarquias_niveles where tipo_id=«id») order by orden desc"
  253. sql = sqlParametrizar(sql, rsTipos("id"))
  254.  
  255. Set rsElementos = CurrentDb.OpenRecordset(sql, dbOpenDynaset)
  256. rsElementos.MoveFirst
  257. Do Until rsElementos.EOF
  258.  
  259. largoRama = rsElementos("tabulador") + 1
  260. rama = Left(rama & String(largoRama, "0"), largoRama)
  261.  
  262. unicode = ""
  263. For indice = 1 To largoRama
  264. If indice <= largoRamaAnterior Then
  265. ramitaAnterior = Mid(ramaAnterior, indice, 1)
  266. Else
  267. ramitaAnterior = "0"
  268. End If
  269.  
  270. If indice = largoRama Then
  271. If ramitaAnterior = "0" Then
  272. ramita = "H" 'Hijo
  273. unicode = unicode & boxUpRight
  274. Else
  275. ramita = "L" 'Línea
  276. unicode = unicode & boxVerticalRight
  277. End If
  278. If largoRama < largoRamaAnterior Then
  279. ramita = "P" 'Padre
  280. unicode = unicode & boxDownHorizontal
  281. Else
  282. unicode = unicode & boxHorizontal
  283. End If
  284. Else
  285. If ramitaAnterior = "0" Then
  286. ramita = "0" 'Nada
  287. unicode = unicode & " "
  288. Else
  289. ramita = "1" 'Línea
  290. unicode = unicode & boxVertical
  291. End If
  292. End If
  293. Mid(rama, indice, 1) = ramita
  294. Next
  295.  
  296. rsElementos.Edit
  297. 'rsElementos("rama") = rama 'Para depuración
  298. rsElementos("rama") = unicode
  299. rsElementos.Update
  300.  
  301. ramaAnterior = rama
  302. largoRamaAnterior = largoRama
  303.  
  304. rsElementos.MoveNext
  305. Loop
  306.  
  307. rsElementos.Close
  308. rsTipos.MoveNext
  309. Loop
  310.  
  311. On Error Resume Next
  312. rsTipos.Close: Set rsTipos = Nothing
  313. rsElementos.Close: Set rsElementos = Nothing
  314. End Function
  315.  
  316. '------------------------------------------------
  317. ' Informe
  318. '------------------------------------------------
  319.  
  320. Public Function jerarquiaInformeEnTexto(ByVal tipo_id As Long, Optional conRamas As Boolean = True) As String
  321. '2009-IV-13
  322.  
  323. Dim rs As DAO.Recordset
  324. Dim sql As String
  325. Dim filtro As String
  326. Dim lineas() As String
  327. Dim numLineas As Integer
  328. Dim indice As Integer
  329.  
  330. filtro = "nivel_id in (select id from jerarquias_niveles where tipo_id=«id»)"
  331. filtro = sqlParametrizar(filtro, tipo_id)
  332.  
  333. numLineas = DCount("*", "jerarquias", filtro)
  334. If numLineas <= 0 Then Exit Function
  335. ReDim lineas(numLineas - 1)
  336. indice = 0
  337.  
  338. sql = "select * from jerarquias where «filtro» order by orden"
  339. sql = sqlParametrizar(sql, filtro)
  340. Set rs = CurrentDb.OpenRecordset(sql, dbOpenForwardOnly)
  341. Do Until rs.EOF
  342. If conRamas Then
  343. lineas(indice) = rs("rama") & " " & rs("jerarquia")
  344. Else
  345. lineas(indice) = Space(rs("tabulador") * 4) & rs("jerarquia")
  346. End If
  347.  
  348. indice = indice + 1
  349. rs.MoveNext
  350. Loop
  351.  
  352. jerarquiaInformeEnTexto = Join(lineas, vbCrLf)
  353.  
  354. rs.Close: Set rs = Nothing
  355.  
  356. End Function
  357.  
  358. '------------------------------------------------
  359. ' Consulta
  360. '------------------------------------------------
  361.  
  362. Public Function jerarquiaBuscarProgenitor(ByVal elemento_id As Long, ByVal nivel_id As Long) As Long
  363. 'Asciende por los ancestros hasta llegar al nivel indicado.
  364. 'Retorna el elemento encontrado y sino 0
  365. '2009-IV-10 <fco@proinf.net>
  366.  
  367. Dim rs As DAO.Recordset
  368. Dim progenitor_id As Long
  369. Dim padre_id As Long
  370.  
  371. Set rs = CurrentDb.OpenRecordset("jerarquias", dbOpenSnapshot)
  372.  
  373. rs.FindFirst "id=" & elemento_id
  374. Do Until rs.NoMatch
  375. If esId(rs("nivel_id")) Then
  376. If rs("nivel_id") = nivel_id Then
  377. progenitor_id = rs("id")
  378. Exit Do
  379. ElseIf esId(rs("padre_id")) Then
  380. rs.FindFirst "id=" & rs("padre_id")
  381. Else
  382. Exit Do
  383. End If
  384. Else
  385. Exit Do
  386. End If
  387. Loop
  388.  
  389. jerarquiaBuscarProgenitor = progenitor_id
  390.  
  391. rs.Close: Set rs = Nothing
  392.  
  393. End Function
  394.  
  395. Public Function jerarquiaDesplazarNivel(ByVal nivel_id As Long, ByVal nivel_desplaz As Integer) As Long
  396. 'Sube o baja por los niveles
  397. 'Retorna el elemento encontrado y sino 0
  398. '2009-IV-10 <fco@proinf.net>
  399.  
  400. Dim rs As DAO.Recordset
  401. Dim tipo_id As Long
  402. Dim filtro As String
  403. Dim sql As String
  404.  
  405. sql = "select * from jerarquias_niveles order by id «orden»"
  406. sql = sqlParametrizar(sql, IIf(nivel_desplaz > 0, "asc", "desc"))
  407.  
  408. Set rs = CurrentDb.OpenRecordset(sql, dbOpenSnapshot)
  409.  
  410. rs.FindFirst "id=" & nivel_id
  411. If rs.NoMatch Then
  412. nivel_id = 0
  413. Else
  414. tipo_id = rs("tipo_id")
  415.  
  416. Do Until nivel_desplaz = 0
  417.  
  418. filtro = "id«comparacion»«nivel_id» and tipo_id=«tipo_id»"
  419.  
  420. filtro = sqlParametrizar(filtro, IIf(nivel_desplaz > 0, ">", "<"), nivel_id, tipo_id)
  421.  
  422. rs.FindFirst filtro
  423. If rs.NoMatch Then
  424. nivel_id = 0
  425. Exit Do
  426. Else
  427. nivel_id = rs("id")
  428. nivel_desplaz = nivel_desplaz - Sgn(nivel_desplaz)
  429. End If
  430. Loop
  431. End If
  432.  
  433. jerarquiaDesplazarNivel = nivel_id
  434.  
  435. rs.Close: Set rs = Nothing
  436.  
  437. End Function
  438.  
  439. Public Function jerarquiaPrimerNivel(ByVal tipo_id As Long) As Long
  440. 'Retorna el primer nivel del tipo indicado y sino 0
  441. '2009-IV-12
  442. jerarquiaPrimerNivel = Nz(DLookup("id", "jerarquias_niveles", "tipo_id=" & tipo_id), 0)
  443. End Function
  444.  
  445. Public Function jerarquiaTieneHijos(ByVal elemento_id As Long) As Boolean
  446. 'Indica si el elemento tiene descendientes
  447. '2009-IV-12
  448. jerarquiaTieneHijos = Not IsNull(DLookup("id", "jerarquias", "padre_id=" & elemento_id))
  449. End Function
  450.  
  451. Public Function jerarquiaPadre(ByVal elemento_id As Long) As Long
  452. 'Retorna el elemento padre y sino 0
  453. '2009-IV-12
  454. jerarquiaPadre = Nz(DLookup("padre_id", "jerarquias", "id=" & elemento_id), 0)
  455. End Function
  456.  
  457. Public Function jerarquiaNivel(ByVal elemento_id As Long) As Long
  458. 'Retorna el nivel del elemento y sino 0
  459. '2009-IV-12
  460. jerarquiaNivel = Nz(DLookup("nivel_id", "jerarquias", "id=" & elemento_id), 0)
  461. End Function
  462.  
  463. Public Function jerarquiaElemento(ByVal elemento_id As Long) As String
  464. 'Retorna la descripción del elemento y sino ""
  465. jerarquiaElemento = Nz(DLookup("jerarquia", "jerarquias", "id=" & elemento_id), "")
  466. End Function
  467.  
  468. Public Function jerarquiaRuta(ByVal elemento_id As Long) As String
  469. 'Retorna la ruta del elemento y sino ""
  470. jerarquiaRuta = Nz(DLookup("ruta", "jerarquias", "id=" & elemento_id), "")
  471. End Function
  472.  
  473. Public Function jerarquiaAncestro(ByVal ruta_unix As String, ByVal num_nivel As Integer) As String
  474. 'Ej: "/abuelo/padre/hijo" --1--> "padre"
  475. ' "/abuelo/padre/hijo" --0--> "abuelo"
  476. ' "/abuelo/padre/hijo" --4--> ""
  477. '2009-IV-17
  478. Dim ruta As String
  479. Dim lista As Variant
  480.  
  481. ruta = Mid(ruta_unix, 2)
  482. lista = Split(ruta, SEPARADOR_RUTA_UNIX)
  483.  
  484. If num_nivel >= LBound(lista) And num_nivel <= UBound(lista) Then
  485. jerarquiaAncestro = lista(num_nivel)
  486. End If
  487.  
  488. End Function
  489.  
  490. '------------------------------------------------
  491. ' Lista progresiva para seleccionar
  492. '------------------------------------------------
  493.  
  494. Public Function jerarquiaListaProgresiva( _
  495. ByVal elemento_id As Long, _
  496. ByVal tipo_id As Long, _
  497. ByVal clave As String _
  498. ) As Boolean
  499. 'Actualiza la tabla temp_jerarquia según el valor de elemento_id o de tipo_id
  500. 'Dicha tabla se ha de utilizar como origen de lista de un combobox
  501. 'Ejemplo de uso en los eventos `jerarquia_id_Click` y `Form_Current`:
  502. ' If jerarquiaListaProgresiva(NZ(Me.combobox,0), 1, 'clave1') Then Me.combobox.Requery
  503. 'Las propiedades del combobox son:
  504. ' -Origen de la fila : select * from temp_jerarquias where clave='clave1'
  505. ' -Número de columnas: 2
  506. ' -Ancho de columnas : 0cm;
  507. '2009-IV-13 <fco@proinf.net>
  508. '2009-IV-17 Añadido el parámetro clave para diferenciar un combobox de otro
  509.  
  510. Const SQL_BORRAR = "delete * from temp_jerarquias where clave=«clave»"
  511. Const SQL_INSERTAR = "insert into temp_jerarquias(jerarquia_id, elemento, clave) "
  512.  
  513. Const SQL_FILTRO_TIPO = "nivel_id in (select id from jerarquias_niveles where tipo_id=«id»)"
  514.  
  515. Const SQL_RAICES = "select id, jerarquia, «clave» from jerarquias where «filtro_tipo» and tabulador=0 order by hermano"
  516. Const SQL_HIJOS = "select id, jerarquia, «clave» from jerarquias where padre_id=«id» order by hermano"
  517. Const SQL_RAIZ = "values (0, '/', «clave»)"
  518. Const SQL_ANCESTROS = "select id, ruta_unix, «clave» from jerarquias where numeracion in («numeraciones») and «filtro_tipo» order by tabulador desc"
  519.  
  520. Dim sql As String
  521. Dim filtroTipo As String
  522. Dim numeracion As String
  523. Dim numeraciones As String
  524.  
  525. clave = sqlLiteral(clave, "STRING")
  526.  
  527. filtroTipo = sqlParametrizar(SQL_FILTRO_TIPO, tipo_id)
  528.  
  529. 'Borrarlo todo
  530. sql = sqlParametrizar(SQL_BORRAR, clave)
  531. If Not sqlEjecutar(sql) Then Exit Function
  532.  
  533. If elemento_id = 0 Then
  534. sql = SQL_INSERTAR & sqlParametrizar(SQL_RAICES, clave, filtroTipo)
  535. If Not sqlEjecutar(sql) Then Exit Function
  536. Else
  537. numeracion = Nz(DLookup("numeracion", "jerarquias", "id=" & elemento_id), "")
  538. Do Until numeracion = ""
  539. If numeraciones <> "" Then numeraciones = numeraciones & ","
  540. numeraciones = numeraciones & "'" & numeracion & "'"
  541. numeracion = Left(numeracion, Len(numeracion) - 2)
  542. Loop
  543.  
  544. 'Ancestros
  545. sql = SQL_INSERTAR & sqlParametrizar(SQL_ANCESTROS, clave, numeraciones, filtroTipo)
  546. If Not sqlEjecutar(sql) Then Exit Function
  547.  
  548. 'Vacío
  549. sql = SQL_INSERTAR & sqlParametrizar(SQL_RAIZ, clave)
  550. If Not sqlEjecutar(sql) Then Exit Function
  551.  
  552. 'Inserta los hijos
  553. sql = SQL_INSERTAR & sqlParametrizar(SQL_HIJOS, clave, elemento_id)
  554. If Not sqlEjecutar(sql) Then Exit Function
  555.  
  556.  
  557. End If
  558. jerarquiaListaProgresiva = True
  559.  
  560. End Function
  561.  

Proinf.net