====== Layout de los formularios (PWA_FORMS) ====== //Cómo se declara, en un texto de configuración, lo que enseña cada ficha de edición de SmartPWA: sus pestañas y campos, las rejillas del panel inferior y las opciones de su menú desplegable.// Nada de esto está en el programa: vive en la base de datos, en el campo ''DESCRIPCION'' de la tabla ''PWA_FORMS'', y se lee cada vez que se abre la ficha. Cambiar dónde va un campo, añadir una pestaña de una tabla hija o quitarle a una instalación una opción del menú es cambiar ese texto, sin tocar la aplicación ni publicar una versión nueva. ===== Dónde vive y a quién se le aplica ===== Una fila de ''PWA_FORMS'' es la definición de una pantalla para un ámbito. La clave primaria es ''COD_FORM + TIPO_AMBITO + COD_AMBITO + CLAVE_INSTALACION'', donde ''COD_FORM'' es el código de la pantalla tal y como lo nombra la aplicación: ''albaran-venta-edit'', ''parte-averia-edit'', ''producto-edit''… (distingue mayúsculas de minúsculas). Cuando la ficha se abre, ''PWA_GET_FORM(COD_FORM, COD_USUARIO)'' elige **una sola** de esas filas —usuario, usuario del que hereda, clave de instalación, clave de grupo y, por último, la general—, así que una instalación que no alquile no tiene por qué ver la pestaña «Precios alquiler», y a un usuario se le puede dejar la ficha con los cuatro campos que usa. Cómo se gestiona todo eso está en [[pwa:doc:Documentación de ámbitos en SmartPWA|Ámbitos: quién ve qué]]. Cada definición describe la ficha **entera**, no un parche sobre la general: lo que se añada a la general no llega a quien tenga la suya. El texto se lee en tres trozos de 8000 caracteres, así que el límite práctico de un layout son **24 000 caracteres**. Una ficha con cuarenta pestañas anda por los ocho mil. La aplicación cachea la definición por usuario y formulario. Después de cambiar el texto en la pantalla de **Formularios** hay que volver a entrar en la aplicación para verlo; si se diseña desde la propia ficha, con //Diseñar el formulario//, el refresco lo hace ella sola al cerrar la ventana del diseño. ===== Cómo se edita ===== * **Desde la propia ficha**, con la opción //Diseñar el formulario// de su menú de acciones. Se abre el diseño **del ámbito con el que se está viendo la ficha**, en una ventana aparte; al cerrarla, la ficha relee el layout y se vuelve a montar con lo recién diseñado, sin salir de la aplicación ni perder el registro que se tenía delante. * Desde la propia aplicación, en **Formularios** (''/formularios''), que además ofrece «Copiar script de actualización» y «Descargar script (.sql)» para llevar el cambio a otras bases de datos. * En el repositorio, los layouts generales están versionados en ''db/pwa_forms_layouts.sql'' (''UPDATE OR INSERT'', idempotente), y de ahí salen los scripts ''JMnnnn'' que se incorporan a las bases de los clientes. ==== Quién puede diseñar desde la ficha ==== La opción //Diseñar el formulario// solo sale a quien puede cambiar esa definición sin cambiársela a nadie más: ^ Quién ^ Qué ámbitos puede diseñar desde la ficha ^ | ''SYSDBA'' | cualquiera: el suyo, el de la instalación, el del grupo y el general | | Cualquier otro usuario | solo el suyo propio: la fila ''US'' con su código de usuario (precedencia 1) | Un layout heredado de otro usuario, el de la instalación, el del grupo o el general se ven igual de bien, pero editarlos se lo cambiaría también a los demás: para eso está la pantalla de **Formularios**, con su ámbito elegido a conciencia. ===== El texto, línea a línea ===== Una directiva por línea. Las líneas en blanco se ignoran y los nombres se recortan. Lo que no encaje con ninguna directiva se toma como una fila de campos. ^ Directiva ^ Qué hace ^ | ''P:título'' | abre una pestaña de la cabecera | | ''L:título'' | corta el formulario en horizontal: cierra las columnas y abre un bloque nuevo debajo, con sus propias columnas | | ''C1:40%'' | abre una columna del ancho indicado (el número que sigue a la C es libre) | | ''CAMPO'' | una fila de la columna actual; varios separados por coma van en la misma fila | | ''D:alias = origen'' | declara un origen de datos auxiliar | | ''R:título = origen'' | añade una pestaña al panel inferior | | ''G:título'' | abre un grupo de pestañas del panel inferior | | ''A:título = destino'' | añade una opción al menú desplegable de la ficha | | ''Q:modificadores'' | cómo se comporta una pantalla de consulta (solo lo miran las consultas) | ==== Campos ==== P:General C1:40% NRO_ALBARAN TIPO_TERCERO, COD_TERCERO -USUARIO_CREA +COMENTARIOS(300%, 100%) QRES.S_CUOTA_TOTAL * **Coma**: los campos de una misma línea se pintan uno al lado del otro. * **Guión delante** (''-USUARIO_CREA''): el campo se ve pero no se edita. * **Más delante** (''+COMENTARIOS''): la etiqueta va encima del cuadro en vez de a su izquierda. Es lo que quieren los campos anchos. * **Paréntesis con dos porcentajes** (''COMENTARIOS(300%, 100%)''): cuadro de texto multilínea. El primero es cuánto crece de alto respecto a un campo normal (100 % = el doble de alto) y el segundo, el ancho como porcentaje de la columna. * Las marcas se combinan y en cualquier orden: ''-+USUARIO_CREA''. * **ALIAS.CAMPO**: el campo sale de un origen auxiliar ''D:'' y no de la tabla de la ficha; siempre de solo lectura. === Ejemplo: la pestaña General del albarán de venta === P:General C1:40% NRO_ALBARAN TIPO_TERCERO, COD_TERCERO NOMBRE RAZON_SOCIAL COD_OBRA DESC_OBRA C2:30% NRO_PEDIDO REFERENCIA FECHA_SERVICIO FECHA_DEVENGO COD_DELEGACION C3:30% COD_FORMA_ENTREGA SOLICITANTE ENTREGADO_A PRIORIDAD Tres columnas que suman el 100 %, y el tercero en su fila junto a su tipo. Los anchos son orientativos: si no suman cien, se reparte lo que haya. === Ejemplo: una pestaña de un solo cuadro de texto === P:Comentarios C1:100% +COMENTARIOS(300%, 100%) Etiqueta encima, cuatro veces el alto de un campo normal y todo el ancho de la columna. Es como están los comentarios del cliente, del producto y del parte de avería. === Ejemplo: dos bloques, uno encima de otro === P:Datos C1:50% CODIGO DESCRIPCION C2:50% FECHA_ALTA COD_DELEGACION L:Importes C1:33% BASE_IMPONIBLE C2:33% CUOTA_IVA C3:34% TOTAL Sin el ''L:'', las columnas nuevas se seguirían añadiendo a la derecha de las anteriores. Con él, //Importes// empieza una franja debajo, con sus propios anchos y su título. ==== Valores: lo que se puede escribir entre paréntesis o corchetes ==== Es el mismo juego en los datasets ''D:'', en las rejillas ''R:'' y en las acciones ''A:'': ^ Se escribe ^ Vale ^ | '':CAMPO'' | el valor que tiene ese campo en la cabecera de la ficha | | ''123'' | el número | | ''%%'007'%%'' | el texto entre comillas simples, tal cual (los códigos con ceros delante) | | ''NULL'' | un nulo de verdad, que no es lo mismo que no pasar nada | | ''si(valor, siLo, siNo)'' | el primero si ese valor tiene contenido y el segundo si no; sirve para depender de lo que devolvió un paso anterior (''si(:SALIDA, :SALIDA, :NRO_ALBARAN)'') | | ''2000-01-01'' | esa fecha, en el formato del servidor | | ''SIMULACION'' | la simulación de la sesión | | ''USUARIO'' | el usuario conectado, para los procedimientos que dejan rastro de quién hizo qué | | ''HOY'', ''INI_ANO'', ''FIN_ANO'' | fechas, con desplazamiento opcional: ''HOY-10D'', ''HOY+6M'', ''FIN_ANO-1A'' | Un parámetro con **''?''** delante no se fija: se propone. En las rejillas de procedimiento el usuario lo ve relleno en la barra de filtros, y en las acciones con ''modo=proceso'', en la pantalla de procesos; en los dos casos puede cambiarlo antes de lanzar la consulta. ===== D: orígenes auxiliares ===== No todos los campos de una cabecera salen de su tabla: algunos los calcula un procedimiento (los totales) y otros viven en una tabla aparte. D:QRES = RESUMEN_ALB_VENTA_TOTAL3(:NRO_ALBARAN) D:QUSU = USUARIOS_EN_ALBARANES[NRO_ALBARAN = :NRO_ALBARAN] ... QRES.S_CUOTA_TOTAL Con **paréntesis** el origen es un procedimiento seleccionable y lo de dentro son sus parámetros, **por posición**. Con **corchetes** es una tabla y lo de dentro son las condiciones del WHERE. Se lee la primera fila, y sus campos son siempre de solo lectura. === Ejemplo: los totales del albarán, que no están en su cabecera === D:QRES = RESUMEN_ALB_VENTA_TOTAL3(:NRO_ALBARAN) P:Totales C1:50% QRES.S_BASE_TOTAL QRES.S_CUOTA_TOTAL QRES.S_TOTAL === Ejemplo: el último tenedor de la máquina, en el parte de avería === D:SIT = SITUACION_ACTIVO_A_FECHA(:COD_PRODUCTO, :NRO_SERIE, :FECHA_PARTE) P:Último tenedor C1:50% SIT.S_TIPO_TERCERO SIT.S_COD_TERCERO SIT.S_COD_OBRA C2:50% SIT.S_FECHA_INI SIT.S_FECHA_FIN SIT.S_DIAS_NATURALES El procedimiento recibe tres campos de la propia cabecera, así que la pestaña se rellena sola en cuanto la ficha sabe de qué máquina habla. ===== R: las pestañas del panel inferior ===== Cada ''R:'' es una pestaña de la parte de abajo de la ficha: las tablas hijas y las consultas del documento. Se añaden a las que la pantalla traiga de código —las líneas de un albarán no son configurables: un albarán sin líneas no es nada— y salen en el orden en que se declaran. R:Unidades = UDS_EQUIV_PRODUCTO R:Ubicaciones = UBICACION_PRODUCTO [COD_ALMACEN = :COD_ALMACEN] orden=COD_ALMACEN R:Extracto = EXTRACTO_TERCERO(P_COD_TERC = :CODIGO, ?P_FEC_INI = HOY-3A) * **Corchetes**: filtran la rejilla. Sin ellos manda el filtro que pase la ficha (el número del albarán, el código del producto…), que es lo normal. * Un filtro se puede escribir con ''<>'' en vez de con ''='': la columna **no** vale eso. Las filas que la tengan a nulo entran igualmente, que es lo que hace falta para partir en dos pestañas una misma tabla de líneas. * **Paréntesis**: el origen es un procedimiento y la pestaña enseña su consulta. Dentro van los parámetros que se fijan, **por nombre**; los que no se nombren quedan a mano del usuario en la barra de filtros. ==== Pestañas que cuelgan de la línea elegida ==== Un filtro puede apuntar a la fila que esté elegida en **otra** rejilla en vez de a la cabecera: se escribe la tabla de esa rejilla delante del campo. R:Inspección = LIN_ALBARAN_VENTA_INSPEC [NRO_ALBARAN = :NRO_ALBARAN, NRO_LINEA_ALBARAN = :LIN_ALBARAN_VENTA.NRO_LINEA] R:Lista de verificación = LISTA_CHEQ_ALB_VENTA [NRO_ALBARAN = :NRO_ALBARAN, NRO_LINEA = :LIN_ALBARAN_VENTA.NRO_LINEA] Es el maestro-detalle de toda la vida: en el albarán, la inspección y la lista de verificación son de **una línea**, no del documento. Mientras no haya línea elegida la pestaña no lee nada —lo dice con un aviso—, y en cuanto se cambia de línea se relee sola. ==== Modificadores de las rejillas de tabla ==== ^ Modificador ^ Qué hace ^ | ''alta=modal'' | el alta abre el diálogo de registro nuevo (por defecto) | | ''alta=none'' | la pestaña no deja dar de alta | | ''alta=maximo(CAMPO, incremento)'' | numera sola la fila nueva: máximo + incremento (10000 si no se dice) | | ''alta=generador(PROC, P_UNO = valor…)'' | el número lo da un procedimiento | | ''alta=proceso(PROC, P_UNO = valor…)'' | el botón «Nueva» ejecuta el procedimiento y recarga; solo tiene sentido con ''modo=lista'' | | ''borrado=delete'' | el borrado borra la fila | | ''borrado=anular(CAMPO)'' | el borrado pone ese campo a nulo | | ''modo=edicion'' | se teclea sobre la propia rejilla (por defecto) | | ''modo=lista'' | no se edita en línea; el doble clic abre la ficha de la fila | | ''modo=memo'' | no hay rejilla: es el texto libre que la tabla guarda en una única fila por registro | | ''campo=CAMPO'' | el campo que edita ''modo=memo'' (por defecto, ''COMENTARIO'') | | ''orden=CAMPO'' | por qué campo se ordenan las filas | | ''totales=CAMPO1;CAMPO2'' | columnas que llevan fila de totales | | ''pk=CAMPO1;CAMPO2'' | cuál es la clave de la tabla, para las de líneas que no la declaran en el diccionario | | ''editapk=CAMPO'' | campo de esa clave que sí se teclea | | ''defecto=campo(CAMPO = valor)'' | lo que trae puesto la fila nueva, sin que la rejilla filtre por ello; se puede repetir | | ''n=si'' | la pestaña enseña en un chip cuántas filas tiene | ''n=si'' es un ''COUNT'' más al abrir el grupo de pestañas: se pone donde el número compense. Las tablas de líneas —las ''LIN_%%_%%'' y las ''PREC_%%_%%''— no declaran su clave en el diccionario, así que sin ''pk='' la pestaña no sabe qué fila está tocando y no deja editar ni borrar. Y si uno de esos campos de la clave es un dato que se teclea —la unidad de un precio—, se dice con ''editapk=''. R:Precios de alquiler = PREC_OFERTA_ALQ [COD_OFERTA = :COD_OFERTA, NRO_LINEA = :LIN_OFERTA_VENTA.NRO_LINEA] orden=COD_UDAD pk=COD_OFERTA;NRO_LINEA;COD_UDAD editapk=COD_UDAD === Ejemplo: la misma tabla de líneas en dos pestañas === El contrato de vehículos guarda en una sola tabla las líneas de entrega (''TIPO_LINEA = %%'E'%%''), las de recogida (''%%'R'%%'') y los complementos, que no son ni una cosa ni otra y tienen que verse en las dos: R:Líneas de entrega = VEHI_LIN_CONTRATO [COD_CTO = :CODIGO, TIPO_LINEA <> 'R'] orden=NRO_LINEA alta=maximo(NRO_LINEA) defecto=campo(TIPO_LINEA = 'E') pk=COD_CTO;NRO_LINEA;TIPO_LINEA editapk=TIPO_LINEA n=si R:Líneas de recogida = VEHI_LIN_CONTRATO [COD_CTO = :CODIGO, TIPO_LINEA <> 'E'] orden=NRO_LINEA alta=maximo(NRO_LINEA) defecto=campo(TIPO_LINEA = 'R') pk=COD_CTO;NRO_LINEA;TIPO_LINEA editapk=TIPO_LINEA n=si El ''<>'' es lo que reparte las filas y el ''defecto='' lo que hace que la línea nueva nazca en la pestaña en la que se está: una exclusión, a diferencia de un filtro de igualdad, no propone nada al dar de alta. ==== Modificadores de las rejillas de procedimiento ==== ^ Modificador ^ Qué hace ^ | ''visibles=P_UNO;P_DOS'' | los únicos parámetros que se enseñan; los demás van fijos | | ''ocultas=CAMPO1;CAMPO2'' | columnas del resultado que no se enseñan | | ''totales=si'' | fila de totales de la consulta, que calcula el servidor | | ''abre=TABLA(CAMPO_PK = COLUMNA)'' | qué ficha abre el doble clic en una fila, y con qué columnas del resultado se arma su clave | ==== Ejemplos de rejillas de tabla ==== Líneas que se numeran solas, de diez en diez mil o de una en una: R:Productos = PRODUCTO_TERCERO alta=maximo(NRO_LINEA, 1) R:Productos complementarios = PROD_COMPLEMEN_PROD alta=maximo(NRO_LINEA) Una tabla hija cuyo número lo da un generador de la base de datos: R:Obras = OBRA_TERCERO modo=lista alta=generador(GEN_NRO_OBRA) R:Documentación = DOC_PERFIL [COD_PRODUCTO = :CODIGO] modo=lista alta=generador(GEN_NRO_DOCUMENTO, P_FECHA = HOY, P_SIM = SIMULACION) Documentos de otra tabla que cuelgan de esta ficha, para mirar y no para teclear (''modo=lista'' abre la ficha con doble clic; ''alta=none'' quita el botón de nueva fila): R:Ofertas = CAB_OFERTA_VENTA [TIPO_TERCERO = :TIPO, COD_TERCERO = :CODIGO] modo=lista alta=none R:Partes de avería = CAB_PARTE_AVERIA [TIPO_TERC_ALB = :TIPO, COD_TERC_ALB = :CODIGO, CERRADO = 'N'] modo=lista alta=none Fíjese en el ''CERRADO = 'N''': un filtro no tiene por qué apuntar a un campo de la ficha; puede ser un literal, y así la pestaña enseña solo los partes abiertos. Una fila que crea el sistema, no el usuario: el botón «Nueva» ejecuta un procedimiento y recarga la pestaña. R:Ficha de crédito y caución = CLIENTE_CREDITO_CAUCION [CIF_NIF = :CIF_NIF] modo=lista alta=proceso(CREA_CYC_DESDE_CL, P_TIPO_TERCERO = :TIPO, P_COD_TERCERO = :CODIGO) El comentario libre del cliente, que vive en otra tabla con una única fila por tercero y se edita como un texto y no como una rejilla: R:Comentario libre = COMENTARIO_TERCERO modo=memo campo=COMENTARIO Y una pestaña que además dice cuántas filas tiene, ordenada por almacén: R:Ubicaciones = UBICACION_PRODUCTO [COD_ALMACEN = :COD_ALMACEN] orden=COD_ALMACEN n=si ==== Ejemplos de rejillas de consulta ==== El extracto del cliente, con dos fechas que el usuario puede cambiar y las columnas técnicas escondidas: R:Extracto = EXTRACTO_TERCERO(P_TIPO_TERC = :TIPO, P_COD_TERC = :CODIGO, P_NRO_FACTURA = NULL, ?P_FEC_INI = HOY-3A, ?P_FEC_FIN = HOY) ocultas=TIPO_TERCERO;COD_TERCERO;S_TIPO_TERCERO;S_COD_TERCERO Las facturas del albarán, que además se abren con doble clic —una consulta no dice de qué tabla es cada fila, así que hay que decírselo con ''abre=''—: R:Facturas = FACTURAS_EN_ALBARAN_VTA(P_NRO_ALBARAN = :NRO_ALBARAN) abre=CAB_FACTURA_VENTA(NRO_FACTURA = S_NRO_FACTURA) Los cobros pendientes a una fecha, con fila de totales y con un único parámetro a la vista: R:Cobros pendientes = COBROS_PENDIENTES(P_TODO = 'S', P_TIPO_TERCERO = :TIPO, P_COD_TERCERO = :CODIGO, ?P_FECHA = FIN_ANO) visibles=P_FECHA totales=si Cada rejilla va en **una sola línea**; en la página se han partido para que quepan. ===== G: grupos de pestañas ===== Cuando las pestañas son muchas, ''G:'' las reparte: cada ''R:'' cae en el grupo que abrió el último ''G:'', y el panel enseña una fila con los grupos y, debajo, solo las pestañas del que esté elegido. G:Tablas R:Direcciones = DIRECCION_TERCERO G:Consultas R:Extracto = EXTRACTO_TERCERO(P_COD_TERC = :CODIGO) === Ejemplo: los grupos de la ficha de cliente === G:Consultas R:Extracto = EXTRACTO_TERCERO(P_TIPO_TERC = :TIPO, P_COD_TERC = :CODIGO) R:Cobros pendientes = COBROS_PENDIENTES(P_TODO = 'S', ?P_FECHA = FIN_ANO) G:Tablas R:Bancos = BANCO_TERCERO R:Obras = OBRA_TERCERO modo=lista alta=generador(GEN_NRO_OBRA) R:Comentario libre = COMENTARIO_TERCERO modo=memo campo=COMENTARIO ==== Qué va en cada grupo ==== Los nombres de los grupos son libres, pero conviene que signifiquen lo mismo en todas las fichas. La regla de la casa son estos tres: ^ Grupo ^ Qué lleva ^ | **Detalle** | la información fundamental del documento: sus líneas, la lista de verificación, la inspección, el comentario | | **Tablas** | lo que sirve para **añadir** información al documento o a lo que cuelga de él: canjes, anticipos, direcciones… | | **Consultas** | todo lo que es **mirar**: extractos, informes, imágenes, documentación, facturas relacionadas | Puesto de otro modo: en //Detalle// está el documento, en //Tablas// se escribe y en //Consultas// se lee. Las ''R:'' anteriores al primer ''G:'' se quedan sin grupo y caen en //Detalles//, que es donde van también las pestañas que la ficha trae de código. Un layout que no declare ningún grupo sigue enseñando la barra de pestañas de siempre. ===== A: las opciones del menú ===== Cada ''A:'' es una opción del menú desplegable de la ficha (el botón de la flecha, arriba a la derecha). Salen en el orden en que se declaran y **delante** de las que ofrecen todas las fichas —Eliminar, Copiar url, Color de fondo, Diseñar el formulario y Volver—, de las que las separa una línea horizontal. A:título = destino modificadores ==== El destino ==== ^ Se escribe ^ Qué hace ^ | ''PROC(args)'' | ejecuta el procedimiento (''EXECUTE PROCEDURE''); los argumentos van **por posición** | | ''TABLA[filtros]'' | lleva a la pestaña del panel inferior que enseña esa tabla; si el layout no la trae, abre su listado con los filtros puestos | | '':VALOR(args)'' | el procedimiento no se sabe al escribir el layout: lo dice ese valor, normalmente la salida de un ''proc='' anterior (los asistentes) | | ''@metodo'' | llama a un método de la propia pantalla, para lo que no se puede declarar | | ''campo(NOMBRE = valor)'' | no ejecuta nada: escribe ese valor en el campo de la ficha y lo guarda | | ''imprimir(TIPO, PARAM = valor…)'' | saca el documento por el dispositivo que el usuario tenga para ese tipo (''ET'' etiquetas, ''TK'' tickets…) | El ''@metodo'' es la válvula de escape: hay opciones cuya lógica no cabe en una línea de texto —la que encadena tres procedimientos mirando lo que devuelve cada uno, la que abre un diálogo propio para elegir una máquina—. Esas siguen en el programa, pero **el layout manda igualmente** en su texto, su icono, su sitio en el menú y su tecla. Los nombres de método que admite cada pantalla los publica esa pantalla. === Cuando el procedimiento lo elige la base de datos === Los asistentes no son el mismo procedimiento en todas las instalaciones: hay uno que dice cuál toca. El layout lo declara escribiendo el destino como una referencia al valor que lo trae: A:Contratar = :SALIDA(?P_NRO_SERIE = :S_NRO_SERIE, ?P_COD_ALMACEN = :S_COD_ALMACEN) modo=proceso proc=GEN_ASISTENTE_CTO_ALQ() necesita=fila Primero corre el ''proc='', que devuelve el nombre del asistente en '':SALIDA'', y con él se abre la pantalla de procesos con los dos parámetros propuestos desde la fila. Si no hay asistente configurado, la opción lo dice y no va a ninguna parte. ==== Los modificadores ==== ^ Modificador ^ Qué hace ^ | ''icono=cash-outline'' | icono de la opción, de los que la aplicación sabe pintar; el que no esté sale sin icono | | ''tecla=F11'' | atajo de teclado de la ficha | | ''necesita=fila'' | la opción trabaja sobre la fila elegida; mientras no haya ninguna se enseña apagada (solo tiene sentido en las consultas) | | ''pregunta="¿Facturar el parte :NRO?"'' | pide confirmación antes de hacer nada; sin él, no pregunta | | ''boton="Facturar"'' | texto del botón que confirma (por defecto, «Aceptar») | | ''si=CONFIRMADO<>S'' | la opción solo sale si el campo de la cabecera cumple; admite ''='' y ''<>'', y varias condiciones se cumplen todas | | ''modo=consulta'' | no ejecuta el procedimiento: abre su consulta, que es como se piden los informes | | ''modo=proceso'' | abre la pantalla de ejecutar procesos: lo declarado va fijo, salvo lo que lleve ''?'' delante, que se propone y el usuario puede cambiar | | ''modo=seleccion'' | el protocolo de añadir y quitar: el destino lista lo enlazable y ''anade='' enlaza y desenlaza cada fila | | ''anade=PROC(args)'' | procedimiento que enlaza y desenlaza; lo que salga de la fila elegida se escribe '':FILA.CAMPO'' | | ''contenido=PROC(args)'' | en una impresión, lo que se manda al dispositivo lo compone ese procedimiento —una fila por línea— en vez de la plantilla | | ''enlace=CAMPO = valor'' | columna del listado que dice si la fila está enlazada, y con qué valor | | ''proc=PROC(args)'' | procedimiento que se ejecuta **antes** del destino; se puede repetir, y corren en el orden en que se declaran. Cada uno deja lo que devuelve en '':SALIDA'' | | ''pide=CAMPO:tipo("Etiqueta")'' | pregunta un dato antes de hacer nada; se usa como '':PIDE.CAMPO''. Los tipos son ''texto'' (por defecto), ''numero'' y ''fecha'', y se puede repetir | | ''pide=CAMPO:tabla(TABLA)'' | igual, pero el dato se elige de esa tabla con el selector del F6 | | ''tras=…'' | qué hacer al terminar (ver debajo) | En los textos de ''pregunta='' y ''aviso('')'' se escribe '':CAMPO'' para meter el valor del campo de la cabecera. Un procedimiento lanzado desde el menú se ejecuta con la espera larga de los procesos —los hay que tardan minutos— y mientras corre se ve en el indicador de procesos en marcha de la aplicación. ==== tras=: qué pasa al terminar ==== Varias cosas separadas por punto y coma, y en ese orden. Cada una puede llevar delante un guardia ''si(...)'', que es lo que permite hacer una cosa u otra según lo que haya devuelto el procedimiento: ^ Se escribe ^ Qué hace ^ | ''refrescar'' | relee la cabecera de la ficha | | ''pestana(TABLA)'' | lleva a la pestaña del panel que enseña esa tabla (y abre el panel si estaba recogido) | | ''abrir(TABLA, CAMPO_PK)'' | abre la ficha del documento que acaba de salir | | ''abrir(TABLA, CAMPO_PK = :SALIDA.COLUMNA)'' | igual, pero con la columna que se diga | | ''listado(TABLA, CAMPO = :SALIDA)'' | abre el listado de esa tabla acotado a lo que ha salido; si vienen varios códigos separados por ''%%|%%'', los enseña todos | | ''aviso("Se ha creado :SALIDA")'' | lo cuenta en un diálogo | | ''volver'' | cierra la ficha | ''abrir'' mira la **primera salida** del procedimiento —los de este sistema devuelven el número del documento que crean, o nada—, que en los textos se escribe '':SALIDA''. Sin salida no abre nada: el procedimiento no llegó a crear el documento. === Todo lo que devuelve un procedimiento === Un procedimiento puede devolver más de una columna, y todas están a mano: ^ Se escribe ^ Qué es ^ | '':SALIDA'' | la primera columna, que es el caso normal | | '':SALIDA.COLUMNA'' | esa columna, por su nombre | | '':SALIDA1'', '':SALIDA2''… | esa columna, por su posición | Lo posicional está por los procedimientos que han cambiado el nombre de sus salidas entre versiones: el layout tiene que valer para todas. === si(...): hacer una cosa u otra === Un guardia delante de cualquiera de las cosas de ''tras='' la deja en suspenso hasta que se cumpla. Se escribe con paréntesis —y no como el modificador ''si='' de la opción entera— para que no se confunda con él. ''CONFIRM_PARTE_AVERIA'' devuelve el documento que ha generado y de qué tipo es; si es un albarán se abre su ficha, y si no, se cuenta: A:Confirmar F11 = CONFIRM_PARTE_AVERIA(:NRO, 'N', '', HOY, 'N') si=CONFIRMADO<>S tecla=F11 pregunta="¿Confirmar el parte :NRO?" boton="Confirmar" tras=refrescar;si(:SALIDA2=AV) abrir(CAB_ALBARAN_VENTA, NRO_ALBARAN);si(:SALIDA2<>AV) aviso("Se ha generado el documento :SALIDA1.") La condición se escribe igual que la de ''si='': ''CAMPO=VALOR'' o ''CAMPO<>VALOR'', sin distinguir mayúsculas. Una columna que no venga cuenta como vacía, así que ''<>'' se cumple y ''='' no. Con ''aviso'' pasa lo mismo **solo si su texto nombra '':SALIDA''**. El aviso que se limita a decir que ha terminado se da siempre, que es lo que se espera después de lanzar un proceso largo: A:Quitar marcas = ELIMINAR_MARCA_TIPO_TERCERO(:TIPO) icono=remove-circle-outline pregunta="Este proceso puede tardar bastante. ¿Quitar las marcas de todos los clientes?" boton="Quitar" tras=refrescar;aviso("Quitar marcas: completado.") ==== Ejemplo: el menú del parte de avería ==== A:Confirmar F11 = @confirmar icono=checkmark-circle-outline tecla=F11 A:Facturar avería = FACTURAR_AVERIA(:NRO) icono=cash-outline pregunta="¿Facturar el parte :NRO?" boton="Facturar" tras=refrescar;abrir(CAB_FACTURA_VENTA, NRO_FACTURA) A:Imprimir F8 = IMPRES_PARTE_AVERIA(P_NRO_PARTE = :NRO) modo=consulta icono=print-outline tecla=F8 A:Generar albaranes F10 = CAB_ALBARAN_VENTA[NRO_PARTE_AVERIA = :NRO] proc=GEN_ALBARANES_AVERIA(:NRO) proc=GEN_ALBARANES_AVERIA_COMPRA(:NRO) icono=receipt-outline tecla=F10 A:Asignar terminal = PCO_ADD_AVERIA_CARTERA(P_NRO_PARTE = :NRO) modo=proceso icono=phone-portrait-outline Cada opción va en **una sola línea**; aquí se han partido para que quepan en la página. Mientras un layout no declare ninguna ''A:'', la ficha enseña el menú que trae de código. Es lo que permite ir pasando las pantallas al layout de una en una, sin que ninguna se quede por el camino sin opciones. ==== Más ejemplos de opciones ==== **Ejecutar un procedimiento y contar lo que ha hecho.** Si no ha creado nada, no dice nada: A:Confirmar (albarán compra) = CONFIRM_PARTE_AVERIA_COMPRA(:NRO, HOY) icono=cart-outline pregunta="¿Confirmar el parte :NRO en albarán de compra?" boton="Confirmar" tras=refrescar;aviso("Se ha generado el albarán de compra :SALIDA.") **Cerrar algo y llevar a la pestaña donde se ve el resultado:** A:Cerrar presupuesto = CIERRA_AVERIA_PPTO(:NRO, NULL) icono=lock-closed-outline pregunta="¿Cerrar el presupuesto de la avería :NRO?" boton="Cerrar" tras=refrescar;pestana(CAB_OFERTA_VENTA) **Generar primero y enseñar después.** Los dos ''proc='' corren en orden y luego se abre la pestaña de los albaranes (o su listado, si esta instalación no la tiene en el panel): A:Generar albaranes F10 = CAB_ALBARAN_VENTA[NRO_PARTE_AVERIA = :NRO] proc=GEN_ALBARANES_AVERIA(:NRO) proc=GEN_ALBARANES_AVERIA_COMPRA(:NRO) icono=receipt-outline tecla=F10 **Una misma opción con dos caras**, según cómo esté el registro. Son dos líneas que nunca salen a la vez: A:Confirmar F11 = CONFIRM_ALB_VENTA(:NRO_ALBARAN) si=CONFIRMADO<>S tecla=F11 icono=checkmark-circle-outline pregunta="¿Confirmar el albarán :NRO_ALBARAN?" boton="Confirmar" tras=refrescar A:Desconfirmar F11 = CONFIRM_ALB_VENTA_DESHACER(:NRO_ALBARAN) si=CONFIRMADO=S tecla=F11 icono=close-circle-outline pregunta="¿Desconfirmar el albarán :NRO_ALBARAN?" boton="Desconfirmar" tras=refrescar **Un informe**, que no es más que una consulta con sus parámetros puestos: A:Imprimir F8 = IMPRES_PARTE_AVERIA(P_NRO_PARTE = :NRO) modo=consulta icono=print-outline tecla=F8 **Un proceso que el usuario termina de rellenar.** Lo declarado va fijo y lo demás lo pide la pantalla de procesos: A:Asignar terminal = PCO_ADD_AVERIA_CARTERA(P_NRO_PARTE = :NRO) modo=proceso icono=phone-portrait-outline **Y ese mismo caso con fechas propuestas**: el cliente va fijo, y las dos fechas salen rellenas para que el usuario las repase: A:Calcular seguimiento facturas = CAL_SEGUIM_FRA_CLI_FECHA( P_COD_TERCERO = :CODIGO, ?P_FECHA_DESDE = 2000-01-01, ?P_FECHA_HASTA = HOY) modo=proceso icono=calculator-outline **Una opción con dos textos según el estado**, cuando lo que hace no es un procedimiento sino tocar un campo de la propia ficha (la marca del cliente): A:Marcar = @marcarDesmarcar icono=bookmark-outline si=MARCADO<>S A:Desmarcar = @marcarDesmarcar icono=bookmark-outline si=MARCADO=S **Un listado de otra tabla acotado a esta ficha**, sin procedimiento de por medio: A:Ver sus facturas = CAB_FACTURA_VENTA[TIPO_TERCERO = :TIPO, COD_TERCERO = :CODIGO] icono=document-text-outline **Algo que hay que preguntar antes.** Lo tecleado se usa como '':PIDE.CAMPO'', y si el usuario cancela no se ejecuta nada: A:Añadir líneas de oferta = COPIA_LIN_OFERTA_FUSION_ALV( P_OFERTA_ORIGEN = :PIDE.OFERTA, P_NRO_ALBARAN = :NRO_ALBARAN) pide=OFERTA:texto("Nº de oferta") icono=add-circle-outline **Un procedimiento que trabaja con lo que devolvió el anterior.** Cada ''proc='' deja su resultado en '':SALIDA'', así que el destino puede usarlo: A:Orden de revisión = PARTE_REVISION(P_COD_ACTIVO = :SALIDA) modo=consulta proc=GET_ACTIVO_ALBARAN(:NRO_ALBARAN, :LIN_ALBARAN_VENTA.NRO_LINEA) **Marcar y desmarcar**, que no ejecuta nada: solo cambia un campo del registro que se está editando: A:Marcar = campo(MARCADO = 'S') si=MARCADO<>S icono=bookmark-outline A:Desmarcar = campo(MARCADO = 'N') si=MARCADO=S icono=bookmark-outline **Aceptar lo que propone un procedimiento**, que es lo mismo con el valor encadenado: A:Propone cód. EAN13 = campo(COD_EAN12 = :SALIDA) proc=PROPONE_COD_EAN12(P_COD_PRODUCTO = :CODIGO) pregunta="¿Guardar en el producto el código EAN13 que proponga el sistema?" **Imprimir por el dispositivo del usuario**, con su plantilla: A:Imprimir etiquetas = imprimir(ET, COD_PRODUCTO = :CODIGO) icono=print-outline tecla=F8 **Imprimir lo que compone la propia base de datos**, sin plantilla: el procedimiento devuelve la etiqueta hecha, una fila por línea: A:Imprimir etiqueta = imprimir(ET) contenido=ZEBRA_ETIQUETA_DOCUMENTO(:NRO_ALBARAN, :PIDE.PAGINAS) pide=PAGINAS:numero("Nº de páginas") **Un valor que depende de lo que devolvió el paso anterior.** El ticket lleva el número de la factura si el albarán está facturado y el suyo si no: A:Imprimir ticket = imprimir(TK, NRO_ALBARAN = :NRO_ALBARAN, NRO_TICKET = si(:SALIDA, :SALIDA, :NRO_ALBARAN), TIPO_DOC = si(:SALIDA, 'Factura simplificada', 'Albarán')) proc=GET_FACTURA_ALBARAN_VTA(:NRO_ALBARAN) **Elegir un código de una tabla antes de ejecutar:** A:Generar alb. traspaso = GEN_ALB_TRASPASO(:NRO_ALBARAN, :PIDE.ALMACEN) pide=ALMACEN:tabla(ALMACEN) **Añadir y quitar**, que es el diálogo de siempre: una lista con una casilla por fila, y un procedimiento que enlaza o desenlaza cada una con el documento: A:Selección de albaranes F10 = LIN_ALB_VENTA_FACTURAR(P_NRO_FRA = :NRO_FACTURA, P_SIM = SIMULACION) modo=seleccion tecla=F10 anade=ANADE_ALBARAN_VENTA_FACTURA(P_NRO_FACTURA = :NRO_FACTURA, P_NRO_ALBARAN = :FILA.S_NRO_ALBARAN, P_NRO_LIN_ALB = :FILA.S_NRO_LINEA) enlace=S_NRO_FACTURA = :NRO_FACTURA Lo que sale de la fila que se conmuta se escribe '':FILA.CAMPO''; el resto de parámetros son valores de la ficha. '':enlace='' dice qué columna del listado refleja el enlace —vacía si no lo está— para pintar las que ya lo están. **Algo que hay que hacer en el programa**, con el layout decidiendo dónde va y cómo se llama: A:Escoger máquina = @escogerMaquina icono=cube-outline ===== Los asistentes ===== Un **asistente** es una ficha de un solo registro cuya clave primaria es el código del usuario: cada uno tiene el suyo, y como mucho uno. Se entra desde el menú —no desde un listado, porque no hay nada que listar—, y si el usuario todavía no tiene registro, la pantalla lo crea vacío al abrirse. Al final, un procedimiento se lleva lo tecleado a un documento de verdad y borra la fila, de modo que la siguiente visita empieza en blanco. Es lo que en SmartOffice son los ''UnitFormAsistente...'' (oferta, alquiler, pedido de compra…), y en SmartPWA una sola pantalla para todos: ''/asistentes/TABLA''. ^ Qué ^ Cómo ^ | La pantalla | ''/asistentes/ASISTENTE_OFERTA'', con la tabla en la propia dirección | | Su layout | ''COD_FORM = asistente-ASISTENTE_OFERTA'': el prefijo ''asistente-'' y el nombre de la tabla, en mayúsculas | | Su título | la descripción de la tabla en el diccionario, o el ''titulo='' de la opción de menú | | La opción de menú | ''Asistente de oferta %%|%% /asistentes/ASISTENTE_OFERTA %%|%% sparkles'' | El layout es un layout normal y corriente: ''P:'', ''C1:'', campos, ''D:'', ''R:'' y ''A:'' hacen lo de siempre. Lo único que la pantalla pone de su parte es el arranque —leer el registro del usuario y crearlo si no está—, el aviso de ''MENSAJE_USUARIO'' (si la tabla tiene ese campo, sale arriba en amarillo y no como un campo más) y la opción **Empezar de nuevo**, que descarta lo tecleado. La tabla tiene que tener **una sola columna de clave primaria**, la del usuario. Si tiene más, la pantalla lo dice y no monta nada: no sabría de qué registro habla. La pantalla publica un método para el layout: ''@empezarDeNuevo'', que es lo mismo que la opción de su menú. Sirve para ponerlo en otro sitio del menú o con otro nombre: A:Descartar y empezar = @empezarDeNuevo icono=refresh-outline ==== El campo se guarda y se relee ==== Como en las demás fichas, cada campo se guarda en cuanto se sale de él y se vuelve a leer el registro entero. En un asistente eso es lo importante: toda su inteligencia está en los disparadores de su tabla —al elegir el cliente se rellenan la razón social, la delegación, la tarifa y el mensaje; al elegir el proyecto, la descripción de la obra—, y es así como lo rellenado por la base de datos aparece en la pantalla. ==== El procedimiento del final ==== La opción que cierra el asistente es una ''A:'' de las de siempre, con el usuario como argumento y ''tras=abrir(...)'' para ir al documento recién creado: A:Generar oferta F11 = ASISTENTE_OFERTA_PROCESA2(USUARIO) tecla=F11 icono=checkmark-circle-outline pregunta="¿Generar la oferta con estos datos?" boton="Generar" tras=abrir(CAB_OFERTA_VENTA, COD_OFERTA) El procedimiento tiene que **recibir el usuario como parámetro**. Los de SmartOffice se apañan con el ''USER'' de la conexión; el layout, en cambio, dice de quién es el asistente con el valor ''USUARIO'', que es con el que se entró en la aplicación y el mismo que lleva la clave del registro. Lo hecho con el asistente de oferta es dejar ''ASISTENTE_OFERTA_PROCESA'' como envoltorio para el Delphi —que sigue pasando su ''USER''— y mover el trabajo a ''ASISTENTE_OFERTA_PROCESA2(P_USUARIO)'', al que llama el layout. Al volver del documento, el asistente se encuentra sin registro y crea otro vacío: lo que se espera de una pantalla que se usa una vez por oferta. ==== El asistente de oferta, entero ==== P:General C1:50% TIPO_TERCERO, COD_TERCERO RAZON_SOCIAL -CLIENTE_DIVERSO COD_PROYECTO -NOM_PROYECTO DESCRIPCION_OBRA ATENCION_DE TELEFONO_CONTACTO CORREO_E C2:50% COD_DELEGACION TIPO_TERC_EMP, COD_TERC_EMP COD_TARIFA COD_TIPO_PORTE COD_TARIFA_PORTE COD_UDAD_ALQ FIANZA A:Generar oferta F11 = ASISTENTE_OFERTA_PROCESA2(USUARIO) tecla=F11 icono=checkmark-circle-outline pregunta="¿Generar la oferta con estos datos?" boton="Generar" tras=abrir(CAB_OFERTA_VENTA, COD_OFERTA) ==== Dar de alta otro asistente ==== No hace falta programa: la tabla ya existe en la base de datos y su ficha se monta sola. - Darla de alta en el diccionario (''Z_DIC_TABLAS'' y ''Z_DIC_CAMPOS''), con sus etiquetas y sus relaciones de F6. - Escribir su layout en ''PWA_FORMS'' con el código ''asistente-''. - Asegurarse de que el procedimiento que lo procesa recibe el usuario. - Añadir la opción al menú, apuntando a ''/asistentes/''. ===== Las consultas también tienen menú ===== Una pantalla de consulta —la que sale del menú con sus parámetros arriba y su rejilla debajo— puede tener sus propias opciones, igual que una ficha. Es lo que en SmartOffice son los formularios de consulta especializados, como el del diario oficial (''UnitQrDiario'': «Borrar asiento», «Modificar datos», «Cambiar fecha»…). El código de formulario es **''consulta-'' seguido del nombre del procedimiento**, tal cual, en mayúsculas: COD_FORM = consulta-GEN_DIARIO De todo el layout, una consulta mira sus opciones de menú (''A:'') y la línea ''Q:'' que dice cómo se comporta; no tiene pestañas de cabecera que colocar ni panel inferior. Si no hay fila en ''PWA_FORMS'', la consulta se comporta como siempre y su menú se queda con el de todas (Calcular, Mostrar SQL, los informes y Copiar url): así se van migrando de una en una. ==== De dónde salen los valores ==== ^ Se escribe ^ Vale ^ | '':P_FEC_INI'' | lo que el usuario haya puesto en ese parámetro, arriba | | '':S_NRO_MOV'' | esa columna **de la fila elegida** en la rejilla | No se pisan porque el diccionario los tiene prefijados: lo que empieza por ''P_'' es un parámetro de entrada y lo que empieza por ''S_'', una columna del resultado. La fila es la del cursor, se llegue a ella con el ratón o con las flechas. La opción que trabaje sobre una fila **tiene que declararlo** con ''necesita=fila'': mientras no haya ninguna elegida se enseña apagada, en vez de ejecutarse con el número en blanco. ''tras=refrescar'' vuelve a calcular la página que se está viendo y deja el cursor donde estaba (si la fila ya no existe —se acaba de borrar—, en la siguiente). Es lo que quieren casi todas. ==== Q: cómo se comporta la consulta ==== Lo que antes estaba en tablas dentro del programa —qué consultas se calculan solas, qué abre el doble clic, de qué color va cada fila— se declara en una línea ''Q:''. Se puede repartir en varias, que se suman: ^ Modificador ^ Qué hace ^ | ''auto=si'' | la consulta se calcula al entrar, sin esperar al F10 (la que no tiene parámetros lo hace igualmente) | | ''abre=TABLA(CAMPO_PK = S_COLUMNA, …)'' | qué ficha abre el doble clic en una fila, y con qué columnas del resultado se arma su clave | | ''ocultas=S_UNO;S_DOS'' | columnas del resultado que no se enseñan | | ''color=hecha(S_REALIZADO = S)'' | pinta la fila cuando esa columna vale eso; los colores son ''hecha'' (verde), ''aviso'' (ámbar) y ''error'' (rojo), se puede repetir y gana la primera que casa | Q:auto=si abre=ACTIVO(CODIGO = S_COD_ACTIVO) La consulta de la ficha de vehículo, entera: se calcula al entrar y el doble clic en cualquier fila lleva a la ficha del activo. Si la tabla tiene ficha propia se abre esa, y si no, el editor genérico. ==== Las teclas ==== El layout puede poner una opción en ''F4'', ''F8'' y ''F11''. **''F10'' no**: en una consulta es calcular, y eso no se le quita. ==== Ejemplo: el diario oficial ==== A:Borrar asiento = BORRARDO_ASIENTOS() icono=trash-outline tecla=F4 necesita=fila proc=BORRA_ASIENTO(:S_NRO_MOV) pregunta="¿Borrar el asiento :S_ASIENTO?" boton="Borrar" tras=refrescar A:Borrar asiento y copiarlo al diario = BORRARDO_ASIENTOS() necesita=fila proc=BORRA_ASIENTO(:S_NRO_MOV) proc=BORRAR_ASIENTO_RESCATA(:S_NRO_MOV, USUARIO) pregunta="¿Borrar el asiento :S_ASIENTO y copiarlo al diario?" tras=refrescar A:Modificar datos = MODIFICAR_DATOS_MOV_CONTA2(P_NRO_LINEA = :S_NRO_LINEA, ?P_CUENTA = :S_CUENTA, ?P_COMENTARIO = :S_COMENTARIO) modo=proceso icono=create-outline necesita=fila tras=refrescar A:Cambiar fecha = CAMBIAR_FECHA_REGISTRO(:S_NRO_MOV, :PIDE.FECHA) necesita=fila pide=FECHA:fecha("Nueva fecha del asiento") tras=refrescar A:Ver diario original = XLINEA_DIARIO[NRO_MOV = :S_NRO_MOV] necesita=fila icono=document-text-outline Tres maneras de hacer lo mismo que el Delphi, y ninguna necesita programa: * El **borrado** encadena los tres procedimientos del original; como los ''proc='' corren antes del destino, el que va al final del Delphi (''BORRARDO_ASIENTOS'') es aquí el destino. * El **diálogo de modificar datos** no se reescribe: con ''modo=proceso'' y los parámetros propuestos (''?'') desde la fila, la pantalla de procesos los enseña rellenos y editables, con el F6 de la cuenta y el tercero que ya trae el diccionario. * La **pregunta de la fecha** es el ''pide='' de siempre. * El **diario original** no ejecuta nada: es un destino de tabla, que abre el listado de ''XLINEA_DIARIO'' acotado al asiento. Lo del Delphi que se hace con dos preguntas encadenadas —«¿Borrar?» y luego «¿Copiar al diario?»— aquí son **dos opciones distintas** del menú, que además se lee mejor. Lo que sea de una instalación concreta —un procedimiento a medida, una opción que solo allí tiene sentido— se declara en el ámbito de esa instalación, no en el general. ===== Recetas ===== ^ Lo que se quiere ^ Cómo se escribe ^ | Que un campo se vea pero no se toque | ''-FECHA_ALTA'' | | Dos campos en la misma línea | ''TIPO_TERCERO, COD_TERCERO'' | | Un cuadro grande para escribir | ''+OBSERVACIONES(300%, 100%)'' | | Un dato que no está en la tabla de la ficha | un ''D:'' y luego ''ALIAS.CAMPO'' | | Una pestaña con las líneas de una tabla hija | ''R:Título = TABLA alta=maximo(NRO_LINEA)'' | | Una pestaña de documentos que se abren, no se teclean | ''R:Título = TABLA [FILTRO = :CAMPO] modo=lista'' | | Que esa pestaña no deje añadir | ''alta=none'' | | Una consulta con fechas que el usuario cambie | ''R:Título = PROC(?P_FEC_INI = HOY-3A, ?P_FEC_FIN = HOY)'' | | Esconder columnas técnicas de una consulta | ''ocultas=CAMPO1;CAMPO2'' | | Saber cuántas filas tiene una pestaña sin abrirla | ''n=si'' | | Una pestaña con el detalle de la línea elegida | ''[NRO_LINEA = :LIN_ALBARAN_VENTA.NRO_LINEA]'' | | Que el doble clic en una consulta abra la ficha | ''abre=TABLA(CAMPO_PK = COLUMNA)'' | | Ordenar las pestañas en dos filas | ''G:Tablas'' y ''G:Consultas'' | | Una opción que ejecute un proceso y pregunte antes | ''A:Título = PROC(:CAMPO) pregunta="¿…?"'' | | Que además abra el documento creado | ''tras=abrir(TABLA, CAMPO_PK)'' | | Abrir el documento salga de la clase que salga | un ''abrir'' por clase, cada uno con su ''= :SALIDA.COLUMNA'' | | La clave de una tabla de líneas | ''pk=COD_OFERTA;NRO_LINEA;COD_UDAD'' | | Cambiar un campo del registro | ''campo(MARCADO = 'S')'' | | Elegir un código de una tabla | ''pide=ALMACEN:tabla(ALMACEN)'' | | Imprimir por el dispositivo del usuario | ''imprimir(ET, COD_PRODUCTO = :CODIGO)'' | | Añadir y quitar filas de una lista | ''modo=seleccion'' con ''anade='' y ''enlace='' | | Un valor distinto según lo que devolvió un paso | ''si(:SALIDA, :SALIDA, :NRO_ALBARAN)'' | | Hacer una cosa u otra según lo que devolvió | ''tras=si(:SALIDA2=AV) abrir(...);si(:SALIDA2<>AV) aviso("...")'' | | Mirar otra columna de lo devuelto | '':SALIDA.TIPO'' o '':SALIDA2'' | | Imprimir lo que compone un procedimiento | ''imprimir(ET)'' con ''contenido=PROC(args)'' | | Enseñar el listado de lo que devuelve un proceso | ''tras=listado(TABLA, CAMPO = :SALIDA)'' | | Que además recargue la ficha | ''tras=refrescar'' | | Una opción solo para los registros sin cerrar | ''si=CERRADO<>S'' | | Preguntar un dato antes de ejecutar | ''pide=OFERTA:texto("Nº de oferta")'' y '':PIDE.OFERTA'' | | Encadenar dos procedimientos | ''proc=UNO(:CAMPO)'' y '':SALIDA'' en el destino | | Quitarle a una instalación una opción o una pestaña | borrar esa línea de **su** fila de ''PWA_FORMS'' | ===== Un layout completo, en pequeño ===== D:SIT = SITUACION_ACTIVO_A_FECHA(:COD_PRODUCTO, :NRO_SERIE, :FECHA_PARTE) P:General C1:50% NRO DESCRIPCION COD_PRODUCTO, NRO_SERIE C2:50% FECHA_PARTE -CERRADO, -FECHA_CIERRA P:Último tenedor C1:100% SIT.S_TIPO_TERCERO SIT.S_COD_TERCERO P:Comentarios C1:100% +COMENTARIOS(300%, 100%) G:Tablas R:Albaranes de venta = CAB_ALBARAN_VENTA [NRO_PARTE_AVERIA = :NRO] modo=lista G:Consultas R:Facturas = FACTURAS_EN_AVERIA(P_NRO_PARTE = :NRO) A:Cerrar avería = CIERRA_AVERIA(:NRO, NULL) icono=lock-closed-outline ==== El más corto que hay en producción ==== La ficha de series, entera: P:General C1:50% CODIGO DESCRIPCION SERIE_RELACIONADA C2:50% SIMULACION OMITIR_CTRL_BLOQ_ULT_FRA Una pestaña, dos columnas, cinco campos. No hace falta más para que la ficha funcione: sin ''R:'' no hay panel inferior, y sin ''A:'' el menú es el de siempre. ===== Páginas relacionadas ===== * [[pwa:doc:Documentación diseño de menús en SmartPWA|Diseño del menú (PWA_MENUS)]], que se declara con la misma idea y otra sintaxis. * [[pwa:doc:Documentación de ámbitos en SmartPWA|Ámbitos: quién ve qué]], común a formularios, menús e informes. ===== Cómo se mantiene esta página ===== Este meta-lenguaje crece: cada vez que una ficha necesita algo que no se podía declarar, se le añade una directiva o un modificador. **La página se actualiza en el mismo cambio**, que es la única manera de que siga sirviendo. Lo que hay que mirar en el código, por si esta página se ha quedado atrás: ^ Qué ^ Dónde ^ | Directivas, campos, datasets y rejillas | ''projects/smart-core/src/lib/formularios/layout-cabecera.ts'' | | Opciones del menú (''A:'') | ''projects/smart-core/src/lib/formularios/acciones-layout.ts'' | | Valores (''%%:CAMPO%%'', ''HOY'', ''SIMULACION''…) | ''projects/smart-core/src/lib/formularios/valores-layout.ts'' | | Ejecución de las opciones del menú | ''projects/smart-core/src/lib/servicios/acciones-layout.service.ts'' | | Iconos que se pueden nombrar | ''projects/smart-core/src/lib/componentes/acciones-popover/iconos-acciones.ts'' | | Ámbitos y precedencias | ''projects/smart-core/src/lib/utilidades/pwa-forms.ts'' y ''form-layout.service.ts'' | | La pantalla de los asistentes | ''projects/smart-core/paginas/src/lib/asistente-form/asistente-form.page.ts'' | | Layouts generales versionados | ''db/pwa_forms_layouts.sql'' |