Este ejemplo que he hecho es motivado porque existe mucha inquietud en sincronizar la actualización de “Maestro y Detalle”, por ejemplo, en una “Factura” con las líneas de la “Factura” y que este ejemplo sirva para este y otros casos donde tenemos esta relación de Maestro y Detalle.
Para explicarlo, pongo las imágenes del ejemplo.
Objetivo:
Se desea mostrar la información del Máter “Factura” en donde se dispone del total de la valoración de los productos, que todos ellos están en la parte inferior de la página.
En la acción de dar de alta la Factura, en esa misma acción se podrá informar del detalle de los productos y se irá refrescando por JavaScript el total de la factura (incluyendo la cancelación de algún alta de producto).
En el caso de revisión o edición de la Factura, se realizará esta misma actualización por cada cambio que se produzca en productos y se utilizará otro método alternativo, con refresco de la página de edición de Factura.
DEMO: https://fhumanes.com/javascript/
Solución Técnica
Como he indicado es diferente tratamiento para la acción de “Add” y la acción de “Edit”, por lo que utilizamos una variable de sesión y una variable de proxy, para que en la gestión de las “Líneas” sepamos en qué acción estamos.
Existe un “Tips”, en el Foro de PHPRunner “Calculating order totals on the fly” que es de donde he partido, pero a diferencia de él, os dejo un ejemplo completo y algunas características que en el citado artículo no se describen.
Una de esas características especiales es cómo hacer un “Reload” de la página cuando hemos hecho uno o varios “deletes” de líneas de facturas.
Paso a describiros los códigos realizados:
- En “Factura”.
- Tanto en Add como en Edit, evento “Before Display”
En este punto se definen las variables de sesión “ACCION” que indica si estamos en Add o Edit, y la variable “IDFACTURA”, que es la clave primaria que identifica a la Factura.$_SESSION["ACTION"] = "Add";
- En Add, evento “After record added”, es donde creamos la variable de sesión “IDFACTURA”, puesto que hasta no hacer el insert no se tiene este valor.
$_SESSION['IDFACTURA'] = $values['idfactura'];
- Tanto en Add como en Edit, evento “Javascript Onload event” ponemos en “readonly” las variables que no queremos que se actualicen directamente y creamos una variable JS para guardar el objeto de la página. Este objeto lo vamos a utilizar posteriormente para producir el “Reload” de la página.
var TotalFactura = Runner.getControl(pageid, 'Totalfactura'); // TotalFactura.makeReadonly(); var cliente_idcliente = Runner.getControl(pageid, 'cliente_idcliente'); cliente_idcliente.makeReadonly(); var Nif = Runner.getControl(pageid, 'Nif'); Nif.makeReadonly(); var Domicilio = Runner.getControl(pageid, 'Domicilio'); Domicilio.makeReadonly(); var RestoDomicilio = Runner.getControl(pageid, 'RestoDomicilio'); RestoDomicilio.makeReadonly(); var Email = Runner.getControl(pageid, 'Email'); Email.makeReadonly(); // Para Reload of page EDIT window.customersPage = pageObj;
- Tanto en Add como en Edit, evento “Before Display”
- En “Líneas Factura”.
- En List, se utilizan estos dos eventos, para producir el “Reload” de la página cuando se eliminar “Líneas Factura”. Evento “After record deleted”.
Cuando se han eliminado los registros se cargan dos variables de Proxy (para subir al navegador) “RELOAD” y “IDFACTURA”.
Además se llama la código PHP que recalcula el “TotalFactura” y se actualiza el registro “Factura”.$pageObject->setProxyValue("RELOAD", "Sí"); $pageObject->setProxyValue("IDFACTURA", $_SESSION['IDFACTURA']); include "MyCode/total.php"; // Actualizar total
<?php $idfactura = $_SESSION['IDFACTURA']; $sql = "SELECT sum(Valor) total FROM linea_factura where factura_idfactura = $idfactura"; $rs = DB::Query($sql); $data = $rs->fetchAssoc(); $total = $data['total']; $sql = "update factura set TotalFactura = $total where idfactura = $idfactura"; DB::Exec($sql); ?>
- En List, evento “Javascript Onload event”.
Si existe la variable “RELOAD” es porque se viene de haber hecho “DELETE” de registros y se hace un RELOAD de la página (guardada anteriormente) y que corresponde al registro de “Factura”. Se utiliza “IDFACTURA” para volver a mostrar ese mismo registro.// Reload age Master if( proxy['RELOAD'] ) { window.customersPage.reload({editid1:proxy['IDFACTURA']}); }
- Tanto en Add como en Edit, se ha utilizado el evento “After record added/updated”.
Para ejecutar el proceso que actualiza “TotalFactura” de las líneas de la factura.include "MyCode/total.php"; // Actualizar total
- Tanto en Add como en Edit, se utiliza el evento “Before display”.
Para crear las variables proxy (se subida al navegador) “ACTION” y “IDFACTURA”.$pageObject->setProxyValue("ACTION", $_SESSION["ACTION"]); $pageObject->setProxyValue("IDFACTURA", $_SESSION['IDFACTURA']);
- En Add se utiliza el evento “Javascript Onload event”.
Para hacer los cálculos de multiplicar el Precio por la Cantidad. Veréis que en este punto, al utilizar el marcado de los números como es España, el símbolo decimal es la coma “,” y en este aso es necesario pasarlo a punto “.”, para hacer operaciones en javascript.
También, diferencia si estamos en la ACTION= Add, porque en caso de Add, se hace el recalculo de “TotalFactura” en javascript y de lo contrario, cuando se acepte el Add, se hace la recarga de la página.function calcTotals() { // Cálculo de "TotalFactura" y su almacenamiento var total= 0; $("tr[id*=gridRow]").each(function() { var id=$(this).attr('id'); id = id.substring(7); total = total + parseFloat(($("#readonly_value_Valor_"+id).val()).replace(",", ".")); }); $("#value_TotalFactura_1").val(total); }; var ACTION = proxy['ACTION']; // Para conocer la página del Máster var producto_idproducto = Runner.getControl(pageid, 'producto_idproducto'); producto_idproducto.makeReadonly(); var Precio = Runner.getControl(pageid, 'Precio'); Precio.makeReadonly(); var Valor = Runner.getControl(pageid, 'Valor'); Valor.makeReadonly(); var Cantidad = Runner.getControl(pageid, 'Cantidad'); Cantidad.makeReadonly(); Precio.on('change', function(e){ Cantidad.makeReadWrite(); if ( Cantidad.getValue() != 0 ) { Valor.setValue(parseFloat(Precio.getValue().replace(",", ".")) * parseFloat(Cantidad.getValue().replace(",", "."))); if (ACTION == 'Add') { calcTotals(); } } }); Cantidad.on('change', function(e){ Valor.setValue(parseFloat(Precio.getValue().replace(",", ".")) * parseFloat(Cantidad.getValue().replace(",", "."))); if (ACTION == 'Add') { calcTotals(); } }); if(inlineRow){ // En el caso de que se cancele una operación de Add Línea inlineRow.onCancel = function(){ if (ACTION == 'Add') { calcTotals(); } }; } // Reload age Master this.on('afterInlineAdd', function(){ if (ACTION != 'Add') { window.customersPage.reload({editid1:proxy['IDFACTURA']}); } });
- En Edit se utiliza el evento “Javascrit Onload event”.
Para hacer los cálculos de Precios por Cantidad y para hacer los “Reload” del Máster cuando las operaciones se hayan completado.var producto_idproducto = Runner.getControl(pageid, 'producto_idproducto'); producto_idproducto.makeReadonly(); var Precio = Runner.getControl(pageid, 'Precio'); Precio.makeReadonly(); var Valor = Runner.getControl(pageid, 'Valor'); Valor.makeReadonly(); var Cantidad = Runner.getControl(pageid, 'Cantidad'); // Cantidad.makeReadonly(); Precio.on('change', function(e){ // Cantidad.makeReadWrite(); if ( Cantidad.getValue() != 0 ) { Valor.setValue(parseFloat(Precio.getValue().replace(",", ".")) * parseFloat(Cantidad.getValue().replace(",", "."))); } }); Cantidad.on('change', function(e){ Valor.setValue(parseFloat(Precio.getValue().replace(",", ".")) * parseFloat(Cantidad.getValue().replace(",", "."))); }); // Reload age Master this.on('afterInlineEdit', function(){ window.customersPage.reload({editid1:proxy['IDFACTURA']}); });
- En List, se utilizan estos dos eventos, para producir el “Reload” de la página cuando se eliminar “Líneas Factura”. Evento “After record deleted”.
Actualización
En la versión PHPRunner 10.7 se ha corregido algunos problemas y se ha fijado que tiene que dar de alta una Factura con al menos 1 línea de factura.
Como en todas las ocasiones, podéis preguntarme cualquier duda o problema en mi cuenta de email [email protected]
También, dejo como adjunto de este artículo, el ejemplo para que lo podáis reproducir en vuestros equipos.