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,647 @@
1
+ # XOne JavaScript — Plantillas de código y funciones utilitarias
2
+
3
+ > Fuente: `xone/v2/xone-help-docs/topics/03e-js-patrones-buenas-practicas.md` §12–§13. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
4
+
5
+ Contenido: §12 plantillas completas: CRUD, filtrado, maestro-detalle, GPS, fotos, chat, QR, login personalizado · §13 funciones utilitarias recomendadas para functions.js
6
+
7
+ ---
8
+
9
+ ## 12. Patrones Comunes con Ejemplos
10
+
11
+ ### 12.1 CRUD Completo
12
+
13
+ ```javascript
14
+ // === CREAR ===
15
+ function crearProducto() {
16
+ if (!validarRequerido(self.MAP_NOMBRE, "Nombre")) return;
17
+ if (!validarRequerido(self.MAP_PRECIO, "Precio")) return;
18
+
19
+ let coll = appData.getCollection("Productos");
20
+ coll.unlock();
21
+ try {
22
+ let obj = new Productos({
23
+ MAP_NOMBRE: cstr(self.MAP_NOMBRE),
24
+ MAP_PRECIO: cnum(self.MAP_PRECIO),
25
+ MAP_ACTIVO: 1,
26
+ MAP_FECHA_ALTA: new Date()
27
+ });
28
+ coll.addItem(obj);
29
+ obj.save();
30
+ ui.showToast("Producto creado");
31
+ cerrarPantalla();
32
+ } catch(ex) {
33
+ ui.showToast("Error: " + ex);
34
+ } finally {
35
+ coll.lock();
36
+ }
37
+ }
38
+
39
+ // === LEER ===
40
+ function buscarProducto(codigo) {
41
+ let coll = appData.getCollection("Productos");
42
+ let escapado = cstr(codigo).replace(/'/g, "''");
43
+ return coll.findObject("MAP_CODIGO = '" + escapado + "'");
44
+ }
45
+
46
+ function listarProductosActivos() {
47
+ let coll = appData.getCollection("Productos");
48
+ coll.setFilter("MAP_ACTIVO = 1");
49
+ coll.clear();
50
+ coll.loadAll();
51
+ coll.doSort("MAP_NOMBRE ASC");
52
+
53
+ let items = [];
54
+ for (let i = 0; i < coll.getCount(); i++) {
55
+ items.push(coll.get(i));
56
+ }
57
+ return items;
58
+ }
59
+
60
+ // === ACTUALIZAR ===
61
+ function actualizarProducto(producto) {
62
+ if (!producto) return;
63
+ producto.MAP_FECHA_MOD = new Date();
64
+ producto.save();
65
+ ui.showToast("Producto actualizado");
66
+ }
67
+
68
+ // === ELIMINAR ===
69
+ function eliminarProducto(producto) {
70
+ if (!producto) return;
71
+ if (!confirmar("Eliminar " + producto.MAP_NOMBRE + "?", "Eliminar")) return;
72
+
73
+ let coll = appData.getCollection("Productos");
74
+ for (let i = 0; i < coll.getCount(); i++) {
75
+ if (coll.get(i).ID == producto.ID) {
76
+ coll.deleteItem(i);
77
+ break;
78
+ }
79
+ }
80
+ ui.showToast("Producto eliminado");
81
+ }
82
+ ```
83
+
84
+ ### 12.2 Filtrado Dinámico
85
+
86
+ ```javascript
87
+ function buscarProductos(criterio, precioMin, precioMax, activo) {
88
+ let filtros = [];
89
+
90
+ if (!isEmpty(criterio)) {
91
+ let escapado = cstr(criterio).replace(/'/g, "''");
92
+ filtros.push("MAP_NOMBRE LIKE '%" + escapado + "%'");
93
+ }
94
+ if (!isEmpty(precioMin)) filtros.push("MAP_PRECIO >= " + cnum(precioMin));
95
+ if (!isEmpty(precioMax)) filtros.push("MAP_PRECIO <= " + cnum(precioMax));
96
+ if (!isEmpty(activo)) filtros.push("MAP_ACTIVO = " + cnum(activo));
97
+
98
+ let coll = appData.getCollection("Productos");
99
+ coll.setFilter(filtros.length > 0 ? filtros.join(" AND ") : "");
100
+ coll.clear();
101
+ coll.loadAll();
102
+ coll.doSort("MAP_NOMBRE ASC");
103
+ ui.refresh("MAP_LISTA_PRODUCTOS");
104
+ }
105
+ ```
106
+
107
+ ### 12.3 Navegación Maestro-Detalle
108
+
109
+ ```javascript
110
+ function onItemSeleccionado() {
111
+ let collDetalle = appData.getCollection("DetalleProducto");
112
+ let obj = new DetalleProducto({
113
+ MAP_NOMBRE: self.MAP_NOMBRE,
114
+ MAP_PRECIO: self.MAP_PRECIO,
115
+ MAP_ID_PRODUCTO: self.ID
116
+ });
117
+ collDetalle.addItem(obj);
118
+
119
+ ui.openEditView(obj);
120
+ }
121
+
122
+ function cargarDetalle() {
123
+ let idProducto = self.MAP_ID_PRODUCTO;
124
+ if (isEmpty(idProducto)) return;
125
+
126
+ let coll = appData.getCollection("Productos");
127
+ let producto = coll.findObject("ID = " + idProducto);
128
+ if (producto) {
129
+ let lineas = producto.getContents("@LineasProducto");
130
+ lineas.unlock();
131
+ lineas.clear();
132
+ lineas.loadAll();
133
+ lineas.lock();
134
+ }
135
+ }
136
+ ```
137
+
138
+ ### 12.4 Manejo de GPS en Tiempo Real
139
+
140
+ ```javascript
141
+ var rastreoActivo = false;
142
+
143
+ function iniciarRastreo() {
144
+ let estadoGPS = ui.checkGpsStatus();
145
+ if (estadoGPS == 0 || estadoGPS == 3) {
146
+ ui.askUserForGpsPermission({
147
+ onEnabled: function() { activarGPS(); },
148
+ onDenied : function() { ui.showToast("Se necesita GPS"); }
149
+ });
150
+ } else {
151
+ activarGPS();
152
+ }
153
+ }
154
+
155
+ function activarGPS() {
156
+ ui.startGps({
157
+ nodeName : "onPosicionActualizada",
158
+ timeBetweenUpdates : 5000,
159
+ minimumMetersDistanceRange: 10,
160
+ foreground : true,
161
+ title : "Rastreo GPS",
162
+ text : "Registrando ubicación..."
163
+ });
164
+ rastreoActivo = true;
165
+ ui.showToast("Rastreo GPS iniciado");
166
+ }
167
+
168
+ function detenerRastreo() {
169
+ ui.stopGps();
170
+ rastreoActivo = false;
171
+ ui.showToast("Rastreo GPS detenido");
172
+ }
173
+
174
+ // Callback GPS (nodo "onPosicionActualizada" en XML)
175
+ function onPosicionRecibida() {
176
+ let collGps = appData.getCollection("GPSColl");
177
+ collGps.startBrowse();
178
+ try {
179
+ let gps = collGps.getCurrentItem();
180
+ if (!gps || gps.STATUS != 1) return;
181
+
182
+ self.MAP_LATITUD = gps.LATITUD;
183
+ self.MAP_LONGITUD = gps.LONGITUD;
184
+ self.MAP_VELOCIDAD = gps.VELOCIDAD;
185
+ ui.refresh("MAP_LATITUD,MAP_LONGITUD,MAP_VELOCIDAD");
186
+
187
+ guardarPosicion(gps.LATITUD, gps.LONGITUD, gps.PRECISION);
188
+
189
+ let mapControl = getControl("MAP_MAPA");
190
+ if (mapControl) {
191
+ mapControl.zoomTo(gps.LATITUD, gps.LONGITUD, 15);
192
+ }
193
+ } finally {
194
+ collGps.endBrowse();
195
+ }
196
+ }
197
+
198
+ function guardarPosicion(lat, lng, precision) {
199
+ let coll = appData.getCollection("Posiciones");
200
+ coll.unlock();
201
+ try {
202
+ let obj = new Posiciones({
203
+ MAP_LATITUD: lat,
204
+ MAP_LONGITUD: lng,
205
+ MAP_PRECISION: precision,
206
+ MAP_TIMESTAMP: new Date()
207
+ });
208
+ coll.addItem(obj);
209
+ obj.save();
210
+ } finally {
211
+ coll.lock();
212
+ }
213
+ }
214
+ ```
215
+
216
+ ### 12.5 Tomar y Mostrar Fotos
217
+
218
+ ```javascript
219
+ // Foto con prop tipo PH (se activa automaticamente al pulsar)
220
+ // En <onchange> de MAP_FOTO:
221
+ function onFotoCapturada() {
222
+ let foto = self.MAP_FOTO;
223
+ if (isEmpty(foto)) {
224
+ ui.showToast("Captura cancelada");
225
+ return;
226
+ }
227
+ self.MAP_FECHA_FOTO = new Date();
228
+ self.save();
229
+ ui.showToast("Foto capturada");
230
+ ui.refresh("MAP_FOTO");
231
+ }
232
+
233
+ // Foto con camara tipo VD (control manual)
234
+ function tomarFotoConCamara() {
235
+ let control = getControl("MAP_CAMERA");
236
+ if (!control) return;
237
+
238
+ control.takePicture({
239
+ filename : "foto_" + Date.now() + ".jpg",
240
+ saveToGallery: false,
241
+ width : 640,
242
+ height : 480,
243
+ onFinished : function(sFileName) {
244
+ if (sFileName) {
245
+ self.MAP_FOTO = sFileName;
246
+ self.save();
247
+ ui.refresh("MAP_FOTO");
248
+ ui.showToast("Foto guardada");
249
+ }
250
+ }
251
+ });
252
+ }
253
+ ```
254
+
255
+ ### 12.6 Sistema de Chat
256
+
257
+ ```javascript
258
+ // Inicializar chat
259
+ function inicializarChat() {
260
+ self.MAP_GRUPOSEL = 1;
261
+ self.MAP_VERFLOTANTE = 0;
262
+ self.MAP_RECORDON = 0;
263
+ self.MAP_USERLOGIN = appData.getGlobalMacro("##USERNAME##");
264
+ self.getContents("Chat").setMacro("##MACRO##", self.MAP_USERLOGIN);
265
+ }
266
+
267
+ // Crear un chat entre dos usuarios
268
+ function createChat(userFrom, userTo) {
269
+ let coll = self.getContents("Chat");
270
+ coll.unlock();
271
+ let obj = coll.findObject(
272
+ "(USUARIO='" + userFrom + "' AND USUARIO2='" + userTo + "') OR " +
273
+ "(USUARIO='" + userTo + "' AND USUARIO2='" + userFrom + "')"
274
+ );
275
+ if (obj == null) {
276
+ obj = coll.createObject();
277
+ obj.USUARIO = userFrom;
278
+ obj.USUARIO2 = userTo;
279
+ obj.FECHA = new Date();
280
+ obj.save();
281
+ }
282
+ let index = obj.getObjectIndex();
283
+ coll.lock();
284
+ return index;
285
+ }
286
+
287
+ // Enviar mensaje
288
+ function sendMessage(colMensajes, obj, titleField, isFromUser) {
289
+ if (obj[titleField].length == 0) return;
290
+
291
+ let msg = new MensajesReader();
292
+
293
+ if (isFromUser) {
294
+ msg.USUARIOTO = self.MAP_CCUSUARIO;
295
+ msg.USUARIOFROM = appData.getGlobalMacro("##USERNAME##");
296
+ } else {
297
+ msg.USUARIOTO = appData.getGlobalMacro("##USERNAME##");
298
+ msg.USUARIOFROM = self.MAP_CCUSUARIO;
299
+ }
300
+
301
+ msg.FECHA = new Date();
302
+ msg.MENSAJE = self[titleField];
303
+ msg.TIPO = self.MAP_TIPO;
304
+ msg.IDCHAT = self.MAP_IDCHATSEL;
305
+ msg.save();
306
+
307
+ self[titleField] = "";
308
+ ui.refresh("MensajesUsuarios," + titleField);
309
+ }
310
+ ```
311
+
312
+ ### 12.7 Escaneo QR/Barcode
313
+
314
+ ```javascript
315
+ function iniciarEscaneoQR() {
316
+ let control = getControl("MAP_CAMERA");
317
+ if (!control) {
318
+ ui.showToast("Control de camara no encontrado");
319
+ return;
320
+ }
321
+
322
+ control.setOnCodeScanned(function(evento) {
323
+ self.MAP_CODIGO_ESCANEADO = evento.data;
324
+ self.MAP_TIPO_CODIGO = evento.type;
325
+ ui.refresh("MAP_CODIGO_ESCANEADO,MAP_TIPO_CODIGO");
326
+
327
+ let nResult = ui.msgBox(
328
+ "Código: " + evento.data + "\nTipo: " + evento.type,
329
+ "Lectura correcta?", 4
330
+ );
331
+ return (nResult == 6);
332
+ });
333
+ }
334
+ ```
335
+
336
+ ### 12.8 Descarga de Archivos del Servidor
337
+
338
+ ```javascript
339
+ function descargarDocumento(url, nombre) {
340
+ ui.showWaitDialog("Descargando " + nombre + "...");
341
+
342
+ let miObjeto = self;
343
+ let request = {
344
+ headers: { "Authorization": "Bearer " + obtenerToken() },
345
+ parameters: { connectTimeout: 30000, readTimeout: 60000 }
346
+ };
347
+
348
+ $http.download(url, request,
349
+ function(sPath, headers, nStatus) {
350
+ ui.hideWaitDialog();
351
+ miObjeto.MAP_ARCHIVO = sPath;
352
+ miObjeto.save();
353
+ ui.refresh("MAP_ARCHIVO");
354
+
355
+ let nResult = ui.msgBox("Abrir archivo?", "Descarga completa", 4);
356
+ if (nResult == 6) {
357
+ ui.openFile(sPath);
358
+ }
359
+ },
360
+ function(nError, sMessage) {
361
+ ui.hideWaitDialog();
362
+ ui.showToast("Error: " + sMessage);
363
+ }
364
+ );
365
+ }
366
+ ```
367
+
368
+ ### 12.9 Sincronización con Servidor
369
+
370
+ ```javascript
371
+ function sincronizarPendientes(nombreColl, endpoint) {
372
+ let coll = appData.getCollection(nombreColl);
373
+ coll.setFilter("MAP_SINCRONIZADO = 0");
374
+ coll.clear();
375
+ coll.loadAll();
376
+
377
+ let count = coll.getCount();
378
+ if (count == 0) {
379
+ ui.showToast("No hay registros pendientes");
380
+ return;
381
+ }
382
+
383
+ ui.showWaitDialog("Sincronizando " + count + " registros...");
384
+
385
+ let pendientes = [];
386
+ for (let i = 0; i < count; i++) {
387
+ pendientes.push(coll.get(i).toJson());
388
+ }
389
+
390
+ let miColl = coll;
391
+ let request = {
392
+ headers: {
393
+ "Content-Type": "application/json",
394
+ "Authorization": "Bearer " + obtenerToken()
395
+ },
396
+ parameters: { connectTimeout: 30000, readTimeout: 120000 },
397
+ data: { registros: pendientes }
398
+ };
399
+
400
+ $http.post(endpoint, request,
401
+ function(sData) {
402
+ for (let i = 0; i < miColl.getCount(); i++) {
403
+ let obj = miColl.get(i);
404
+ obj.MAP_SINCRONIZADO = 1;
405
+ obj.MAP_FECHA_SYNC = new Date();
406
+ obj.save();
407
+ }
408
+ ui.hideWaitDialog();
409
+ ui.showToast(count + " registros sincronizados");
410
+ },
411
+ function(nError, sDesc) {
412
+ ui.hideWaitDialog();
413
+ ui.showToast("Error sync: " + sDesc);
414
+ }
415
+ );
416
+ }
417
+ ```
418
+
419
+ ### 12.10 Login Personalizado
420
+
421
+ ```javascript
422
+ function doLogin() {
423
+ let usuario = cstr(self.MAP_LOGIN).trim();
424
+ let password = cstr(self.MAP_PASSWORD).trim();
425
+
426
+ // Solo validamos el usuario: en XOne puede haber cuentas sin contraseña
427
+ // (invitado, kiosco) y, si la contraseña es incorrecta o falta cuando hace
428
+ // falta, el backend la rechaza vía onLoginFailed (no la validamos en cliente).
429
+ if (isEmpty(usuario)) { ui.showToast("Introduzca el usuario"); return; }
430
+
431
+ appData.login({
432
+ userName : usuario,
433
+ password : password,
434
+ entryPoint : "MenuPrincipal",
435
+ onLoginSuccessful : function() {
436
+ appData.setGlobalMacro("##USERNAME##", usuario);
437
+ ui.showToast("Bienvenido, " + usuario);
438
+ },
439
+ onLoginFailed : function() {
440
+ ui.showToast("Usuario o contraseña incorrectos");
441
+ self.MAP_PASSWORD = "";
442
+ ui.refresh("MAP_PASSWORD");
443
+ }
444
+ });
445
+ }
446
+
447
+ function doLogout() {
448
+ if (confirmar("Cerrar sesion?", "Confirmar")) {
449
+ appData.setGlobalMacro("##USERNAME##", "");
450
+ appData.setGlobalMacro("##SESSION_TOKEN##", "");
451
+ appData.logout();
452
+ }
453
+ }
454
+ ```
455
+
456
+ ---
457
+
458
+ ## 13. Funciones Utilitarias Recomendadas
459
+
460
+ Coleccion de funciones helper que todo proyecto XOne deberia incluir en `functions.js`.
461
+
462
+ ```javascript
463
+ /**
464
+ * Funciones Utilitarias para XOne
465
+ * Incluir en functions.js de todo proyecto
466
+ */
467
+
468
+ // ============================================
469
+ // CONVERSIONES Y VERIFICACIONES
470
+ // ============================================
471
+
472
+ function isEmpty(val) {
473
+ return val === undefined || val === null || val === "";
474
+ }
475
+
476
+ function cstr(val) {
477
+ if (val === undefined || val === null) return "";
478
+ return val.toString();
479
+ }
480
+
481
+ function cnum(val) {
482
+ if (val === undefined || val === null || val === "") return 0;
483
+ let num = parseFloat(val);
484
+ return isNaN(num) ? 0 : num;
485
+ }
486
+
487
+ function isNothing(obj) {
488
+ return obj === null || obj === undefined || obj == "undefined";
489
+ }
490
+
491
+ // ============================================
492
+ // ACCESO A CONTROLES
493
+ // ============================================
494
+
495
+ // getControl(name, [dataObject]) es NATIVA del motor — NO declararla aquí.
496
+ // Si un proyecto ya tiene su propio "function getControl(...)" como helper
497
+ // legacy, lo respetamos: la declaración del script sombrea a la nativa en
498
+ // su scope local sin tocar la del global.
499
+
500
+ // ============================================
501
+ // NAVEGACION
502
+ // ============================================
503
+
504
+ function mostrarGrupo(nGroup, sAnimIn, sAnimOut) {
505
+ sAnimIn = sAnimIn || "##ALPHA_IN##";
506
+ sAnimOut = sAnimOut || "##ALPHA_OUT##";
507
+ ui.showGroup(nGroup, sAnimIn, 200, sAnimOut, 200);
508
+ }
509
+
510
+ function cerrarPantalla() {
511
+ let window = ui.getView(self);
512
+ if (window) window.exit();
513
+ }
514
+
515
+ // ============================================
516
+ // MENSAJES Y DIALOGOS
517
+ // ============================================
518
+
519
+ function confirmar(mensaje, titulo) {
520
+ titulo = titulo || "Confirmar";
521
+ let nResult = ui.msgBox(mensaje, titulo, 4);
522
+ return nResult == 6;
523
+ }
524
+
525
+ function mostrarToast(mensaje) {
526
+ ui.showToast(mensaje);
527
+ }
528
+
529
+ function mostrarToastExito(mensaje) {
530
+ ui.showToast({
531
+ text: mensaje, color: "#4CAF50",
532
+ textColor: "#FFFFFF", duration: "short"
533
+ });
534
+ }
535
+
536
+ function mostrarToastError(mensaje) {
537
+ ui.showToast({
538
+ text: mensaje, color: "#F44336",
539
+ textColor: "#FFFFFF", duration: "long"
540
+ });
541
+ }
542
+
543
+ function mostrarCargando(mensaje) {
544
+ ui.showWaitDialog(mensaje || "Cargando...");
545
+ }
546
+
547
+ function ocultarCargando() {
548
+ ui.hideWaitDialog();
549
+ }
550
+
551
+ // ============================================
552
+ // COLECCIONES Y DATOS
553
+ // ============================================
554
+
555
+ function obtenerColeccion(nombreColl) {
556
+ return appData.getCollection(nombreColl);
557
+ }
558
+
559
+ function crearObjeto(nombreColl) {
560
+ let coll = appData.getCollection(nombreColl);
561
+ let obj = coll.createObject();
562
+ coll.addItem(obj);
563
+ return obj;
564
+ }
565
+
566
+ function buscarObjeto(nombreColl, campo, valor) {
567
+ let escapado = cstr(valor).replace(/'/g, "''");
568
+ let coll = appData.getCollection(nombreColl);
569
+ return coll.findObject(campo + "='" + escapado + "'");
570
+ }
571
+
572
+ // ============================================
573
+ // VALIDACIONES
574
+ // ============================================
575
+
576
+ function validarRequerido(valor, nombreCampo) {
577
+ if (isEmpty(valor)) {
578
+ mostrarToastError("El campo " + nombreCampo + " es obligatorio");
579
+ return false;
580
+ }
581
+ return true;
582
+ }
583
+
584
+ function validarEmail(email) {
585
+ if (isEmpty(email)) return false;
586
+ return email.indexOf("@") > 0 && email.indexOf(".") > 0;
587
+ }
588
+
589
+ function sanearEntrada(valor, maxLength) {
590
+ if (isEmpty(valor)) return "";
591
+ let sValor = cstr(valor).trim();
592
+ if (maxLength && sValor.length > maxLength) {
593
+ sValor = sValor.substring(0, maxLength);
594
+ }
595
+ return sValor;
596
+ }
597
+
598
+ // ============================================
599
+ // FECHAS Y TIEMPO
600
+ // ============================================
601
+
602
+ function obtenerFechaActual() {
603
+ let f = new Date();
604
+ return ("0" + f.getDate()).slice(-2) + "/" +
605
+ ("0" + (f.getMonth() + 1)).slice(-2) + "/" +
606
+ f.getFullYear();
607
+ }
608
+
609
+ function obtenerHoraActual() {
610
+ let f = new Date();
611
+ return ("0" + f.getHours()).slice(-2) + ":" +
612
+ ("0" + f.getMinutes()).slice(-2);
613
+ }
614
+
615
+ function obtenerAhora() {
616
+ return new Date();
617
+ }
618
+
619
+ // ============================================
620
+ // CONVERSION BINARIA (NFC, Bluetooth, etc.)
621
+ // ============================================
622
+
623
+ function toUint8Array(str) {
624
+ let arr = new Uint8Array(str.length);
625
+ for (let i = 0; i < str.length; i++) arr[i] = str.charCodeAt(i);
626
+ return arr;
627
+ }
628
+
629
+ function toStringFromUint8Array(arr) {
630
+ let str = "";
631
+ for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);
632
+ return str;
633
+ }
634
+
635
+ // ============================================
636
+ // GUID
637
+ // ============================================
638
+
639
+ function generarGUID() {
640
+ return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
641
+ var r = Math.random() * 16 | 0;
642
+ var v = c == 'x' ? r : (r & 0x3 | 0x8);
643
+ return v.toString(16);
644
+ });
645
+ }
646
+ ```
647
+