xonecode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
@@ -0,0 +1,861 @@
1
+ # Generación XOne — Fases 8-9: eventos, permisos y JavaScript
2
+
3
+ > Fuente: `xone/v2/xone-project-generator/references/xone-project-generation-workflow.md` §9–§10. Referencia de la skill; el índice está en [../SKILL.md](../SKILL.md).
4
+
5
+ Contenido: §9 Fase 8 eventos de ciclo de vida, navegación, colección Empresas, aplicación, inactividad, bind en controles, permisos Android y referencia rápida por ubicación · §10 Fase 9 plantilla functions.js y referencia rápida de API
6
+
7
+ ---
8
+
9
+ ## 9. Fase 8: Eventos y Reglas de Negocio
10
+
11
+ ### 8.1 Objetivo
12
+
13
+ Los eventos son el mecanismo principal para dar vida a la aplicación. Permiten ejecutar lógica de negocio en respuesta a acciones del usuario o del sistema. A diferencia del JavaScript de organización (funciones generales), los eventos están ligados directamente al ciclo de vida de las colecciones, controles y la propia aplicación.
14
+
15
+ > **IMPORTANTE:** Los eventos pueden ocurrir por una acción ejecutada por el usuario en la interfaz visual o por un script creado por el programador.
16
+
17
+ ### 8.2 Estructura General de un Evento
18
+
19
+ Todo evento en XOne sigue esta estructura XML:
20
+
21
+ ```xml
22
+ <nombre-evento refresh="true|false" show-wait-dialog="true|false">
23
+ <action name="runscript">
24
+ <script language="javascript">
25
+ // Código JavaScript aquí
26
+ </script>
27
+ </action>
28
+ </nombre-evento>
29
+ ```
30
+
31
+ **Atributos comunes a todos los eventos:**
32
+
33
+ | Atributo | Valores | Descripción |
34
+ |----------|---------|-------------|
35
+ | `refresh` | `true` / `false` | Si se refresca la UI después de ejecutar el evento |
36
+ | `show-wait-dialog` | `true` / `false` | Si se muestra dialogo de espera durante la ejecución |
37
+
38
+ ---
39
+
40
+ ### 8.3 Eventos de Ciclo de Vida de Coleccion
41
+
42
+ Estos eventos se definen dentro de cualquier coleccion `.xne` y responden al ciclo de vida de sus objetos.
43
+
44
+ | Evento | Cuando se ejecuta |
45
+ |--------|-------------------|
46
+ | `create` | Cuando se crea un objeto nuevo (por acción del framework o por script) |
47
+ | `insert` | Cuando el usuario guarda un objeto (insercion nueva o actualización) |
48
+ | `before-edit` | Al ir a editar un objeto, **antes** de que se pinte la pantalla |
49
+ | `after-edit` | Al ir a editar un objeto, **una vez** que ya esta pintado el UI |
50
+ | `onchange` | Cuando cambia el valor de un campo monitoreado |
51
+ | `delete` | Cuando se va a borrar un objeto |
52
+
53
+ #### `create` — Inicializar valores al crear
54
+
55
+ ```xml
56
+ <create refresh="false" show-wait-dialog="false">
57
+ <action name="runscript">
58
+ <script language="javascript">
59
+ // Inicializar campos por defecto al crear un objeto nuevo
60
+ self.FECHA = new Date();
61
+ self.ESTADO = "PENDIENTE";
62
+ self.ID_USUARIO = appData.getGlobalMacro("##USERID##");
63
+ </script>
64
+ </action>
65
+ </create>
66
+ ```
67
+
68
+ #### `insert` — Validar antes de guardar
69
+
70
+ ```xml
71
+ <insert refresh="false" show-wait-dialog="false">
72
+ <action name="runscript">
73
+ <script language="javascript">
74
+ // Validar campos obligatorios antes de guardar
75
+ if (!self.NOMBRE || self.NOMBRE.trim() === "") {
76
+ appData.failWithMessage(-8100, "El campo NOMBRE es obligatorio.");
77
+ }
78
+ if (!self.FECHA) {
79
+ appData.failWithMessage(-8100, "La fecha no puede estar vacia.");
80
+ }
81
+ </script>
82
+ </action>
83
+ </insert>
84
+ ```
85
+
86
+ #### `before-edit` — Preparar datos antes de mostrar pantalla
87
+
88
+ ```xml
89
+ <before-edit refresh="false" show-wait-dialog="false">
90
+ <action name="runscript">
91
+ <script language="javascript">
92
+ // Se ejecuta antes de pintar la UI
93
+ // Util para cargar datos externos o calcular valores previos
94
+ self.MAP_TITULO = "Editando: " + self.NOMBRE;
95
+ </script>
96
+ </action>
97
+ </before-edit>
98
+ ```
99
+
100
+ #### Refresco de disablevisible por script
101
+
102
+ Cuando un campo referenciado en `disablevisible` cambia por código, hay que refrescar para que se reevalue:
103
+
104
+ ```javascript
105
+ // Refrescar un prop específico
106
+ ui.refresh("MAP_CAMPO");
107
+
108
+ // Refrescar toda la pantalla
109
+ ui.refresh();
110
+
111
+ // Con referencia explicita a la vista
112
+ var view = ui.getView(self);
113
+ view.refresh("MAP_CAMPO"); // prop específico
114
+ view.refresh(); // toda la pantalla
115
+ ```
116
+
117
+ #### `after-edit` — Inicializar UI tras pintar pantalla
118
+
119
+ ```xml
120
+ <after-edit refresh="false" show-wait-dialog="false">
121
+ <action name="runscript">
122
+ <script language="javascript">
123
+ // La pantalla ya esta pintada, se pueden refrescar controles
124
+ ui.refresh("MAP_TITULO", "ESTADO");
125
+ // Mostrar version de la app en campos de display
126
+ self.MAP_VERSION = "v" + appData.getGlobalMacro("##VERSION##");
127
+ </script>
128
+ </action>
129
+ </after-edit>
130
+ ```
131
+
132
+ #### `onchange` — Reaccionar al cambio de un campo
133
+
134
+ ```xml
135
+ <onchange>
136
+ <prop name="ESTADO">
137
+ <action name="runscript">
138
+ <script language="javascript">
139
+ // Se ejecuta cuando cambia el campo ESTADO
140
+ if (self.ESTADO === "CERRADO") {
141
+ self.FECHA_CIERRE = new Date();
142
+ ui.refresh("FECHA_CIERRE");
143
+ }
144
+ </script>
145
+ </action>
146
+ </prop>
147
+ <prop name="CANTIDAD">
148
+ <action name="runscript">
149
+ <script language="javascript">
150
+ // Recalcular total al cambiar cantidad
151
+ self.TOTAL = self.CANTIDAD * self.PRECIO;
152
+ ui.refresh("TOTAL");
153
+ </script>
154
+ </action>
155
+ </prop>
156
+ </onchange>
157
+ ```
158
+
159
+ #### `delete` — Validar o limpiar antes de borrar
160
+
161
+ ```xml
162
+ <delete refresh="false" show-wait-dialog="false">
163
+ <action name="runscript">
164
+ <script language="javascript">
165
+ // Impedir borrado si el objeto esta en estado activo
166
+ if (self.ESTADO === "ACTIVO") {
167
+ appData.failWithMessage(-8100, "No se puede eliminar un registro en estado ACTIVO.");
168
+ }
169
+ </script>
170
+ </action>
171
+ </delete>
172
+ ```
173
+
174
+ #### `load` — Procesamiento por cada registro al cargar un contents
175
+
176
+ > **Uso poco frecuente.** Se ejecuta una vez por cada registro en el momento en que la coleccion se carga como fuente de datos de un contents. **No tiene relación con la edición** — para eso se usan `before-edit` y `after-edit`. Útil para calcular campos visuales (`MAP_`) que dependen de lógica compleja que no puede resolverse en el SQL.
177
+
178
+ ```xml
179
+ <load refresh="false" show-wait-dialog="false">
180
+ <action name="runscript">
181
+ <script language="javascript">
182
+ // Se ejecuta para cada fila al cargar el contents
183
+ // Util para calcular campos MAP_ de visualización
184
+ if (self.STOCK <= self.STOCK_MINIMO) {
185
+ self.MAP_COLOR_ALERTA = "#FFCCCC";
186
+ } else {
187
+ self.MAP_COLOR_ALERTA = "#FFFFFF";
188
+ }
189
+ </script>
190
+ </action>
191
+ </load>
192
+ ```
193
+
194
+ ---
195
+
196
+ ### 8.4 Eventos de Navegación
197
+
198
+ #### `onback` — Controlar el botón Atrás (solo Android)
199
+
200
+ Obligatorio en toda pantalla. Controla el comportamiento del botón físico/gesto de volver atrás del sistema.
201
+
202
+ ```xml
203
+ <onback show-wait-dialog="false">
204
+ <action name="runscript">
205
+ <script language="javascript">
206
+ ui.getView(self).exit();
207
+ </script>
208
+ </action>
209
+ </onback>
210
+ ```
211
+
212
+ #### `selecteditem` — Selección en contents (subgrid)
213
+
214
+ Se ejecuta cuando se selecciona un elemento de un contents. Solo disponible en colecciones mostradas como contents.
215
+
216
+ ```xml
217
+ <selecteditem refresh="false" show-wait-dialog="false">
218
+ <action name="runscript">
219
+ <script language="javascript">
220
+ // self es el objeto seleccionado en el contents
221
+ ui.openEditView(self);
222
+ </script>
223
+ </action>
224
+ </selecteditem>
225
+ ```
226
+
227
+ #### `auto-selecteditem` — Paso automático de slides
228
+
229
+ Exclusivo para contents con `viewmode="slideview"`. Se ejecuta conforme van pasando automáticamente las presentaciones sin interaccion del usuario.
230
+
231
+ ```xml
232
+ <auto-selecteditem refresh="false" show-wait-dialog="false">
233
+ <action name="runscript">
234
+ <script language="javascript">
235
+ // Lógica al cambiar de slide automaticamente
236
+ </script>
237
+ </action>
238
+ </auto-selecteditem>
239
+ ```
240
+
241
+ ---
242
+
243
+ ### 8.5 Eventos de la Coleccion Empresas
244
+
245
+ Estos eventos son **exclusivos de la coleccion Empresas** en `mappings.xne` y controlan el ciclo de vida global de la aplicación.
246
+
247
+ | Evento | Descripción |
248
+ |--------|-------------|
249
+ | `onlogon` | Cuando el usuario entra en la aplicación (login correcto) |
250
+ | `onlogoff` | Cuando el usuario sale de la aplicación |
251
+ | `maintenance` | Tareas programadas que se ejecutan en segundo plano periódicamente |
252
+ | `replica-ok` | Cuando se completa la replica de una tabla concreta de la BD |
253
+ | `sys-message` | Para recibir eventos de XOneLive |
254
+ | `onpushnotificationclick` | Cuando el usuario pulsa en una notificación PUSH |
255
+ | `onmessage` | Procesamiento de intents del sistema |
256
+ | `onrecovery` | Chequea si el usuario ya estaba validado (sesión recuperada) |
257
+ | `after-recovery-login` | Tras recuperar sesión de login |
258
+
259
+ ```xml
260
+ <!-- En mappings.xne, dentro de la coleccion Empresas -->
261
+ <onlogon refresh="false" show-wait-dialog="false">
262
+ <action name="runscript">
263
+ <script language="javascript">
264
+ // Inicializar variables globales al entrar en la app
265
+ ui.showToast("Bienvenido, " + appData.getGlobalMacro("##USERNAME##"));
266
+ </script>
267
+ </action>
268
+ </onlogon>
269
+
270
+ <onlogoff refresh="false" show-wait-dialog="false">
271
+ <action name="runscript">
272
+ <script language="javascript">
273
+ // Limpiar datos al salir
274
+ </script>
275
+ </action>
276
+ </onlogoff>
277
+
278
+ <maintenance interval="300000" refresh="false" show-wait-dialog="false">
279
+ <action name="runscript">
280
+ <script language="javascript">
281
+ // Tarea que se ejecuta cada 5 minutos (300000 ms) en segundo plano
282
+ replica.start();
283
+ </script>
284
+ </action>
285
+ </maintenance>
286
+
287
+ <replica-ok table="Pedidos" refresh="false" show-wait-dialog="false">
288
+ <action name="runscript">
289
+ <script language="javascript">
290
+ // Se ejecuta cuando termina la replica de la tabla Pedidos
291
+ ui.showToast("Pedidos sincronizados");
292
+ </script>
293
+ </action>
294
+ </replica-ok>
295
+ ```
296
+
297
+ ---
298
+
299
+ ### 8.6 Eventos Especiales de Aplicación
300
+
301
+ Estos eventos controlan el comportamiento global de la app cuando cambia de estado en el sistema operativo.
302
+
303
+ #### `on-app-foreground` — App vuelve a primer plano
304
+
305
+ ```xml
306
+ <on-app-foreground refresh="false" show-wait-dialog="false">
307
+ <action name="runscript">
308
+ <script language="javascript">
309
+ // La app vuelve al primer plano
310
+ // Comprobar inactividad y relanzar timer si procede
311
+ let nTime = ui.getInactivityTime();
312
+ if (nTime >= jsconst_oper.tiempoInactividad) {
313
+ var objVisible = getLoginInStack();
314
+ if (isNothing(objVisible)) {
315
+ let objCrear = new LoginColl_Inactivity();
316
+ ui.openEditView(objCrear);
317
+ }
318
+ }
319
+ let inactividad = setInactivityTimer(jsconst_oper.tiempoInactividad);
320
+ </script>
321
+ </action>
322
+ </on-app-foreground>
323
+ ```
324
+
325
+ #### `on-app-background` — App pasa a segundo plano
326
+
327
+ ```xml
328
+ <on-app-background refresh="false" show-wait-dialog="false">
329
+ <action name="runscript">
330
+ <script language="javascript">
331
+ // La app pasa a segundo plano
332
+ // Detener el timer de inactividad
333
+ ui.removeInactivityTimer();
334
+ </script>
335
+ </action>
336
+ </on-app-background>
337
+ ```
338
+
339
+ ---
340
+
341
+ ### 8.7 Inactividad de Sesión
342
+
343
+ Para implementar bloqueo por inactividad, definir el código en el nodo `entryPoint` o en `onlogon`:
344
+
345
+ ```javascript
346
+ // Lanzar el contador de inactividad
347
+ function setInactivityTimer(nValue) {
348
+ ui.setInactivityTimer({
349
+ timeout: nValue, // Tiempo en milisegundos
350
+ callback: function() {
351
+ var objVisible = getLoginInStack();
352
+ if (isNothing(objVisible)) {
353
+ let objCrear = new LoginColl_Inactivity();
354
+ ui.openEditView(objCrear);
355
+ }
356
+ return 3;
357
+ }
358
+ });
359
+ }
360
+
361
+ // Obtener tiempo de inactividad actual
362
+ let nTime = ui.getInactivityTime();
363
+
364
+ // Detener el timer
365
+ ui.removeInactivityTimer();
366
+ ```
367
+
368
+ ---
369
+
370
+ ### 8.8 Eventos en Controles (bind)
371
+
372
+ Los eventos de controles especificos se definen como atributos en el prop o mediante `bind()` en JavaScript.
373
+
374
+ > Los eventos se nombran todo en **minuscula** cuando se definen como atributos XML, y es indiferente cuando se usa `bind()`.
375
+
376
+ **Propiedades comunes a todos los objetos de eventos:**
377
+
378
+ | Propiedad | Descripción |
379
+ |-----------|-------------|
380
+ | `target` | Propiedad objetivo que lanzo el evento |
381
+ | `objItem` | DataObject que lanzo el evento |
382
+ | `data` | Objeto JavaScript extra definido por el usuario al hacer `bind()` |
383
+
384
+ #### Eventos por tipo de control
385
+
386
+ | Control | Evento | Descripción |
387
+ |---------|--------|-------------|
388
+ | `prop type="B"` | `onclick` | Clic en botón. Parámetros: `x`, `y` (posición en pantalla) |
389
+ | `prop type="B"` | `onlongpress` | Pulsación larga en botón. Parámetros: `x`, `y` |
390
+ | `contents` | `onlongpressitem` | Pulsación larga en elemento de lista. Parámetro: `position` |
391
+ | `contents` | `onselecteditem` | Selección de elemento en lista. Parámetro: `position` |
392
+ | `prop type="T"` | `ontextchanged` | Cambio de texto. Parámetros: `target`, `keyPressed`, `oldText`, `newText` |
393
+ | `prop type="T"` | `ontextlengthchanged` | Cambio de longitud del texto. Parámetro: `length` |
394
+ | `prop type="T"` | `onfocuschanged` | Cambio de foco. Parámetro: `isFocused` |
395
+ | `prop type="T"` | `oneditoraction` | Pulsación de tecla intro/siguiente en teclado |
396
+ | `prop type="WEB"` | `onconsolemessage` | Errores del WebView. Parámetros: `messageLevel`, `message`, `lineNumber`, `sourceId` |
397
+ | `frame` | `onscroll` | Scroll en frame. Parámetros: `dx`, `dy`, `scrollX`, `scrollY`, `width`, `height` |
398
+ | `contents viewmode="picturemap"` | `ontouch` | Toque en mapa de imagen. Parámetros: `x`, `y`, `translateX`, `translateY`, `scale` |
399
+
400
+ **Ejemplo `ontextchanged`:**
401
+
402
+ ```javascript
403
+ function eventoOnTextChanged(evento) {
404
+ ui.showToast("Cambio en: " + evento.target +
405
+ " | Tecla: " + evento.keyPressed +
406
+ " | Texto anterior: " + evento.oldText +
407
+ " | Texto nuevo: " + evento.newText);
408
+ }
409
+ ```
410
+
411
+ **Ejemplo `onconsolemessage` para errores WebView:**
412
+
413
+ ```xml
414
+ <prop type="WEB" onconsolemessage="handleError(e);" ... />
415
+ ```
416
+ ```javascript
417
+ function handleError(e) {
418
+ if (e.messageLevel === "ERROR") {
419
+ ui.msgBox("Nivel: " + e.messageLevel +
420
+ "\nMensaje: " + e.message +
421
+ "\nLinea: " + e.lineNumber, "Error WebView", 0);
422
+ }
423
+ }
424
+ ```
425
+
426
+ ---
427
+
428
+ ### 8.9 Permisos de la Aplicación (solo Android)
429
+
430
+ A partir de Android 6.x los permisos no se conceden al instalar la app sino en tiempo de ejecución (runtime). XOne solicita los permisos automáticamente antes de ejecutar `openEditView` si la coleccion tiene definido el nodo `<permissions>`.
431
+
432
+ **Regla:** Poner `<permissions>` en la coleccion `login-coll` o en el `entry-point` para que estén disponibles desde el inicio. Si un permiso ya fue concedido, no se vuelve a solicitar.
433
+
434
+ > Los permisos son **obligatorios**: si el usuario los deniega, no puede entrar en la coleccion donde se solicitaron.
435
+
436
+ #### Nodo `<permissions>` — lista de permisos disponibles
437
+
438
+ ```xml
439
+ <permissions>
440
+ <!-- Acceso a almacenamiento externo/SDCard. Necesario para replica y funcionamiento general -->
441
+ <permission name="external-storage" />
442
+
443
+ <!-- Acceso al hardware de telefonia: llamadas, IMEI y otros identificadores -->
444
+ <permission name="phone" />
445
+
446
+ <!-- Acceso a la camara. Necesario para foto, QR, OCR, etc. -->
447
+ <permission name="camera" />
448
+
449
+ <!-- GPS y localización por wifi. Permanece activo aunque la app este en segundo plano -->
450
+ <permission name="location" />
451
+
452
+ <!-- Solo coordenadas en primer plano (menos intrusivo que location) -->
453
+ <permission name="location-foreground" />
454
+
455
+ <!-- Leer y escribir eventos en el calendario del usuario -->
456
+ <permission name="calendar" />
457
+
458
+ <!-- Leer los contactos del dispositivo -->
459
+ <permission name="contacts" />
460
+
461
+ <!-- Acceso al microfono -->
462
+ <permission name="microphone" />
463
+
464
+ <!-- Recibir notificaciones push. OBLIGATORIO para Android >= 13 en instalaciones nuevas -->
465
+ <permission name="notifications" />
466
+
467
+ <!-- Bluetooth. OBLIGATORIO para Android >= 12 si se usan impresoras u otros dispositivos BT -->
468
+ <permission name="bluetooth" />
469
+
470
+ <!-- SMS: lectura y envio. CUIDADO: Google Play NO permite este permiso salvo causa justificada -->
471
+ <!-- <permission name="sms" /> -->
472
+ </permissions>
473
+ ```
474
+
475
+ #### Tabla de permisos y cuando usarlos
476
+
477
+ | Permiso | Cuando es necesario |
478
+ |---------|---------------------|
479
+ | `external-storage` | Siempre que haya replica o se gestionen ficheros |
480
+ | `phone` | Para obtener IMEI/identificador de dispositivo |
481
+ | `camera` | Fotos (`type="PH"`), lector QR (`type="VD" code-type="qr"`), OCR (`XOneOCR`) |
482
+ | `location` | GPS en segundo plano, tracking de posición |
483
+ | `location-foreground` | GPS solo mientras la app esta en pantalla |
484
+ | `calendar` | Integración con el calendario del dispositivo |
485
+ | `contacts` | Acceso a contactos del dispositivo. Necesario para la fuente de datos `Contacts` (ver §"Leer y escribir los contactos del teléfono") |
486
+ | `microphone` | Grabacion de audio/voz |
487
+ | `notifications` | Android >= 13, instalaciones nuevas |
488
+ | `bluetooth` | Android >= 12, impresoras BT (`XOnePrinter`), dispositivos serie |
489
+
490
+ #### Funciones JavaScript relacionadas con permisos y seguridad
491
+
492
+ **Desactivar optimización de batería** — necesario para apps que deben seguir ejecutandose en segundo plano (GPS, replica periódica, notificaciones):
493
+
494
+ ```javascript
495
+ function requestIgnoreBatteryOptimizations() {
496
+ var bResult = systemSettings.isIgnoringBatteryOptimizations();
497
+ if (!bResult) {
498
+ ui.showToast("Por favor desactive el ahorro de bateria para la aplicación");
499
+ systemSettings.requestIgnoreBatteryOptimizations();
500
+ }
501
+ }
502
+ ```
503
+
504
+ **Comprobar estado del GPS** — verificar si el usuario tiene la localización activada antes de usarla:
505
+
506
+ ```javascript
507
+ function comprobarEstadoGps() {
508
+ var sDeviceOs = appData.getGlobalMacro("##DEVICE_OS##");
509
+ if (sDeviceOs === "android") {
510
+ var nStatus = ui.checkGpsStatus();
511
+ switch (nStatus) {
512
+ case 0:
513
+ ui.showToast("No hay GPS, no se puede activar.");
514
+ break;
515
+ case 1:
516
+ // Localización GPS activa — OK
517
+ break;
518
+ case 2:
519
+ // Localización por redes wifi/telefonia activa — OK
520
+ break;
521
+ case 3:
522
+ // Sin GPS ni redes: pedir al usuario que lo active
523
+ ui.showToast("No esta activado el GPS ni la ubicación por redes. Activelo.");
524
+ ui.askUserForGpsPermission();
525
+ break;
526
+ case 4:
527
+ // GPS + redes activos — OK
528
+ break;
529
+ default:
530
+ break;
531
+ }
532
+ }
533
+ }
534
+ ```
535
+
536
+ | Valor `checkGpsStatus()` | Significado |
537
+ |--------------------------|-------------|
538
+ | `0` | Sin GPS en el dispositivo |
539
+ | `1` | GPS activo |
540
+ | `2` | Localización por wifi/redes activa |
541
+ | `3` | Sin localización activada → llamar a `askUserForGpsPermission()` |
542
+ | `4` | GPS + redes activos |
543
+ | `-1` | Error inesperado |
544
+
545
+ ---
546
+
547
+ ### 8.10 Referencia Rápida de Eventos por Ubicación
548
+
549
+ | Ubicación | Eventos disponibles |
550
+ |-----------|---------------------|
551
+ | **Cualquier coleccion** | `create`, `insert`, `before-edit`, `after-edit`, `onchange`, `delete`, `onback`, `selecteditem`, `load`, `auto-selecteditem` |
552
+ | **Solo coleccion Empresas** | `onlogon`, `onlogoff`, `maintenance`, `replica-ok`, `sys-message`, `onpushnotificationclick`, `onmessage`, `onrecovery`, `after-recovery-login` |
553
+ | **Solo Android (cualquier coll)** | `onback` |
554
+ | **Coleccion con entryPoint** | `on-app-foreground`, `on-app-background` |
555
+ | **Controles (atributo o bind)** | `onclick`, `onlongpress`, `ontextchanged`, `onfocuschanged`, `oneditoraction`, `onscroll`, `onconsolemessage`, `ontouch` |
556
+
557
+ ---
558
+
559
+ ## 10. Fase 9: Funciones JavaScript
560
+
561
+ ### 9.1 Objetivo
562
+
563
+ Crear el archivo `functions.js` con las funciones JavaScript globales de la aplicación.
564
+
565
+ ### 9.2 Reglas Obligatorias
566
+
567
+ 1. **Solo usar API documentada**: `ui.*`, `appData.*`, `self.*`, `$http.*`, `crypto.*`
568
+ 2. **NO usar APIs web del DOM**: `document.*`, `window.*`, `localStorage.*`, `XMLHttpRequest`, `navigator.*`
569
+ 3. **Async**: el patrón XOne idiomático son callbacks. Si la situación lo requiere, también está disponible `Promise` (ES2024 completo) o `fetch`. `async`/`await` todavía NO es parseable.
570
+ 4. **Objeto `self`**: Referencia al DataObject actual en el contexto del script
571
+
572
+ ### 9.3 Plantilla: functions.js
573
+
574
+ ```javascript
575
+ /**
576
+ * Funciones globales del proyecto
577
+ * NombreProyecto - v1.0.0
578
+ */
579
+
580
+ // ============================================
581
+ // CONSTANTES DEL PROYECTO
582
+ // ============================================
583
+
584
+ var APP_VERSION = "1.0.0";
585
+ var COLOR_PRIMARIO = "#2196F3";
586
+ var COLOR_EXITO = "#4CAF50";
587
+ var COLOR_ERROR = "#F44336";
588
+
589
+ // ============================================
590
+ // UTILIDADES GENERALES
591
+ // ============================================
592
+
593
+ /**
594
+ * Verifica si un valor esta vacio
595
+ * @param {*} val - Valor a verificar
596
+ * @returns {boolean}
597
+ */
598
+ function isEmpty(val) {
599
+ return val === undefined || val === null || val === "";
600
+ }
601
+
602
+ /**
603
+ * Conversion segura a string
604
+ * @param {*} val - Valor a convertir
605
+ * @returns {string}
606
+ */
607
+ function cstr(val) {
608
+ if (val === undefined || val === null) return "";
609
+ return val.toString();
610
+ }
611
+
612
+ /**
613
+ * Conversion segura a numero
614
+ * @param {*} val - Valor a convertir
615
+ * @returns {number}
616
+ */
617
+ function cnum(val) {
618
+ if (val === undefined || val === null) return 0;
619
+ var n = parseFloat(val);
620
+ return isNaN(n) ? 0 : n;
621
+ }
622
+
623
+ // getControl(name, [dataObject]) es NATIVA del motor (Rhino y V8).
624
+ // NO declararla en functions.js. Firma:
625
+ // getControl(name) → control en la última ventana visible.
626
+ // getControl(name, dataObject) → control en la ventana asociada a ese DataObject.
627
+ // Semántica estricta: lanza error si el nombre está vacío, el control no existe,
628
+ // no hay ventana destino, o el dataObject no es válido.
629
+ // Si un proyecto legacy ya tiene su propio "function getControl(...)", esa
630
+ // declaración sombrea a la nativa en su scope local sin tocar la global.
631
+
632
+ // ============================================
633
+ // NAVEGACION
634
+ // ============================================
635
+
636
+ /**
637
+ * Abre una pantalla/colección
638
+ * @param {string} sNombreColl - Nombre de la colección a abrir
639
+ */
640
+ function abrirPantalla(sNombreColl) {
641
+ ui.openEditView(sNombreColl); // crea internamente el dataObject + AddItem y abre su EditView
642
+ }
643
+
644
+ /**
645
+ * Cierra la pantalla actual
646
+ */
647
+ function cerrarPantalla() {
648
+ var window = ui.getView(self);
649
+ if (window) {
650
+ window.exit();
651
+ }
652
+ }
653
+
654
+ /**
655
+ * Muestra un grupo con animacion
656
+ * @param {number} nGroup - Indice del grupo (base 0)
657
+ * @param {string} sAnimIn - Animacion de entrada
658
+ * @param {string} sAnimOut - Animacion de salida
659
+ */
660
+ function mostrarGrupo(nGroup, sAnimIn, sAnimOut) {
661
+ sAnimIn = sAnimIn || "##ALPHA_IN##";
662
+ sAnimOut = sAnimOut || "##ALPHA_OUT##";
663
+ ui.showGroup(nGroup, sAnimIn, 200, sAnimOut, 200);
664
+ }
665
+
666
+ // ============================================
667
+ // MENSAJES Y DIALOGOS
668
+ // ============================================
669
+
670
+ /**
671
+ * Muestra un mensaje de confirmacion Si/No
672
+ * @param {string} sMensaje - Mensaje a mostrar
673
+ * @param {string} sTitulo - Titulo del dialogo
674
+ * @returns {boolean} - true si el usuario acepto
675
+ */
676
+ function confirmar(sMensaje, sTitulo) {
677
+ sTitulo = sTitulo || "Confirmar";
678
+ var nResult = ui.msgBox(sMensaje, sTitulo, 4);
679
+ return nResult == 6; // 6=Si, 7=No
680
+ }
681
+
682
+ /**
683
+ * Muestra un toast simple
684
+ * @param {string} sMensaje - Mensaje a mostrar
685
+ */
686
+ function mostrarToast(sMensaje) {
687
+ ui.showToast(sMensaje);
688
+ }
689
+
690
+ /**
691
+ * Muestra un mensaje informativo
692
+ * @param {string} sMensaje - Mensaje a mostrar
693
+ * @param {string} sTitulo - Titulo del dialogo
694
+ */
695
+ function mostrarMensaje(sMensaje, sTitulo) {
696
+ sTitulo = sTitulo || "Información";
697
+ ui.msgBox(sMensaje, sTitulo, 0);
698
+ }
699
+
700
+ // ============================================
701
+ // COLECCIONES Y DATOS
702
+ // ============================================
703
+
704
+ /**
705
+ * Obtiene una coleccion por nombre
706
+ * @param {string} sNombreColl - Nombre de la coleccion
707
+ * @returns {object}
708
+ */
709
+ function obtenerColeccion(sNombreColl) {
710
+ return appData.getCollection(sNombreColl);
711
+ }
712
+
713
+ /**
714
+ * Crea un nuevo objeto en una coleccion
715
+ * @param {string} sNombreColl - Nombre de la coleccion
716
+ * @returns {object} - Nuevo objeto creado
717
+ */
718
+ function crearObjeto(sNombreColl) {
719
+ var coll = appData.getCollection(sNombreColl);
720
+ var obj = coll.createObject();
721
+ coll.addItem(obj);
722
+ return obj;
723
+ }
724
+
725
+ /**
726
+ * Busca un objeto en una coleccion
727
+ * @param {string} sNombreColl - Nombre de la coleccion
728
+ * @param {string} sFiltro - Condicion SQL de busqueda
729
+ * @returns {object|null}
730
+ */
731
+ function buscarObjeto(sNombreColl, sFiltro) {
732
+ var coll = appData.getCollection(sNombreColl);
733
+ return coll.findObject(sFiltro);
734
+ }
735
+
736
+ // ============================================
737
+ // VALIDACIONES
738
+ // ============================================
739
+
740
+ /**
741
+ * Valida que los campos obligatorios no esten vacios
742
+ * @param {object} obj - Objeto a validar
743
+ * @param {array} aCampos - Array de nombres de campos
744
+ * @returns {boolean} - true si todos tienen valor
745
+ */
746
+ function validarObligatorios(obj, aCampos) {
747
+ for (var i = 0; i < aCampos.length; i++) {
748
+ if (isEmpty(obj[aCampos[i]])) {
749
+ ui.showToast("El campo " + aCampos[i] + " es obligatorio");
750
+ return false;
751
+ }
752
+ }
753
+ return true;
754
+ }
755
+
756
+ /**
757
+ * Valida formato de email basico
758
+ * @param {string} sEmail - Email a validar
759
+ * @returns {boolean}
760
+ */
761
+ function validarEmail(sEmail) {
762
+ if (isEmpty(sEmail)) return false;
763
+ return sEmail.indexOf("@") > 0 && sEmail.indexOf(".") > 0;
764
+ }
765
+
766
+ // ============================================
767
+ // FORMATO DE FECHAS
768
+ // ============================================
769
+
770
+ /**
771
+ * Formatea una fecha como DD/MM/YYYY
772
+ * @param {Date} dFecha - Fecha a formatear
773
+ * @returns {string}
774
+ */
775
+ function formatearFecha(dFecha) {
776
+ if (!dFecha) return "";
777
+ var dd = dFecha.getDate();
778
+ var mm = dFecha.getMonth() + 1;
779
+ var yyyy = dFecha.getFullYear();
780
+ if (dd < 10) dd = "0" + dd;
781
+ if (mm < 10) mm = "0" + mm;
782
+ return dd + "/" + mm + "/" + yyyy;
783
+ }
784
+
785
+ /**
786
+ * Formatea una fecha como DD/MM/YYYY HH:MM
787
+ * @param {Date} dFecha - Fecha a formatear
788
+ * @returns {string}
789
+ */
790
+ function formatearFechaHora(dFecha) {
791
+ if (!dFecha) return "";
792
+ var sFecha = formatearFecha(dFecha);
793
+ var hh = dFecha.getHours();
794
+ var mi = dFecha.getMinutes();
795
+ if (hh < 10) hh = "0" + hh;
796
+ if (mi < 10) mi = "0" + mi;
797
+ return sFecha + " " + hh + ":" + mi;
798
+ }
799
+ ```
800
+
801
+ ### 9.4 Referencia Rápida de API JavaScript XOne
802
+
803
+ #### Objeto `ui` (Interfaz de Usuario)
804
+
805
+ | Función | Descripción |
806
+ |---------|-------------|
807
+ | `ui.msgBox(msg, título, tipo)` | Dialogo (tipo 4=Si/No, retorna 6=Si) |
808
+ | `ui.showToast(msg)` | Mensaje rápido |
809
+ | `ui.showWaitDialog(msg)` | Indicador de carga |
810
+ | `ui.hideWaitDialog()` | Ocultar indicador |
811
+ | `ui.getView(self)` | Obtener ventana actual |
812
+ | `ui.openEditView(obj)` | Abrir pantalla. Acepta un dataObject (lo abre en EditView) o un nombre de coll (crea internamente el objeto y abre su EditView). 2º arg `exit=true` cierra la vista origen. **Patrón principal de navegación**. |
813
+ | `ui.openMenu(coll, mask, 0)` | Caso especial: abrir directamente la LISTA (`MainListCollectionActivity`) de una coll en vez de su EditView. Constantes `mask`: ADD=0x01, EDIT=0x02, DELETE=0x04, VIEW=0x200, FULLMASK=0xFFFFFF |
814
+ | `ui.refresh()` | Refrescar UI |
815
+ | `ui.refresh("campo")` | Refrescar campo |
816
+ | `ui.showGroup(n)` | Mostrar grupo/tab |
817
+ | `ui.startGps(options)` | Iniciar GPS |
818
+ | `ui.showDatePicker(options)` | Selector de fecha |
819
+ | `ui.showTimePicker(options)` | Selector de hora |
820
+ | `ui.scanDocument(params)` | Escáner de documentos del sistema (encuadre guiado, recorte, filtros, varias páginas). Params: `mode` (`"base"`/`"baseWithFilters"`/`"full"`), `pageLimit`, `allowGallery`, `outputJpg`/`outputPdf` (al menos uno activo), callbacks `onSuccess` (array de nombres de fichero en `appData.getFilesPath()` con prefijo `scan_`), `onError` y `onCancelled` (ambos obligatorios). Requiere Google Play Services |
821
+ | `ui.recognizeText(params)` | OCR (alfabeto latino) de una imagen del dispositivo. Params: `path` (obligatorio), `onSuccess`/`onError` (obligatorios), `roi` (`{left, top, width, height}`), `scale`, `grayscale`, `detail` (devuelve líneas con `confidence` y posición). Encaja detrás de `scanDocument` para leer lo escaneado |
822
+
823
+ #### Objeto `appData` (Datos de Aplicación)
824
+
825
+ | Función | Descripción |
826
+ |---------|-------------|
827
+ | `appData.getCollection(nombre)` | Obtener coleccion |
828
+ | `appData.login(options)` | Iniciar sesión |
829
+ | `appData.logout()` | Cerrar sesión |
830
+ | `appData.exit()` | Salir de la app |
831
+ | `appData.getAppPath()` | Ruta de la app |
832
+ | `appData.getFilesPath()` | Ruta de archivos |
833
+ | `appData.getGlobalMacro(macro)` | Obtener macro global |
834
+ | `appData.failWithMessage(code, msg)` | Fallo controlado |
835
+
836
+ #### Objeto `self` (Objeto de Datos Actual)
837
+
838
+ | Propiedad/Función | Descripción |
839
+ |-------------------|-------------|
840
+ | `self.CAMPO` | Leer/escribir campo |
841
+ | `self.save()` | Guardar cambios |
842
+ | `self.getOwnerCollection()` | Obtener coleccion |
843
+ | `self.getContents("@nombre")` | Obtener contents |
844
+ | `self.toJsonString()` | Convertir a JSON |
845
+
846
+ #### Colecciones
847
+
848
+ | Función | Descripción |
849
+ |---------|-------------|
850
+ | `coll.loadAll()` | Cargar todos los registros |
851
+ | `coll.getCount()` | Cantidad de registros |
852
+ | `coll.get(índice)` | Obtener por índice |
853
+ | `coll.findObject(filtro)` | Buscar objeto |
854
+ | `coll.createObject()` | Crear objeto (legacy; el patrón preferido es `new NombreColeccion({...})`) |
855
+ | `coll.addItem(obj)` | Agregar objeto |
856
+ | `coll.deleteItem(índice)` | Eliminar objeto |
857
+ | `coll.setFilter(filtro)` | Aplicar filtro |
858
+ | `coll.doSort(campo)` | Ordenar |
859
+ | `coll.lock()` / `coll.unlock()` | Bloquear/desbloquear |
860
+ | `coll.saveAll()` | Guardar todos |
861
+