Veces vista: 3.937
Ya he terminado de codificar esta versión. No he recogido ninguna sugerencia de ningún usuario porque no he recibido ninguna. La única que recibí hace tiempo era la de poner todo el producto en inglés y eso es lo que he hecho.
He incorporado bastantes cambios con respecto a la versión anterior, normalmente para simplificar tanto el modelo de datos como el uso de la solución.
Hay una parte que ha quedado bastante compleja, y es la de multi-idioma. No sólo la aplicación está en Inglés y Español, si no que contenidos de tablas y catálogos (no todos) están en los dos idiomas, con posibilidad de extenderlos a más idiomas.
A nivel técnico, es similar a la versión anterior. Es MySQL y PHP, almacenando los ficheros Word y Excel en “filesystem”. Para construir los documentos Word y Excel he utilizado las librerías de PHPOffice (podéis consultarlas en GITHUB).
¿Qué es esta solución?
Es una solución de múltiples workflows realizados en PHPRunner. El hecho de ser múltiples es porque normalmente cuando se acomete la mecanización de procesos de un departamento o empresa, suelen ser varios procesos los que se gestionan en esa unidad y por funcionalidad, es mejor que en una aplicación se gestionen todos ellos. Si fuesen muchos los procesos y muy diferentes, esta solución no serviría y haría falta fraccionarlos en grupos de procesos similares.
¿Para qué se puede utilizar?
La aplicación utiliza la técnica de workflow de gestión de cambios de estados. Es decir, un expediente siempre tiene un único estado y dependiendo de dicho estado podrá transitar (trámite) a otro u otros, por el grupo de usuarios que tengan el acceso al mismo.
Por esta forma (sencilla) de workflow, y teniendo en cuenta que su principal potencial es la creación de documentos (Word y Excel) y su gestión documental, la aplicación es válida para:
- Gestión de procedimientos administrativos de cualquier administración pública.
- Gestión de procesos de RRHH de cualquier empresa.
- Gestión de procesos de un departamento mediano/grande de informática, para la gestión de peticiones de trabajos entre Unidades.
- Cualquier proceso sencillo, que involucre a varias Unidades y donde requiera una gestión documental (documentos que se reciben y/o generan)
¿Qué gestión de seguridad se utiliza?
Utiliza de forma general el sistema de control de acceso de PHPRunner, pero además utiliza un sistema de seguridad adicional y complementaria, para cada uno de los Procesos mecanizados. Esta gestión es por grupos o perfiles de usuarios, basados los accesos en el estado del expediente.
Explicaciones del interfaz del la aplicación
(1).- Cuando te identificas en el aplicativo tienes que indicar con qué Proceso quieres trabajar. También, en cualquier momento puedes cambiar de Proceso a través de esta opción del menú.
(2).- Para aquellos usuarios que pueden crear nuevos expedientes les sale esta opción y desde ahí van al formulario que les permite dar de alta un nuevo expediente.
(3).- En esta opción se puede consultar los expedientes que están en el sistema.
(4).- En esta opción te presenta de forma gráfica la lógica de estados y transiciones entre los mismos, que contempla el Proceso.
(5).- Es el submenú donde están todas las tablas de parametrización del proceso y sus plantillas de los documentos y código para producir los documentos.
(6).- Este submenú del control de acceso al Proceso y la gestión de usuarios.
(7).- Es el submenú de la gestión de las tablas de catálogos, donde están en multi-idioma y la configuración del aplicativo.
Estando en la consulta de “bandeja de expedientes”, muestra:
(8).- Muestra los Expedientes que el usuario conectado podrá tramitar. El trámite termina si el Expediente cambia de estado.
(9).- Muestra los Expedientes que ha creado el usuario conectado. Esto es importante para por ejemplo la gestión de RRHH y consultar rápidamente cómo va mi expediente.
(10).- Muestra todos los expedientes Activo, es decir, lo que todavía no han llegado al estado final del Proceso.
(11).- Muestra todos los Expediente sin tener en cuenta los estados.
(12).- El sistema dispone de un conjunto de alarmas que puedes poner en cada Expediente, por ejemplo, para recordar hasta cuándo se puede esperar documentación complementaria o plazos administrativos. El sistema utiliza esa información en esas 2 columnas (Alarmas Activas y Alarmas Urgentes). En activa informa que existe una alarma que está vigente y en urgente que al menos una alarma activa está vencida su fecha y requiere una acción inmediata. Si el expediente tiene una alarma urgente el código del mismo se pone en rojo.
(1).- Una vez se ha seleccionado un Expediente de la “Bandeja de expedientes” aparece esta opción que muestra la información del Expediente seleccionado y lo que podemos hacer con dicho Expediente.
(2).- Aparecerá en el submenú de Trámites los trámites que dependiendo del estado y del usuario conectado, se pueden realizar.
(3).- También aparecen las Utilidades que el usuario puede hacer. Estas Utilidades son acciones que no dependen del estado del Expediente si no que dependen exclusivamente del perfil del usuario conectado. Pueden ser acciones se sólo afecten al Expediente seleccionado o a otro conjunto de Expedientes.
(4).- Es información general del Expediente seleccionado con el que hemos elegido trabajar.
(5).- Muestra las acciones que se pueden hacer (visualización, edición) y también el número de registros de información complementaria. Hay 3 informaciones complementarias como mínimo.
+ Las Alertas (6).
+ Los Documentos asociados (7).
+ Log o Auditoria de todas las acciones (trámites y utilidades) que se han hecho (8).
En la opción de Flujograma, el aplicativo construye un gráfico (1) utilizando la técnica de diagrama de cambio de estado donde muestra todos los estados del Proceso y los Trámites que se pueden hacer.
En la parte inferior (2) muestra los grupos o perfiles que pueden ejecutar cada uno de los trámites.
¿Qué ejemplos de Procedimientos hay realizados?
Para que se pueda apreciar que es un producto multi-workflow he puesto 2 ejemplos:
EX01 – Liquidación de Gastos de Viajes
EX04- Solicitud de anticipo de nómina.
El ejemplo más completo, porque tiene desarrollado documentos Word y Excel es EX04
Los usuarios que hay en la demo son (el login y password son iguales):
admin.- Administrador del sistema
user1.- Usuario general
user2.- Usuario general
boss1.- Usuario jefe de user1 y user2
rrhh1.- Usuario de RRHH.
accounting1.- Usuario de Contabilidad
Probar los controles de acceso en este tipo de solución es complejo, por lo que es posible que salga algún problemilla que tenga que solucionar.
¿Cómo incorporar otro Proceso?
Muy importante, tienes que haber valorado si esta solución puede mecanizar dicho Proceso y para ello tienes que ver si es de alguno de los tipos que se han descrito o escribir [email protected], para que te asesore.
Los datos que tienes que tener son:
+ Flujograma del proceso. Yo utilizo la herramienta gratis de Bizagi Modeler.
+ Los documentos que se tienen que obtener en la tramitación.
+ La información (datos) que vamos a tener que extender el modelo de datos y para ello es necesario conocer los datos básicos del Expediente.
Teniendo esto, lo primero que tenemos que hacer es:
+ Crear y registrar todos los nombre de Estados, Trámites y Utilidades, en los idiomas que tengamos (opción de súper catálogos). Similar a como están los de EX04 y EX01.
+ Darle un código al Proceso y registrarlo en el submenú de parámetros.
+ Darle los valores para la numeración de los Expedientes de este Proceso.
+ Autorizar al usuario “admin” al nuevo proceso.
+ Introducir todos los parámetros del nuevo Proceso.
+ Abrir el proyecto con PHPRunner, para generar los formularios básicos del nuevo Proceso.
Copiamos las vistas del Proceso que más se parezca y las identificamos con el código del nuevo Proceso.
+ Se personalizan estas vistas con lo que requiera el nuevo Proceso.
Como puede ser un poco complejo al principio, para aquellos que deseéis que os cree una versión para vosotros con 2 Procesos que me tendréis que pasar la información, yo os hago la transformación y os lo envío por email.
+ Se comprueba si todos los parámetros están completos viendo en flujograma que el sistema genera a través de la opción del menú.
+ Cuando ya está funcionando la tramitación, entonces iniciamos la confección de documentos en la tramitación.
El informe/documento puede ser Word o Excel y siempre pueden tener 3 ficheros:
- La plantilla de Word o Excel. Obligatorio
- El código PHP para hacer el documento (fichero con extensión txt). Obligatorio
- El código PHP de condición. Este se utiliza para los casos en que dependiendo de algún valor de Expediente se tenga que utilizar plantillas distintas. No Obligatorio.
En los ejemplos mostrados se indica cómo se deben construir los códigos PHP para construir los documentos.
Ejemplo para obtener Word
<?php
include_once '../ComponentCode/PHPWord_0.17/autoload.php';
// Template processor instance creation
$template_workflow = $_SESSION['InformeDOCX'];
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($template_workflow);
// -------------------- ^ header needed for Word templates ------------------
$VidProcess = $_SESSION['idProcess'] ;
$VsuperCodeStatus = $_SESSION['superCodeStatus'];
$Vlanguage = $_SESSION['language'];
$VidExpedient = $_SESSION['idExpedient'];
$Viduser=$_SESSION['iduser'];
$sql="
SELECT
expedient.idexpedient,
expedient.process_idprocess,
expedient.ct_status_idct_status,
concat (status.catalogNum,' - ',status.catalogCode) Name_status,
expedient.codeExpedient,
expedient.title,
expedient.observations,
expedient.assignedUser,
ifnull(user1.NameAndSurname,'') Name_assignedUser,
expedient.creationDate,
expedient.creationUser,
ifnull(user2.NameAndSurname,'') Name_creationUser,
expedient.lastUpdateDate,
expedient.lastUpdateUser,
ifnull(user3.NameAndSurname,'') Name_lastUpdateUser,
expedient.finishDate,
expedient.ex04_advanceAmount
FROM expedient
join (
SELECT
s.idct_status,
s.catalogNum,
n.catalogCode
FROM ct_status s
join (
SELECT
s.idsuper_catalog,
s.superCode,
c.language,
c.catalogNum,
c.catalogCode,
c.description,
c.groupNum
FROM catalog AS c
INNER JOIN super_catalog AS s ON c.super_catalog_idsuper_catalog = s.idsuper_catalog
) n on ( s.catalogNum = n.catalogNum )
where process_idprocess = $VidProcess and n.superCode = '$VsuperCodeStatus' and n.language = '$Vlanguage') status
on (status.idct_status = expedient.ct_status_idct_status )
left join user user1 on (user1.iduser = expedient.assignedUser)
left join user user2 on (user2.login = expedient.creationUser)
left join user user3 on (user3.login = expedient.lastUpdateUser)
where expedient.idexpedient = $VidExpedient
";
$resql=db_query($sql,$conn);
$data=db_fetch_array($resql);
// Variables on different parts of document
$templateProcessor->setValue('Name_creationUser', $data['Name_creationUser']);
$templateProcessor->setValue('ex04_advanceAmount ', number_format($data['ex04_advanceAmount'], 2, ',', '.'));
$today = date("Y-m-d H:i:s");
$templateProcessor->setValue('today_date', formatDate($today));
// -------------------- v foot to save new Word document ------------------
$temp_file = tempnam(sys_get_temp_dir(), 'PHPWord');
$templateProcessor->saveAS($temp_file);
// $ temp_file must contain the resulting file
Ejemplo de condición
<?php
// se deben finalizar poniendo los valores 0 o 1 a la variable "$ReportMustBeCreated"
// they must be ended by putting the values 0 or 1 to the variable "$ ReportMustBeCreated"
$ReportMustBeCreated=1;
// 0 = no se crea el informe | 1 = Sí se crea
// 0 = report is not created | 1 = Yes it is created
?>
Ejemplo para obtener Excel
<?php
require_once __DIR__ . './../../../ComponentCode/PhpSpreadsheet_1.10/autoload.php';
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
// Template processor instance creation
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
// $reader->setIncludeCharts(true);
// $reader->setReadFilter();
// $reader->setReadEmptyCells(true);
$template_workflow = $_SESSION['InformeEXCEL'];
$base = __DIR__;
$folder = $_SESSION['config'][array_search('DOCUMENT_ROOT_WF2', array_column($_SESSION['config'], 'name'))][value];
$spreadsheet = $reader->load($base. '/'.$folder . $template_workflow);
$spreadsheet->setActiveSheetIndex(0); // Activate the first sheet
$DateList=now();
$spreadsheet->getActiveSheet()->setCellValue('E2', Date::PHPToExcel($DateList)); // Fill in a cell with the current date in excel format
// Capture data from the BD
$VidProcess = $_SESSION['idProcess'] ;
$VsuperCodeStatus = $_SESSION['superCodeStatus'];
$Vlanguage = $_SESSION['language'];
$VidExpedient = $_SESSION['idExpedient'];
$sql =
"SELECT
expedient.idexpedient,
expedient.process_idprocess,
expedient.ct_status_idct_status,
concat (status.catalogNum,' - ',status.catalogCode) Name_status,
expedient.codeExpedient,
expedient.title,
expedient.observations,
expedient.assignedUser,
ifnull(user1.NameAndSurname,'') Name_assignedUser,
expedient.creationDate,
expedient.creationUser,
ifnull(user2.NameAndSurname,'') Name_creationUser,
expedient.lastUpdateDate,
expedient.lastUpdateUser,
ifnull(user3.NameAndSurname,'') Name_lastUpdateUser,
expedient.finishDate,
expedient.ex04_advanceAmount
FROM expedient
join (
SELECT
s.idct_status,
s.catalogNum,
n.catalogCode
FROM ct_status s
join (
SELECT
s.idsuper_catalog,
s.superCode,
c.language,
c.catalogNum,
c.catalogCode,
c.description,
c.groupNum
FROM catalog AS c
INNER JOIN super_catalog AS s ON c.super_catalog_idsuper_catalog = s.idsuper_catalog
) n on ( s.catalogNum = n.catalogNum )
where process_idprocess = $VidProcess and n.superCode = '$VsuperCodeStatus' and n.language = '$Vlanguage') status
on (status.idct_status = expedient.ct_status_idct_status )
left join user user1 on (user1.iduser = expedient.assignedUser)
left join user user2 on (user2.login = expedient.creationUser)
left join user user3 on (user3.login = expedient.lastUpdateUser)
ORDER by 3";
global $conn;
$result = $conn->query($sql);
$data = $result->fetch_all(MYSQLI_NUM); // faster - All record
$dataRows = count($data);
$spreadsheet->setActiveSheetIndex(1); // Activate the second sheet
$spreadsheet->getActiveSheet()->fromArray($data, null, 'A2'); // Load ALL records in the active sheet
// Define named ranges
$spreadsheet->addNamedRange(new NamedRange('Datos01', $spreadsheet->getActiveSheet(), 'A2:'.'Q'.($dataRows-1+2)));
$spreadsheet->setActiveSheetIndex(0); // Activate the first sheet
$spreadsheet->getActiveSheet()->insertNewRowBefore(5, ($dataRows)); // Copy presentation formats
$spreadsheet->getActiveSheet()->removeRow(($dataRows-1+5), 1); // Delete last row
$spreadsheet->addNamedRange(new NamedRange('DatosPrint001', $spreadsheet->getActiveSheet(), 'A3:'.'R'.($dataRows+3))); // Define print range
// Presentation Field Formulas
$formulaA4 = $spreadsheet->getActiveSheet()->getCell('A4')->getValue();
$formulaB4 = $spreadsheet->getActiveSheet()->getCell('B4')->getValue();
$formulaC4 = $spreadsheet->getActiveSheet()->getCell('C4')->getValue();
$formulaD4 = $spreadsheet->getActiveSheet()->getCell('D4')->getValue();
$formulaE4 = $spreadsheet->getActiveSheet()->getCell('E4')->getValue();
// Build the formulas with definitive references
$dataArray = [];
for ($x = 4; $x <= $dataRows+3; $x++) {
$dataArray[] = [
str_replace("NumRow", $x-3, $formulaA4),
str_replace("NumRow", $x-3, $formulaB4),
str_replace("NumRow", $x-3, $formulaC4),
str_replace("NumRow", $x-3, $formulaD4),
str_replace("NumRow", $x-3, $formulaE4)];
}
$spreadsheet->getActiveSheet()->fromArray($dataArray, null, 'A4'); // Copy formulas in all fils
// Break page by row
$spreadsheet->setActiveSheetIndex(0);
$Ruptura=$data[0][3];
for ($x = 0; $x <= $dataRows; $x++) {
if ($Ruptura <> $data[$x][3] ) {
$Ruptura=$data[$x][3];
$spreadsheet->getActiveSheet()->setBreak('A'.($x+3), \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW); // Rupture for printing. Page break
}
}
// Break page by column
//$spreadsheet->getActiveSheet()->setBreak('C10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN);
// Show/hide gridlines when printing
$spreadsheet->getActiveSheet()->setShowGridlines(false); // Disable grid in print
// Config print file Excel
// $spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
$spreadsheet->getActiveSheet()->getPageSetup()->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT);
$spreadsheet->getActiveSheet()->getPageSetup()->setPaperSize(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::PAPERSIZE_A4);
// Page Setup: Scaling options
$spreadsheet->getActiveSheet()->getPageSetup()->setFitToPage(FALSE);
$spreadsheet->getActiveSheet()->getPageSetup()->setScale(85);
// $spreadsheet->getActiveSheet()->getPageSetup()->setFitToWidth(0);
// $spreadsheet->getActiveSheet()->getPageSetup()->setFitToHeight(0);
// Page margins
// $spreadsheet->getActiveSheet()->getPageMargins()->setTop(1);
// $spreadsheet->getActiveSheet()->getPageMargins()->setRight(0.6);
// $spreadsheet->getActiveSheet()->getPageMargins()->setLeft(0.6);
// $spreadsheet->getActiveSheet()->getPageMargins()->setBottom(1);
// Center a page horizontally/vertically
$spreadsheet->getActiveSheet()->getPageSetup()->setHorizontalCentered(false);
$spreadsheet->getActiveSheet()->getPageSetup()->setVerticalCentered(false);
// Setting the print header and footer of a worksheet
// $spreadsheet->getActiveSheet()->getHeaderFooter()->setOddHeader('&C&HPlease treat this document as confidential!');
$spreadsheet->getActiveSheet()->getHeaderFooter()->setOddFooter('&L&B' . $spreadsheet->getProperties()->getTitle() . '&RPage &P of &N');
// Setting rows/columns to repeat at top/left
$spreadsheet->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(1, 3);
// Specify printing area
$spreadsheet->getActiveSheet()->getPageSetup()->setPrintArea('A1:'.'E'.($dataRows+3));
// Create file XLSX
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$spreadsheet->setActiveSheetIndex(0);
// -------------------- v foot to save the new Excel document ------------------
$temp_file = tempnam(sys_get_temp_dir(), 'Excel');
// Save EXCEL
$writer = new Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false); //Disable formula validation
$writer->save($temp_file);
// $ temp_file must contain
Como siempre, me podéis hacer cualquier consulta a través de [email protected] y os dejo los códigos para que lo podáis instalar en vuestros equipos.
Animaros a describir las necesidades o gustos que tendríais en este tipo de solución. El objetivo es hacer algo que os sirva, sea práctico y sencillo de utilizar en vuestros proyectos.
Podéis describirlo en los comentarios, para que sea visible para todos o enviarmelo a mi email [email protected]
A aquellos que os animéis. ¡¡¡MUCHAS GRACIAS!!!
Very interesting
Will definitely look into this as we need to automate some processes here at work.
como siempre, fernando, es un placer siempre entrar a esta plataforma y deleitarse con nuevos ejemplos, felicitaciones, gracias por compartir sus conocimientos.
He descargado la aplicación de la versión 3 de workflow, revisando varias características que me parecen interesantes, pero no he obtenido los resultados anunciados a través de la aplicación.
Me parece que es laborioso y poco óptimo tener que recrear procesos cada vez que se crea un nuevo proceso, que tampoco logré pasar a elegir de la opción del primer menú (Seleccionar Proceso). Si descargué el Bizagi modeler (no sabía que era gratuito), conozco algo de BPM y puedo crear el diagrama, pero no sé ni siquiera por dónde o cómo cargarlo hacia la aplicación.
La librería de carga de Javascript para el diagrama es otro punto interesante.
No tengo claro si un usuario puede editar y guardar un documento Word o Excel a través de la aplicación, o lo hace externamente y luego lo carga para mantenerlo dentro de la opción que le corresponda del proceso. En fin, son varias inquietudes y algunos descubrimientos que me hicieron descargar su aplicación, pero no son muy claras las definiciones ni las intenciones de ésta. Muchas gracias.