SAGE

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:

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.