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.
- package/README.md +167 -0
- package/dist/agent/authEnDisco.js +56 -0
- package/dist/agent/authEnDisco.js.map +1 -0
- package/dist/agent/configEnDisco.js +109 -0
- package/dist/agent/configEnDisco.js.map +1 -0
- package/dist/agent/crearProyecto.js +34 -0
- package/dist/agent/crearProyecto.js.map +1 -0
- package/dist/agent/entorno.js +124 -0
- package/dist/agent/entorno.js.map +1 -0
- package/dist/agent/guionizado.js +53 -0
- package/dist/agent/guionizado.js.map +1 -0
- package/dist/agent/instantanea.js +129 -0
- package/dist/agent/instantanea.js.map +1 -0
- package/dist/agent/interrupts.js +83 -0
- package/dist/agent/interrupts.js.map +1 -0
- package/dist/agent/mensajes.js +24 -0
- package/dist/agent/mensajes.js.map +1 -0
- package/dist/agent/modelos.js +42 -0
- package/dist/agent/modelos.js.map +1 -0
- package/dist/agent/normalizar.js +20 -0
- package/dist/agent/normalizar.js.map +1 -0
- package/dist/agent/perfiles.js +103 -0
- package/dist/agent/perfiles.js.map +1 -0
- package/dist/agent/proyecto.js +107 -0
- package/dist/agent/proyecto.js.map +1 -0
- package/dist/agent/puente.js +142 -0
- package/dist/agent/puente.js.map +1 -0
- package/dist/agent/resumenDeTool.js +48 -0
- package/dist/agent/resumenDeTool.js.map +1 -0
- package/dist/agent/skills.js +65 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/textoDeTool.js +113 -0
- package/dist/agent/textoDeTool.js.map +1 -0
- package/dist/agent/turnoReal.js +173 -0
- package/dist/agent/turnoReal.js.map +1 -0
- package/dist/agent/verificador.js +65 -0
- package/dist/agent/verificador.js.map +1 -0
- package/dist/agent/xoneAgent.js +106 -0
- package/dist/agent/xoneAgent.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/aprobar.js +82 -0
- package/dist/cli/aprobar.js.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/consola.js +346 -0
- package/dist/cli/consola.js.map +1 -0
- package/dist/cli/describe.js +53 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/doctor.js +45 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +520 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/markdown.js +90 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/run.js +164 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spinner.js +89 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/stdio.js +193 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/tema.js +40 -0
- package/dist/cli/tema.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/core/bitacora.js +30 -0
- package/dist/core/bitacora.js.map +1 -0
- package/dist/core/config.js +236 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contextos.js +56 -0
- package/dist/core/contextos.js.map +1 -0
- package/dist/core/deps.js +51 -0
- package/dist/core/deps.js.map +1 -0
- package/dist/core/diff.js +79 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/esqueleto.js +366 -0
- package/dist/core/esqueleto.js.map +1 -0
- package/dist/core/events.js +2 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/modelos.js +85 -0
- package/dist/core/modelos.js.map +1 -0
- package/dist/core/notify.js +106 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/ports.js +114 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/turno.js +127 -0
- package/dist/core/turno.js.map +1 -0
- package/dist/vendor/hitl.js +103 -0
- package/dist/vendor/hitl.js.map +1 -0
- package/dist/vendor/skillLoaders/catalog.js +74 -0
- package/dist/vendor/skillLoaders/catalog.js.map +1 -0
- package/dist/vendor/tokenTracking.js +58 -0
- package/dist/vendor/tokenTracking.js.map +1 -0
- package/package.json +41 -0
- package/skills/xone-debugging/SKILL.md +52 -0
- package/skills/xone-debugging/references/faq.md +561 -0
- package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
- package/skills/xone-development/SKILL.md +105 -0
- package/skills/xone-development/references/anti-patrones.md +94 -0
- package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
- package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
- package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
- package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
- package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
- package/skills/xone-development/references/datos/appdata.md +611 -0
- package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
- package/skills/xone-development/references/datos/http.md +366 -0
- package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
- package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
- package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
- package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
- package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
- package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
- package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
- package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
- package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
- package/skills/xone-development/references/indice-completo.md +77 -0
- package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
- package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
- package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
- package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
- package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
- package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
- package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
- package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
- package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
- package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
- package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
- package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
- package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
- package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
- package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
- package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
- package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
- package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
- package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
- package/skills/xone-development/references/tipos-de-prop.md +28 -0
- package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
- package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
- package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
- package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
- package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
- package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
- package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
- package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
- package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
- package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
- package/skills/xone-development/references/xml-ui/mapas.md +685 -0
- package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
- package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
- package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
- package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
- package/skills/xone-plan-builder/SKILL.md +183 -0
- package/skills/xone-plan-builder/agents/openai.yaml +5 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
- package/skills/xone-project-generator/SKILL.md +111 -0
- package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
- package/skills/xone-project-generator/references/colecciones-base.md +16 -0
- package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
- package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
- package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
- package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
- package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
- package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
- package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
- package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
- package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
- package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
- package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
- package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
- package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
- package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
- package/skills/xone-review/SKILL.md +189 -0
- package/skills/xone-spec-builder/SKILL.md +235 -0
- package/skills/xone-spec-builder/agents/openai.yaml +5 -0
- package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
- package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
- 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
|
+
|