DEMO Facturación
Esto es un ejemplo de una falsa interfaz de administración.
Generamos una lista de clientes (consulta tabla clientes en BB.DD.) con un enlace para generar una "factura":
Eduardo Ramos:
Generar factura
Cesar Martín:
Generar factura
The "Making off"
El propósito de esta sección es explicar como se hizo esta demo y que funcionalidad de interés pueda tener para entornos reales de producción.
La plantilla de Office

Todos los formatos disponibles (PDF, DOC y HTML) han sido generados a partir de una misma plantilla creada en MS Word y guardada como "Open Document Format" (.odt).
Todos los "campos dinámicos" han sido etiquetados en texto plano utilizando el formato: {{nombre_variable}}.
Podéis descargar aquí la plantilla utilizada.
Está plantilla es editable en MS Word, Libre Office y Open Office.
La base de datos
Los datos cargados en las plantillas provienen de sendas consultas a una base de datos en MySQL (aunque cualquier otra fuente de datos hubiera sido igualmente válida).
la base de datos está sólo compuesta por dos tablas:
- Clientes: con datos generales sobre los mismos (en nuestro caso Eduardo Ramos y Cesar Martín).
- Items: con los "productos" adquiridos por cada cliente.
Podéis descargar un "dump" de la base de datos aquí.
Obviamente esta base de datos de juguete no pretende ser realista sino simplemente ilustrar el proceso.
El script (PHP)
A continuación incluimos el script utilizado para generar los documentos finales en formatos PDF, DOC y HTML (los formatos RTF y ODT podrían también estar igualmente disponibles si así se deseara) a partir de la plantilla y los datos almacenados:
<?php require_once 'conn.php'; //connection info not shown for obvious security reasons :-) //an auxiliar function to format properly the numeric data function str_replacement($var){ return (string) number_format((float) $var, 2, ',', '.'); } //some arrays used to store information $vars = array(); $vars2 = array(); $varsTable = array(); //query to extract the general info about the requested client $firstQuery = "SELECT id, numero, NIF, nombre, apellidos, direccion, cp, provincia FROM clientes WHERE NIF = ?"; $document = $mysqli->prepare($firstQuery); $document->bind_param('s', $_GET['NIF']); $result = $document->execute(); $document->store_result(); $document->bind_result($id, $numero, $NIF, $nombre, $apellidos, $direccion, $cp, $provincia); while($document->fetch()){ //prepare the arrays for the insertion of the global client data $vars['numero_cliente'] = array('value' => (string) $numero); $vars['fecha'] = array('value' => date("d/m/Y")); $vars['facture'] = array('value' => substr(time(), 0, -4)); $vars['NIF'] = array('value' => $NIF); $vars['nombre'] = array('value' => $nombre); $vars['apellidos'] = array('value' => $apellidos); $vars['direccion'] = array('value' => $direccion); $vars['cp'] = array('value' => (string) $cp); $vars['provincia'] = array('value' => $provincia); $vars['rec'] = array('value' => '0'); $vars['recargo'] = array('value' => '0,00'); $varsTable['item'] = array(); $varsTable['cant'] = array(); $varsTable['precio'] = array(); $varsTable['iva'] = array(); $varsTable['dto'] = array(); $varsTable['importe'] = array(); //now get the corresponding entries in the items table to create the invoice $secondQuery = "SELECT item, cantidad, precio, iva, dto FROM items WHERE cliente = ?"; $document2 = $mysqli->prepare($secondQuery); $document2->bind_param('i', $id); $result = $document2->execute(); $document2->store_result(); $document2->bind_result($item, $cantidad, $precio, $iva, $dto); while ($document2->fetch()){ $varsTable['item'][] = $item; $varsTable['cant'][] = $cantidad; $varsTable['precio'][] = round($precio,2); $varsTable['iva'][] = $iva; $varsTable['dto'][] = $dto; } $document2->close(); } $document->close(); //we can not print directly the values extracted from the database because we need to compute the "IVA" (VAT) //and other variables $inet = 0; $iiva = 0; $idto = 0; $total = 0; foreach ($varsTable['item'] as $index => $value) { $inet += round($varsTable['cant'][$index] * $varsTable['precio'][$index],2); $iiva += round(($varsTable['cant'][$index] * $varsTable['precio'][$index])*( $varsTable['iva'][$index]/100),2); $idto += round(($varsTable['cant'][$index] * $varsTable['precio'][$index])*( $varsTable['dto'][$index]/100),2); $imp = round(($varsTable['cant'][$index] * $varsTable['precio'][$index])* ( 1 + $varsTable['iva'][$index]/100) * ( 1 - $varsTable['dto'][$index]/100),2); $total += $imp; $varsTable['importe'][$index] = str_replacement($imp); $varsTable['precio'][$index] = str_replacement($varsTable['precio'][$index]); } $vars['inet'] = array('value' => str_replacement($inet)); $vars['iiva'] = array('value' => str_replacement($iiva)); $vars['total'] = array('value' => str_replacement($total)); $tiva = round(($iiva/$inet) * 100,2); $tdto = round(($idto/$inet) * 100,2); $vars['tiva'] = array('value' => str_replacement($tiva)); $vars['tdto'] = array('value' => str_replacement($tdto)); $vars['forma_pago'] = array('value' => 'Transferencia bancaria'); $vars['observaciones'] = array('value' => 'No hay nada que destacar.'); $vars['datos_adicionales'] = array('value' => 'Factura generada con <strong style="color: #b70000">Docxpresso</strong>.'); //call the Docxpresso API: the path is stored in the conn.php script that was previously loaded require_once($APIPath); $doc = new Docxpresso\createDocument(array('template' => 'sage.odt')); //state fromat and path to the final documents if ($_GET['format'] == 'doc') { $path = 'factura_' . $nombre . '_' . $apellidos . '.doc'; } else if ($_GET['format'] == 'pdf') { $path = 'factura_' . $nombre . '_' . $apellidos . '.pdf'; } else { $path = 'factura_' . $nombre . '_' . $apellidos . '.odt'; } //insert the global variables $doc->replace($vars); //prepare an arry for the insertion of the "products" $vars2 = array(); $vars2['item'] = array('value' => $varsTable['item']); $vars2['cant'] = array('value' => $varsTable['cant']); $vars2['precio'] = array('value' => $varsTable['precio']); $vars2['iva'] = array('value' => $varsTable['iva']); $vars2['dto'] = array('value' => $varsTable['dto']); $vars2['importe'] = array('value' => $varsTable['importe']); //insert them into the invoice $doc->replace($vars2, array('element' => 'table')); $doc->render($path); //download the document //like document generation is an asynchronous process we loop to make sure that it has been created $counter = 0; while( !file_exists($path) && $counter < 10) { sleep(1); $counter++; } if (file_exists($path) && $_GET['format'] == 'doc') { header("Content-Type: application/msword"); header("Content-Disposition: attachment; filename=" . $path); header("Content-Length: " . filesize($path)); readfile($path); } else if (file_exists($path) && $_GET['format'] == 'pdf') { header("Content-Type: application/pdf"); header("Content-Disposition: attachment; filename=" . $path); header("Content-Length: " . filesize($path)); readfile($path); } else if (file_exists($path) && $_GET['format'] == 'html') { $document = new Docxpresso\createDocument(array('template' => $path)); $html = $document->ODF2HTML5('previsualization.html', array('format' => 'single-file', 'loadJQuery' => false)); header("Content-Type: text/html"); echo $html; } else { //there were problems and the document was not generated $content = 'There was a time out generating the required document.'; echo $content; }
Como podéis comprobar la mayor parte del script se basa en las consultas a la base de datos y al "formateo de los datos" previos a la generación de los documentos finales con Docxpresso.