-
Notifications
You must be signed in to change notification settings - Fork 24
formatos de impresion con Jinja
La idea de esta aplicación es evitar los problemas creados por los formularios estándar preimpresos, en los que uno está legalmente obligado a imprimir los datos de las facturas.
Los clientes querrán un formato específico de todos modos, por lo que algunos trabajos anteriores que hemos hecho son relevantes para este tema.
Jinja es un motor de plantillas. Permite a los usuarios dar formato a los datos con soporte completo de unicode, con Python, CSS y HTML.
En ERPNext, el usuario puede definir plantillas de impresión en HTML y CSS con plantillas Jinja para el renderizado del lado del servidor. El HTML permite al usuario controlar qué tipo de elementos aparecen en una página imprimible. El CSS ayuda a definir los estilos para un uso amplio en la página HTML que se va a renderizar. Este ejemplo/tutorial te permitirá fusionar todos los datos necesarios en la página aprovechando el poder de Jinja al permitir la inserción de variables previamente definidas, cuyos valores específicos son llamados al momento de renderizar el documento para servirlo o producirlo. La belleza de esto es que se puede mezclar fácilmente con un documento HTML (u otro).
En nuestro caso particular, tener Jinja disponible en un formato de impresión nos permite extraer datos del tipo de documento que se está imprimiendo, y más allá.
La sinopsis original es aquí Jinja depende de cuatro componentes principales. En general, usaremos los tres primeros.
- Declaraciones {% ... %}
- Expresiones {{ ... }}
- Comentarios {# ... #}
- Declaraciones de la línea # ... ##
Este documento explora los fundamentos de una Plantilla Jinja para una aplicación específica, y aquellos elementos que realmente nos ayudaron a hacerla funcionar. Fue sólo a través de varias iteraciones que finalmente logramos que funcionara. Espero que sea una buena base para su plantilla que pueda ayudarle a coger aquellos elementos que le servirán para su aplicación particular.
Uno de los aspectos más frustrantes a la hora de configurar su plantilla para su uso con ERPNext será la región y el idioma con los que trabaje. En nuestro caso, nuestro formato estaba destinado a la impresión en español, y por lo tanto era importante conseguir el idioma correcto. El problema específico que nos frustraba perniciosamente era la "cantidad en palabras", que es una función frappe que depende de la biblioteca num2words.
El encabezado de nuestro código de la plantilla está comentado así, para alertar a quien edite la plantilla sobre aspectos importantes de su modificación. Estamos alertando al usuario para que configure el lenguaje del sistema: es Encontramos que el uso del lenguaje regional requerido es_GT causaba un error en la cantidad de palabras, dado que este módulo no había sido añadido a la biblioteca python num2words. Desde entonces lo hemos modificado y presentado una solicitud de Pull, pero la solución rápida fue la de utilizar por defecto un idioma que pudiera imprimir la cantidad en palabras correctamente.
<!-- ## ADVERTENCIA: Para evitar errores de impresión: El lenguaje del sistema debe ser: es ## -->
<!-- ## Puede configurar esto en Configuración> Configuración del sistema > Idioma ## -->
<!-- ## Esto se configura en Configuracion> Ajustes del Sistema > Idioma ## -->
Lo siguiente será nuestra etiqueta HTML para el estilo. Lo que sea que esté en esta sección estará dentro de estas etiquetas, hasta que comencemos con la primera etiqueta <div>
.
<style></style>
Lo primero es el estilo que definirá el tamaño de la pantalla visible para el usuario desde dentro de ERPNext. Esta configuración también afecta al tamaño del documento que se configurará en la impresora, por lo que es muy importante que se establezca el ancho y la altura (si es necesario) del documento que se desea imprimir. Nota: Utilizo estas líneas de comentarios para hacer claramente evidente en el archivo Jinja lo que necesito editar. Una vez que termino de configurarlo, elimino los caracteres adicionales. También dejo comentarios con las cantidades medidas por encima de los ajustes reales para el CSS. Esto ayuda a recordar el tamaño medido en caso de que necesites ajustar en uno o dos milímetros los tamaños finales de la vista para obtener una impresión del documento de tamaño exacto.
EN:
/* ################################ 1.1 Size of the viewport ############################ */
@media screen {
.print-format {
/*PRINTABLE WIDTH: 21.59cm, 8.5in */
width: 21.59cm;
/*PRINTABLE HEIGHT: 27.94cm, 11.00in */
min-height: 27.94cm;
/*PADDING FROM MARGINS: 0.9cm, .354331in */
padding: 0.0cm;
}
}
ES:
/* ########################## 1.1 Tamaño del puerto de visualización ######################*/
@pantalla de medios {
.formato de impresión {
/*ANCHO PRINCIPAL: 21.59cm, 8.5in */
ancho: 21,59 cm;
/*ALTURA IMPRESIONABLE: 27.94cm, 11.00in */
Altura mínima: 27,94 cm;
/*Acoplamiento de los márgenes: 0.9cm, .354331in */
acolchado: 0,0 cm;
}
}
Esta sección la añadí para ahorrarme tiempo en el futuro cuando los clientes te digan algo como: "¿podemos mover todo a la derecha/izquierda o más abajo/arriba?"
Estos valores equivalen a la altura y el ancho medidos del documento.
EN:
/* Only modify the variable numbers and not the names */
/* Width and Height in Centimeters 21.59 o 27.94 */
{% set alto_doc_cm = 27.94 %} /* 27.94 */
{% set ancho_doc_cm = 21.59 %} /* 21.59 */
ES:
/* Sólo modifica los números de las variables y no los nombres */
/* Anchura y altura en centímetros 21.59 o 27.94 */
{% set alto_doc_cm = 27.94 %} /* 27.94 */
{% set ancho_doc_cm = 21.59 %} /* 21.59 */
Aquí es donde se ajusta la ubicación de cada elemento de la página. La posición original de cada elemento puede ser corregida alterando los datos en una línea.
EN:
/* Error adjustment to move everything by a specified quantity. */
{% set error_top_cm = 0 %} /* -1.5 A negative value here moves everything up */
{% set error_left_cm = 0 %} /* -1.88 A negative value here moves everything to the left */
ES:
/* Ajuste de error para mover todo en una cantidad determinada. */
{% set error_top_cm = 0 %} /* -1.5 Un valor negativo aquí mueve todo hacia arriba */
{% set error_left_cm = 0 %} /* -1.88 Un valor negativo aquí mueve todo a la izquierda */
Esto establece el margen de la página en centímetros.
EN:
/* Left, right, top and bottom margins in centimeters */
{% set margin_left_cm = 0.74 %}
{% set margin_right_cm = 0.74 %}
{% set margin_top_cm = 1.91 %}
{% set margin_bottom_cm = 1.91 %}
ES:
/* Márgenes izquierdo, derecho, superior e inferior en centímetros */
{% margen_de_ajuste_a_la_izquierda_cm = 0.74 %}
{% margen_de_ajuste_a_la_derecha_cm = 0.74 %}
{% margen_de_ajuste_superior_cm = 1.91 %}
{% margen_establecido_de_fondo_cm = 1.91 %}
Esto establece el ancho de la tabla que contendrá los artículos individuales listados en la factura de venta.
EN:
/* The width of the table is calculated automatically */
{% set item_table_width = ancho_doc_cm-(margin_left_cm+margin_right_cm) %}
ES:
/* El ancho de la tabla se calcula automáticamente */
{% del ancho de la mesa de juego = ancho doc cm-(margen izquierdo cm + margen derecho cm) %}
Estos estilos definen la ubicación de los principales puntos de inmersión en la página. Una es una inmersión de contenedor "mastre" o "general" que establece el inicio en las coordenadas 0,0 en un plano x, y.
EN:
div.general{
position: relative;
top: 0.0cm;
/*left: 0.0cm; */
}
ES:
div.general{
posición: relativa;
superior: 0.0cm;
/*izquierda: 0.0cm; */
}
Esta división específica gestiona el tamaño del documento, tomando valores de las variables declaradas anteriormente, y compilando el elemento CSS que lo define.
EN:
div.tamano_documento{
position: absolute;
height: {{alto_doc_cm}}cm;
width: {{ancho_doc_cm}}cm;
}
ES:
div.tamano_documento{
posición: absoluta;
altura: {{alto_doc_cm}}cm;
ancho: {{ancho_doc_cm}}cm;
}
Estos estilos definen el color del texto, el tamaño, la fuente, etc. de todo el documento.
EN:
.print-format div, .print-format p, .print-format span, .print-format tr, .print-format td {
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
line-height: 0%;
vertical-align: middle;
}
ES:
.formato de impresión div, .formato de impresión p, .formato de impresión span, .formato de impresión tr, .formato de impresión td {
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
altura de la línea: 0%;
alineación vertical: media;
}
En nuestro caso, nuestra serie de nombres contiene un punto "." en ERPNext, por lo que asignamos el valor a una variable que limpiamos con el método de reemplazo de Python. Nótese que aquí utilizamos la metodología de la Declaración Jinja como se muestra arriba usando {% %}
para encerrar la asignación de la variable pitón u otra declaración.
EN:
/* In this case we remove any "periods" in the series and assign it to the variable that we will print. */
{% set original_series = doc.naming_series %}
{% set clean_series = original_series.replace(".", "") %}
ES:
/* En este caso eliminamos cualquier "punto" de la serie y lo asignamos a la variable que vamos a imprimir. */
{% establecer serie original = doc.nombrar serie %}
{% establecer serie limpia = serie original.reemplazar (".", "") %}
Recopilamos datos específicos de otros tipos de documentos, como el nombre de su empresa, la dirección, los campos personalizados del tipo de documento y otros datos necesarios. Esto debe ajustarse a lo que usted requiere para su formato de impresión.
EN:
{% set company_fields = frappe.get_doc('Company',doc.company) %}
{% set address_fields = frappe.get_doc('Address','Sala de Ventas-Billing') %}
{% set config_series_fields = frappe.get_doc('Configuracion Series','1cc49e63f6') %} /* FIXME */
{% set config_facelec = frappe.get_doc('Configuracion Factura Electronica','CONFIG-FAC00001') %}
ES:
{% set company fields = frappe.get_doc('Compañía', doc.company) %}
{% establecer campos de dirección = frappe.get_doc('Dirección','Sala de Ventas-Facturación') %}
{% establecer campos de la serie de configuración = frappe.get_doc('Serie de configuración','1cc49e63f6') %} /* FIXME */
{% set config_facelec = frappe.get_doc('Configuracion Factura Electronica','CONFIG-FAC00001') %}
Establecí estas variables porque quería simplemente desplazarse hacia abajo 100 líneas en lugar de 400 líneas donde iba a usarlas. Este es un ejemplo de cómo puedes declarar variables en Jinja para ser usadas dentro de un documento. Claramente, es redundante porque puedes cambiar el HTML real en su lugar, pero esto me ayudó a cambiar los valores más rápido.
EN:
{% set firma_digital = 'FIRMA DIGITAL: ' %}
{% set dte_part1 = 'Documento Tributario Electrónico Según Resolución SAT:' %}
{% set dte_part2 = ' De fecha: ' %}
{% set dte_part3 = ' Serie: ' %}
{% set dte_part4 = ' Del ' %}
{% set dte_part5 = ' Al ' %}
{% set dte_part6 = ' GFACE: ' %}
{% set dte_part7 = ' NIT: ' %}
ES:
{% set firma_digital = 'DIGITAL SIGNATURE: ' %}
{% set dte_part1 = 'Electronic Tax Document According to SAT Resolution:' %}
{% set dte_part2 = ' From date: ' %}
{% set dte_part3 = ' Series: ' %}
{% set dte_part4 = ' From ' %}
{% set dte_part5 = ' Al ' %}
{% set dte_part6 = ' GFACE: ' %}
{% set dte_part7 = ' TIN: ' %}
Normalmente, la factura tendrá el logo de la compañía impreso en la cabecera. Aquí indico la posición en el documento. Tenga en cuenta que las imágenes se dibujarán a la derecha y abajo de este punto. También declaro el camino, el texto alternativo, el tamaño en píxeles y el estilo del texto alternativo, en caso de que deba ser impreso. De nuevo por comodidad al modificar el documento, así no tengo que desplazarme demasiado hacia abajo.
SUGERENCIA: Si un estilo CSS no se carga correctamente, trate de agregar el texto !importante!
justo después del parámetro que desea para asegurarse de que funciona.
EN:
/* Indicate the desired position of the logo in cm from the top and left of origin 0,0. */
/* The images are going to be drawn to the right and down from this point. */
{% set top_logo = 0.3 %}
{% set left_logo = 0.74 %}
/* Indicate the path to the image */
{% set logo_path_to_image = "/files/example-factura-gr.jpg" %}
/* Indicat the Alternate text for the image (in case the link is not loaded, this text will be displayed */
{% set logo_alt_text = "◎ Company logo alternate" %}
/* Size of the image in pixels*/
{% set width_logo = 195.19 %}
{% set height_logo = 69.09 %}
/* Fast adjustment of the image scaling, as a percentage value. From 0 and beyond. */
{% set logo_scaling = 100 %}
{% set scaled_width_logo = width_logo * (logo_scaling / 100) %}
{% set scaled_height_logo = height_logo * (logo_scaling / 100) %}
/* Declare the CSS style, taking the values for width and height */
img.logo{
width: {{ scaled_width_logo }}px;
height: {{ scaled_height_logo }}px;
/* Indicate the style of text, color, size, and others just for the Alternate text */
/* This ensures that in case the logo does not load, the alternate text, has the desired format. */
font-size: 10pt;
font-weight: 700;
color: #99989a !important;
}
ES:
/* Indique la posición deseada del logo en cm. de la parte superior y a la izquierda del origen 0,0. */
/* Las imágenes se dibujarán a la derecha y abajo de este punto. */
{% set top logo = 0.3 %}
{% poner el logo de la izquierda = 0.74 %}
/* Indique el camino a la imagen */
{% establecer la ruta del logo a la imagen = "/archivos/ejemplo-factura-gr.jpg" %}
/* Indica el texto alternativo de la imagen (en caso de que el enlace no se cargue, este texto se mostrará */
{% establecer el texto alternativo del logo = "◎ Logotipo alternativo de la compañía" %}
/* Tamaño de la imagen en píxeles*/
{% de ancho del logo = 195.19 %}
{% logo de altura fija = 69.09 %}
/* Ajuste rápido de la escala de la imagen, como un valor porcentual. Desde 0 y más allá. */
{% establecer escala de logo = 100 %}
{% establecer el logo de ancho escalado = logo de ancho * (escala de logo / 100) %}
{% establecer escala de logo de altura = logo de altura * (escala de logo / 100) %}
/* Declarar el estilo CSS, tomando los valores de ancho y alto */
img.logo{
ancho: {{ logo de ancho escalado }}px;
altura: {{ logo de altura a escala }}px;
/* Indique el estilo del texto, el color, el tamaño y otros sólo para el texto alternativo */
/* Esto asegura que en caso de que el logo no se cargue, el texto alternativo, tiene el formato deseado. */
tamaño de letra: 10pt;
peso de la fuente: 700;
color: #99989a !importante;
}
Tenga en cuenta el ¡Importante!
. para el color del texto alternativo en la declaración CSS de arriba. Esto ayuda en los casos en que el estilo no se cargue por alguna razón. (Y ahorra mucha frustración).
Ahora definimos los estilos para el Título en la factura. Como antes, declaramos la posición inicial, el estilo, y luego el bloque de corrección. El bloque de corrección estará presente después del estilo de cada elemento. Opté por tener el control sobre cada elemento individual desde el principio, y claramente pude manejar los estilos generales para simplificar, pero en esta ocasión, me gusta tener el control sobre cada elemento individual. De ahí la larga declaración de estilo.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_titulo = 1.0 %}
{% set left_titulo = 14.32 %}
/* Indicate the seller company Title style for text, color, size and other parameters */
p.doc-titulo{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_titulo = top_titulo + error_top_cm %}
{% set corrected_left_titulo = left_titulo + error_left_cm %}
span.doc-titulo{
position: absolute;
top: {{ corrected_top_titulo }}cm;
left: {{ corrected_left_titulo }}cm;
}
ES:
{% establece el titulo superior = 1.0 %}
{% establecer el titulo izquierdo = 14.32 %}
/* Indique la empresa vendedora Estilo del título para el texto, color, tamaño y otros parámetros */
p.doc-titulo{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected top titulo = top titulo + error top cm %}
{% set corrected left titulo = titulo izquierdo + error izquierdo cm %}
span.doc-titulo{
posición: absoluta;
superior:cm;
izquierda:cm;
}```
Este bloque es similar al estilo del título, pero para la etiqueta de la serie de nombres de facturas.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_serie = 2.00 %}
{% set left_serie = 14.32 %}
/* Indicate the seller company series label style for text, color, size and other parameters */
p.doc-serie{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_serie = top_serie + error_top_cm %}
{% set corrected_left_serie = left_serie + error_left_cm %}
span.doc-serie{
position: absolute;
top: {{ corrected_top_serie }}cm;
left: {{ corrected_left_serie }}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% establecer} serie superior = 2.00 %}
{% establecido} serie izquierda = 14.32 %}
/* Indique el estilo de la etiqueta de la serie de la compañía vendedora para el texto, el color, el tamaño y otros parámetros */
p.doc-serie{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% conjunto corregido serie superior = serie superior + error superior cm %}
{% conjunto corregido serie izquierda = serie izquierda + error izquierda cm %}
span.doc-serie{
posición: absoluta;
superior: cm;
izquierda: {{ corrected_left_serie }}cm;
}
Este bloque es similar al anterior, pero para la factura que nombra los números de serie.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_corr = 2.5 %}
{% set left_corr = 14.32 %}
/* Indicate the seller company numbering series style for text, color, size and other parameters */
p.doc-correlativo{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_corr = top_corr + error_top_cm %}
{% set corrected_left_corr = left_corr + error_left_cm %}
span.doc-correlativo{
position: absolute;
top: {{corrected_top_corr}}cm;
left: {{corrected_left_corr}}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% establecer corr. superior = 2.5 %}
{% establecer corr. izquierdo = 14.32 %}
/* Indique el estilo de la serie de numeración de la empresa vendedora para el texto, el color, el tamaño y otros parámetros */
p.doc-correlativo{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected top corr = top corr + error top cm %}
{% set corrected left corr = left_corr + error left cm %}
span.doc-correlativo{
posición: absoluta;
superior: {{corregido_top_corr}}cm;
izquierda: {{corrected_left_corr}}cm;
}
Para esta factura, la dirección se divide en tres líneas separadas. Así que hemos declarado el estilo de cada una. Teóricamente podrías declarar un solo estilo para el tamaño de la fuente de la dirección, el peso y el color. Sólo dejo esto para el control cuando diseñe la factura.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_company_address_1 = 2.90 %}
{% set left_company_address_1 = 0.8 %}
{% set top_company_address_2 = 3.25 %}
{% set left_company_address_2 = 0.8 %}
{% set top_company_address_3 = 3.65 %}
{% set left_company_address_3 = 0.8 %}
/* Indicate the seller company address style for text, color, size and other parameters */
p.address1{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 9pt;
font-weight: 200;
color: #99989a !important;
}
p.address2{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 9pt;
font-weight: 200;
color: #99989a !important;
}
p.address3{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 9pt;
font-weight: 200;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_company_address_1 = top_company_address_1 + error_top_cm %}
{% set corrected_left_company_address_1 = left_company_address_1 + error_left_cm %}
span.address1{
position: absolute;
top: {{ corrected_top_company_address_1 }}cm;
left: {{ corrected_left_company_address_1 }}cm;
}
{% set corrected_top_company_address_2 = top_company_address_2 + error_top_cm %}
{% set corrected_left_company_address_2 = left_company_address_2 + error_left_cm %}
span.address2{
position: absolute;
top: {{ corrected_top_company_address_2 }}cm;
left: {{ corrected_left_company_address_2 }}cm;
}
{% set corrected_top_company_address_3 = top_company_address_3 + error_top_cm %}
{% set corrected_left_company_address_3 = left_company_address_3 + error_left_cm %}
span.address3{
position: absolute;
top: {{ corrected_top_company_address_3 }}cm;
left: {{ corrected_left_company_address_3 }}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% establecer dirección_de_compañía_superior_1 = 2.90 %}
{% establecer dirección_de_compañía_izquierda_1 = 0.8 %}
{% establecer dirección_de_compañía_2 = 3.25 %}
{% establecer dirección_de_compañía_de_la_izquierda_2 = 0.8 %}
{% establecer dirección_de_compañía_superior_3 = 3.65 %}
{% establecer dirección_de_compañía_de_izquierda_3 = 0.8 %}
/* Indique el estilo de la dirección de la empresa vendedora para el texto, el color, el tamaño y otros parámetros */
p.dirección1{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 9pt;
peso de la fuente: 200;
color: #99989a !importante;
}
p.dirección2{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 9pt;
peso de la fuente: 200;
color: #99989a !importante;
}
p.dirección3{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 9pt;
peso de la fuente: 200;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_company_address_1 = top_company_address_1 + error_top_cm %}
{% set corrected_left_company_address_1 = left_company_address_1 + error_left_cm %}
span.address1{
posición: absoluta;
superior: {{ corrected_top_company_address_1 }}cm;
a la izquierda: {{ corrected_left_company_address_1 }}cm;
}
{% set corrected_top_company_address_2 = top_company_address_2 + error_top_cm %}
{% set corrected_left_company_address_2 = left_company_address_2 + error_left_cm %}
span.address2{
posición: absoluta;
superior: {{ corrected_top_company_address_2 }}cm;
a la izquierda: {{ corrected_left_company_address_2 }}cm;
}
{% set corrected_top_company_address_3 = top_company_address_3 + error_top_cm %}
{% set corrected_left_company_address_3 = left_company_address_3 + error_left_cm %}
span.address3{
posición: absoluta;
superior: {{ corrected_top_company_address_3 }}cm;
a la izquierda: {{ corrected_left_company_address_3 }}cm;
}
El mismo patrón, ahora para el correo electrónico de la empresa vendedora (su empresa))
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_company_email = 4.25 %}
{% set left_company_email = 0.8 %}
/* Indicate the seller company e-mail style for text, color, size and other parameters */
p.company_email{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 9pt;
font-weight: 600;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_company_email = top_company_email + error_top_cm %}
{% set corrected_left_company_email = left_company_email + error_left_cm %}
span.company_email{
position: absolute;
top: {{ corrected_top_company_email }}cm;
left: {{ corrected_left_company_email }}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_company_email = 4.25 %}
{% set left_company_email = 0.8 %}
/* Indique el estilo de correo electrónico de la empresa vendedora para el texto, el color, el tamaño y otros parámetros */
p.company_email{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 9pt;
peso de la fuente: 600;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_company_email = top_company_email + error_top_cm %}
{% set corrected_left_company_email = left_company_email + error_left_cm %}
span.company_email{
posición: absoluta;
superior: {{corregido_top_company_email }}cm;
a la izquierda: {{ corrected_left_company_email }}cm;
}
El nombre de la empresa vendedora tiene que estar impreso en la factura, así que aquí definimos el estilo para eso.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_doc_nombre_empresa = 4.97 %}
{% set left_doc_nombre_empresa = 0.8 %}
/* Indicate the seller company name style for text, color, size and other parameters */
p.doc-nombre-empresa{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 200;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_doc_nombre_empresa = top_doc_nombre_empresa + error_top_cm %}
{% set corrected_left_doc_nombre_empresa = left_doc_nombre_empresa + error_left_cm %}
span.doc-nombre-empresa{
position: absolute;
top: {{corrected_top_doc_nombre_empresa}}cm;
left: {{corrected_left_doc_nombre_empresa}}cm;
width: {{ corr_item_table_width }}cm !important;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_doc_nombre_empresa = 4.97 %}
{% set left_doc_nombre_empresa = 0.8 %}
/* Indique el estilo del nombre de la empresa vendedora para el texto, el color, el tamaño y otros parámetros */
p.doc-nombre-empresa{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 200;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_doc_nombre_empresa = top_doc_nombre_empresa + error_top_cm %}
{% set corrected_left_doc_nombre_empresa = left_doc_nombre_empresa + error_left_cm %}
span.doc-nombre-empresa{
posición: absoluta;
superior: {{corregido_top_doc_nombre_empresa}}cm;
izquierda: {{corrected_left_doc_nombre_empresa}}cm;
ancho: {{ corr_item_table_width }}cm !importante;
}
El número de identificación del contribuyente de la empresa vendedora tiene que estar impreso en la factura, así que aquí definimos el estilo para eso.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_doc_tax_id_empresa = 5.37 %}
{% set left_doc_tax_id_empresa = 0.8 %}
/* Indicate the seller company tax id style for text, color, size and other parameters */
p.doc-tax-id-empresa{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 200;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_doc_tax_id_empresa = top_doc_tax_id_empresa + error_top_cm %}
{% set corrected_left_doc_tax_id_empresa = left_doc_tax_id_empresa + error_left_cm %}
span.doc-tax-id-empresa{
position: absolute;
top: {{corrected_top_doc_tax_id_empresa}}cm;
left: {{corrected_left_doc_tax_id_empresa}}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_doc_tax_id_empresa = 5.37 %}
{% set left_doc_tax_id_empresa = 0.8 %}
/* Indique el estilo de identificación fiscal de la empresa vendedora para el texto, el color, el tamaño y otros parámetros */
p.doc-tax-id-empresa{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 200;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_doc_tax_id_empresa = top_doc_tax_id_empresa + error_top_cm %}
{% set corrected_left_doc_tax_id_empresa = left_doc_tax_id_empresa + error_left_cm %}
span.doc-tax-id-empresa{
posición: absoluta;
superior: {{corregido_top_doc_tax_id_empresa}}cm;
izquierda: {{corrected_left_doc_tax_id_empresa}}cm;
}
Se requiere la fecha de emisión de la factura, y los estilos se declaran aquí.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_fecha_doc = 6.50 %}
{% set left_fecha_doc = 0.8 %}
/* Indicate the issue date style for text, color, size and other parameters */
p.fecha-documento{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 11pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_fecha_doc = top_fecha_doc + error_top_cm %}
{% set corrected_left_fecha_doc = left_fecha_doc + error_left_cm %}
span.fecha-documento{
position: absolute;
top: {{corrected_top_fecha_doc}}cm;
left: {{corrected_left_fecha_doc}}cm;
width: {{ corr_item_table_width }}cm !important;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_fecha_doc = 6.50 %}
{% set left_fecha_doc = 0.8 %}
/* Indique el estilo de la fecha de emisión para el texto, el color, el tamaño y otros parámetros */
p.fecha-documento{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 11pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_fecha_doc = top_fecha_doc + error_top_cm %}
{% set corrected_left_fecha_doc = left_fecha_doc + error_left_cm %}
span.fecha-documento{
posición: absoluta;
superior: {{corregido_top_fecha_doc}}cm;
izquierda: {{corrected_left_fecha_doc}}cm;
ancho: {{ corr_item_table_width }}cm !importante;
}
Se requiere el nombre del cliente de ERPNext para la factura, y los estilos se declaran aquí.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_direccion = 8.36 %}
{% set left_direccion = 0.8 %}
/* Indicate the customer address style for text, color, size and other parameters */
p.direccion{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 11pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_direccion = top_direccion + error_top_cm %}
{% set corrected_left_direccion = left_direccion + error_left_cm %}
span.direccion{
position: absolute;
top: {{corrected_top_direccion}}cm;
left: {{corrected_left_direccion}}cm;
width: {{ corr_item_table_width }}cm !important;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_direccion = 8.36 %}
{% set left_direccion = 0.8 %}
/* Indique el estilo de la dirección del cliente para el texto, el color, el tamaño y otros parámetros */
p.direccion{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 11pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_direccion = top_direccion + error_top_cm %}
{% set corrected_left_direccion = left_direccion + error_left_cm %}
span.direccion{
posición: absoluta;
superior: {{corregido_top_direccion}}cm;
izquierda: {{corrected_left_direccion}}cm;
ancho: {{ corr_item_table_width }}cm !importante;
}
La identificación de cliente y contribuyente de ERPNext está legalmente obligada a aparecer en la factura, por lo que los estilos se declaran aquí.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_nit = 9.16 %}
{% set left_nit = 0.8 %}
/* Indicate the customer address style for text, color, size and other parameters */
p.nit{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_nit = top_nit + error_top_cm %}
{% set corrected_left_nit = left_nit + error_left_cm %}
span.nit{
position: absolute;
top: {{corrected_top_nit}}cm;
left: {{corrected_left_nit}}cm;
width: {{ corr_item_table_width }}cm !important;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_nit = 9.16 %}
{% set left_nit = 0.8 %}
/* Indique el estilo de la dirección del cliente para el texto, el color, el tamaño y otros parámetros */
p.nit{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_nit = top_nit + error_top_cm %}
{% set corrected_left_nit = left_nit + error_left_cm %}
span.nit{
posición: absoluta;
superior: {{{corregido_top_nit}}cm;
izquierda: {{corrected_left_nit}}cm;
ancho: {{ corr_item_table_width }}cm !importante;
}
OK. En este punto hemos definido lo que me gusta llamar el "Bloque Superior" de la factura, que contiene elementos que definen la factura. A continuación, declararemos los estilos de los elementos. He encontrado que usar una tabla HTML es más efectivo para esto. Pero hay que tener cuidado de definir correctamente los estilos de los elementos de la tabla para que se muestren con precisión.
Hay una forma particular de declarar los encabezados de una tabla, y aún así debo implementar la declaración HTML en consecuencia.
EN:
/* Indicate the starting position of the table in cm from top and left */
{% set top_items = 9.85 %}
{% set left_items = 0.8 %}
/* Indicate the height of the header for the table in pixels */
{% set height_encabezado = 22 %}
/* Indicate the vertical alignment of the text in the header of the table in pixels */ FIXME!
/* vertical-align: Working OK the use of a string of text alignment */
{% set vert_align_encabezado = "middle" %}
/* Indicate where you align the quantity horizontally within the cell in the item table */
/* "left", "right", "center", "justify", "char" */
{% set cantidad_align = "left" %}
/* Indicate where you align the description horizontally within the cell in the item table */
/* "left", "right", "center", "justify", "char" */
{% set descripcion_align = "left" %}
/* Indicate where you align the unit price horizontally within the cell in the item table */
/* "left", "right", "center", "justify", "char" */
{% set precio_unitario_align = "left" %}
/* Indicate where you align the subtotal amount horizontally within the cell in the item table */
/* "left", "right", "center", "justify", "char" */
{% set valor_subtotal_align = "left" %}
ES:
/* Indique la posición inicial de la mesa en cm. de la parte superior e izquierda */
{% set top_items = 9.85 %}
{% set left_items = 0.8 %}
/* Indique la altura del encabezado de la tabla en píxeles */
{% set height_encabezado = 22 %}
/* Indique la alineación vertical del texto en el encabezado de la tabla en píxeles */ ¡CORRECTO!
/* Alineación vertical: Funciona bien el uso de una cadena de alineación de texto */
{% set vert_align_encabezado = "middle" %}
/* Indique dónde alinea la cantidad horizontalmente dentro de la celda en la tabla de artículos */
/* "izquierda", "derecha", "centro", "justificar", "char" */
{% set cantidad_align = "left" %}
/* Indique dónde se alinea la descripción horizontalmente dentro de la celda en la tabla de elementos */
/* "izquierda", "derecha", "centro", "justificar", "char" */
{% set descripcion_align = "left" %}
/* Indique dónde alinea el precio unitario horizontalmente dentro de la celda en la tabla de artículos */
/* "izquierda", "derecha", "centro", "justificar", "char" */
{% set precio_unitario_align = "izquierda" %}
/* Indique dónde alinea la cantidad de subtotal horizontalmente dentro de la celda en la tabla de elementos */
/* "izquierda", "derecha", "centro", "justificar", "char" */
{% set valor_subtotal_align = "left" %}
Este es el lugar donde pasarás la mayor parte del tiempo arreglando los artículos. Serán necesarias varias impresiones de prueba y errores para ajustar. Sin embargo, comenzar con una declaración de variables sólidas e imprimir en una imagen escaneada de su formato le ayudará a alinear y salir más rápidamente de la fase de configuración del formato de impresión.
En este caso particular, me pareció eficaz declarar dos tablas diferentes, una para los elementos y otra para el pie de página, porque era más fácil alinear los elementos de abajo sin afectar a la forma en que se mostrarán los elementos de arriba.
span.items{
position: absolute;
top: {{ corrected_top_items }}cm;
left: {{ corrected_left_items }}cm;
width:
}
table.items-example{
width: {{ corr_item_table_width }}cm;
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
/*height: 1cm;*/
height: 16.00cm;
border-spacing: 0;
padding: 1px;
border-collapse: collapse;
border: 1px;
text-color: #99989a !important;
font-size: 10pt;
font-weight: 700;
font-color: #99989a !important;
border-style: solid;
border-color: #99989a !important;
}
table.items-example-footer{
width: {{ corr_item_table_width }}cm;
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
/*height: 1cm;*/
height: 1.0cm;
border-spacing: 0;
padding: 1px;
border-collapse: collapse;
border: 1px;
text-color: #99989a !important;
font-size: 10pt;
font-weight: 700;
font-color: #99989a !important;
border-style: solid;
border-color: #99989a !important;
}
Fíjate en cómo tenemos que usar en varias declaraciones CSS el modificador Importante
para asegurarnos de que estos elementos se muestran correctamente.
Ahora que tenemos las variables y los estilos de tabla, podemos especificar los valores individuales para elementos específicos de la tabla. Lo más importante aquí es la declaración de los anchos, utilizando un valor porcentual. Este será un cambio de valor de prueba y error, pero hemos encontrado que son eficaces.
td.cantidad-header {
width: 8%
padding: 9px;
background-color: #99989a !important;
height: {{ height_encabezado }}px;
text-align: {{ valor_cantidad_align }};
}
p.cantidad-header{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #FFFFFF !important;
}
td.cantidad{
width: 8%
padding: 0px;
/*align: left|right|center|justify|char */
text-align: {{ cantidad_align }};
}
p.cantidad{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 600;
color: #99989a !important;
}
td.descripcion-header{
width: 56%
padding: 9px;
background-color: #99989a !important;
height: {{ height_encabezado }}px;
text-align: {{ valor_descripcion_align }};
}
p.descripcion-header{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 600;
color: #FFFFFF !important;
}
td.descripcion{
width: 56%
padding:0px;
/*align: left|right|center|justify|char */
text-align: {{ descripcion_align }};
}
p.descripcion{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 600;
color: #99989a !important;
}
td.precio_unitario-header {
width: 17%
padding: 9px;
background-color: #99989a !important;
height: {{ height_encabezado }}px;
text-align: {{ valor_precio_unitario_align }};
}
p.precio_unitario-header{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #FFFFFF !important;
}
td.precio_unitario{
width: 17%
padding:0px;
text-align: {{ precio_unitario_align }};
}
p.precio_unitario{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 600;
color: #99989a !important;
}
td.valor_subtotal-header {
width: 17%
padding: 9px;
background-color: #99989a !important;
height: {{ height_encabezado }}px;
text-align: {{ valor_subtotal_align }};
}
p.valor_subtotal-header {
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #FFFFFF !important;
}
td.valor_subtotal {
width: 17%
padding:0px;
text-align: {{ valor_subtotal_align }};
}
p.valor_subtotal {
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 600;
color: #99989a !important;
}
td.total-en-letras {
width: 71%
padding:0px;
text-align: {{ valor_subtotal_align }};
}
td.total {
width: 29%
padding:0px;
text-align: {{ valor_subtotal_align }};
}
Finalmente declaramos los estilos de todos los elementos de pie de página de la tabla, como Total en palabras, Total, Firma digital y Elementos requeridos fiscalmente. El aspecto importante aquí, particularmente en el contexto de la aplicación para la cual este archivo está alojado, es la firma digital de la factura electrónica aka CAE.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_total = 26.30 %}
{% set left_total = 15.28 %}
/* Indicate the item total style for text, color, size and other parameters */
p.total{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 10pt;
font-weight: 700;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_total = top_total + error_top_cm %}
{% set corrected_left_total = left_total + error_left_cm %}
span.total{
position: absolute;
top: {{corrected_top_total}}cm;
left: {{corrected_left_total}}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_total = 26.30 %}
{% set left_total = 15.28 %}
/* Indique el estilo total del artículo para el texto, el color, el tamaño y otros parámetros */
p.total{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 10pt;
peso de la fuente: 700;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_total = top_total + error_top_cm %}
{% set corrected_left_total = left_total + error_left_cm %}
span.total{
posición: absoluta;
superior: {{corregido_top_total}}cm;
izquierda: {{corrected_left_total}}cm;
}
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_cae = 27.00 %}
{% set left_cae = 0.8 %}
/* Indicate the CAE style for text, color, size and other parameters */
p.cae{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 9pt;
font-weight: 400;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_cae = top_cae + error_top_cm %}
{% set corrected_left_cae = left_cae + error_left_cm %}
span.cae{
position: absolute;
top: {{corrected_top_cae}}cm;
left: {{corrected_left_cae}}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_cae = 27.00 %}
{% set left_cae = 0.8 %}
/* Indique el estilo CAE para el texto, el color, el tamaño y otros parámetros */
p.cae{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 9pt;
peso de la fuente: 400;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_cae = top_cae + error_top_cm %}
{% set corrected_left_cae = left_cae + error_left_cm %}
span.cae{
posición: absoluta;
superior: {{corregido_top_cae}}cm;
izquierda: {{corrected_left_cae}}cm;
}
La entidad fiscal requiere la impresión de la resolución o autorización oficial para hacer facturas. Esto ayuda a su proceso de auditoría, y por lo tanto debe ser impreso en las facturas. Si esto no se tiene en cuenta, pueden producirse sanciones.
EN:
/* Indicate the position in cm from the top and left of origin: 0,0 */
{% set top_resolucion = 27.30 %}
{% set left_resolucion = 0.8 %}
/* Indicate the Resolution style for text, color, size and other parameters */
p.resolucion{
font-family: avenir, 'avenir next', helvetica, arial, sans-serif;
font-size: 8pt;
font-weight: 200;
color: #99989a !important;
}
/* DO NOT MODIFY THIS */
{% set corrected_top_resolucion = top_resolucion + error_top_cm %}
{% set corrected_left_resolucion = left_resolucion + error_left_cm %}
span.resolucion{
position: absolute;
top: {{ corrected_top_resolucion }}cm;
left: {{ corrected_left_resolucion }}cm;
}
ES:
/* Indique la posición en cm. de la parte superior y la izquierda del origen: 0,0 */
{% set top_resolución = 27.30 %}
{% set left_resolucion = 0.8 %}
/* Indique el estilo de resolución para el texto, el color, el tamaño y otros parámetros */
p.resolución{
familia de fuentes: avenir, 'avenir next', helvetica, arial, sans-serif;
tamaño de la fuente: 8pt;
peso de la fuente: 200;
color: #99989a !importante;
}
/* NO MODIFIQUEN ESTO.
{% set corrected_top_resolucion = top_resolucion + error_top_cm %}
{% set corrected_left_resolucion = left_resolucion + error_left_cm %}
span.resolucion{
posición: absoluta;
superior: {{ corregido_top_resolución }}cm;
izquierda: {{ corrected_left_resolucion }}cm;
}
Con esto, concluimos las declaraciones de estilo, y para este ejemplo son unas 705 líneas. Se podría definir esto en un archivo separado, y enlazarlo a este HTML, un truco sería colocar el archivo CSS a enlazar dentro de la carpeta privada en ERPNext, y enlazarlo. Opté por definirlo aquí porque no me gusta depender mucho de otros archivos, ya que el archivo CSS podría ser borrado accidentalmente por otro usuario con privilegios similares.
</style>
Ahora finalmente comenzamos con la declaración HTML para llenar el documento con los elementos reales dibujándolos en el puerto de vista Esto crea la factura de venta real!
Este elemento es el contenedor vacío dentro del cual colocaremos todos los demás elementos. Este es el elemento de alineación inicial. Todo se define en términos de este elemento div y alinea todo al punto de origen 0,0
EN:
<!-- 2. HTML Begins -->
<!-- 2.1 First div, container begins -->
<div class="general">
<!-- 2.1 First div, container ends -->
ES:
<!-- 2. El HTML comienza -->
<!-- 2.1 Primera inmersión, comienza el contenedor -->
<div class="general">
<!-- 2.1 Primera inmersión, extremos del contenedor -->
Ahora definimos el tamaño del documento, tomando todas las variables de tamaño declaradas en la clase CSS nombrada arriba.
EN:
<!-- 2.2 Document Size Begins -->
<!-- This div defines the size of the entire document. This "expands" the document to fill the viewport size also declared above.-->
<div class="tamano_documento">
<!-- 2.2 Document Size Ends -->
ES:
<!-- 2.2 El tamaño del documento comienza -->
<Esta división define el tamaño de todo el documento. Esto "expande" el documento para llenar el tamaño de la vista también declarado anteriormente.
<div class="tamano_documento">
<!-- 2.2 El tamaño del documento termina -->
Colocamos el logo. En lugar de un "div" usamos un "elemento de espacio". Aquí utilizamos variables específicas al imprimirlas con Jinja usando la metodología de expresión con {{ y }}
EN:
<!-- 2.3 Logo Begins -->
span class="doc-logo">
<img class="logo" src="{{ logo_path_to_image }}" alt="{{logo_alt_text }}">
</span>
<!-- 2.3 Logo Ends -->
ES:
<!-- 2.3 El logo comienza -->
span class="doc-logo">
<img class="logo" src="{{ logo_path_to_image }}" alt="{{logo_alt_text }}">
</span>
<!-- 2.3 Logotipo termina -->
Colocamos el título. De nuevo, con una declaración Jinja {{{{ }}
. Fíjese que estamos encerrando un método Frappe dentro de la declaración, con el objetivo de que sea traducido.
EN:
<!-- 2.4 Title Begins -->
<span class="doc-titulo">
<p class="doc-titulo">{{ _("FACTURA") }}</p>
</span>
<!-- 2.4 Title Ends -->
ES:
<!-- 2.4 El título comienza -->
<span class="doc-titulo">
<p class="doc-titulo">{{{ _("FACTURA") }}</p>
</span>
<!-- 2.4 El título termina -->
Colocamos el título de la serie de numeración. De nuevo, con una declaración Jinja {{}}
. Usamos la variable clean_series después de eliminar el punto con una declaración Jinja {% %}
.
EN:
<!-- 2.5 Naming Series Begins -->
<span class="doc-serie">
<p class="doc-serie">{{ _("SERIE") }}: {{ clean_series }}
</p>
</span>
<!-- 2.5 Naming Series Ends -->
ES:
<!-- 2.5 La serie de nombres comienza -->
<span class="doc-serie">
<p class="doc-serie">{{{ _("SERIE") }}: {{ clean_series }}
</p>
</span>
<2.5 La serie de nombres termina...
Colocamos la serie de numeración real. De nuevo, con una declaración Jinja precedida de un texto traducible (para el futuro) {{{}}
.
EN:
<!-- 2.6 Title Begins -->
<span class="doc-correlativo">
<p class="doc-correlativo">{{ _("No.") }}: {{ doc.name }}</p>
</span>
<!-- 2.6 Title Ends -->
ES:
<!-- 2.6 El título comienza -->
<span class="doc-correlativo">
<p class="doc-correlativo">{{{ _("No.") }}: {{ doc.name }}</p>
</span>
<!-- 2.6 El título termina -->
Colocamos las tres líneas para la dirección. Los estilos ayudan a cambiar cada estilo de línea, pero los datos que se incluyen son diferentes y modificables. En este caso usamos una línea para la dirección real, otra para los datos de la ubicación secundaria, país (cambiándolo todo a mayúsculas con el método upper()
de Python, seguido de la ciudad y el código postal. Luego la última línea contiene el número de teléfono, que no es requerido sino que es solicitado por algunos clientes.
EN:
<!-- 2.7 Invoicing company address Begins -->
<span class="address1">
<p class="address1">{{ address_fields.address_line1 }}, </p>
</span>
<span class="address2">
<p class="address2">{{ address_fields.address_line2 }} {{ address_fields.country.upper() }}, {{ address_fields.city }}, C.A., {{ address_fields.pincode}}</p>
</span>
<span class="address3">
<p class="address3">TELÉFONO: {{ address_fields.phone }}</p>
</span>
<!-- 2.7 Invoicing company address Ends -->
ES:
<2.7 La dirección de la compañía de facturación comienza...
<span class="address1">
<p class="address1">{{{ address_fields.address_line1 }}, </p>
</span>
<span class="address2">
<p class="address2">{{{ address_fields.address_line2 }} {{ address_fields.country.upper() }}, {{ address_fields.city }}, C.A., {{ address_fields.pincode}}</p>
</span>
<span class="address3">
<p class="address3">TELÉFONO: {{ address_fields.phone }}</p>
</span>
<!-- 2.7 Dirección de la empresa de facturación Finales -->
Ahora colocamos la dirección de correo electrónico, como pidió el cliente para este ejemplo.
EN:
<!-- 2.8 Invoicing company email Begins -->
<span class="company_email">
<p class="company_email">{{ address_fields.email_id}} /</p>
</span>
<!-- 2.8 Invoicing company email Ends -->
ES:
<2.8 La facturación del correo electrónico de la compañía comienza...
<span class="company_email">
<p class="company_email">{{{ address_fields.email_id}} /</p>
</span>
<!-- 2.8 Facturación del correo electrónico de la compañía Termina -->
Otro requisito de nuestro cliente es tener el sitio web listado.
EN:
<!-- 2.9 Invoicing company website Begins -->
<span class="company_website">
<p class="company_website">{{ company_fields.website}}</p>
</span>
<!-- 2.9 Invoicing company website Ends -->
ES:
<!-- 2.9 La página web de la compañía de facturación comienza -->
<span class="company_website">
<p class="company_website">{{{ company_fields.website}}</p>
</span>
<2.9 La página web de la compañía de facturación termina...
El nombre
EN:
<!-- 3.0 Invoicing company name Begins -->
<span class="doc-nombre-empresa">
<p class="doc-nombre-empresa">{{ doc.company }}</p>
</span>
<!-- 3.0 Invoicing company name Ends -->
ES:
<!-- 3.0 El nombre de la compañía de facturación comienza -->
<span class="doc-nombre-empresa">
<p class="doc-nombre-empresa">{{{ doc.company }}</p>
</span>
<!-- 3.0 Nombre de la compañía de facturación Termina -->
Un requisito para las facturas emitidas en Guatemala, la empresa emisora debe imprimir su identificación fiscal.
EN:
<!-- 3.1 Invoicing company Tax ID Begins -->
<span class="doc-tax-id-empresa">
<p class="doc-tax-id-empresa">NIT: {{ company_fields.nit_face_company}}</p>
</span>
<!-- 3.1 Invoicing company Tax ID Ends -->
ES:
<!-- 3.1 Facturación de la empresa Empieza la identificación fiscal -->
<span class="doc-tax-id-empresa">
<p class="doc-tax-id-empresa">NIT: {{ company_fields.nit_face_company}}</p>
</span>
<!-- 3.1 Facturación de la empresa Fin de la identificación fiscal -->
Usé esta referencia: Python Formateando una fecha en Jinja, sin embargo frappe tiene su propio método de formateo de fechas que usamos aquí.
EN:
<!-- 3.1 Posting Date Begins -->
<span class="fecha-documento">
<p class="fecha-documento">LUGAR Y FECHA: {{ address_fields.city }}, {{ doc.get_formatted("posting_date") }}</p>
</span>
<!-- 3.1 Posting Date Ends -->
ES:
<!-- 3.1 La fecha de publicación comienza -->
<span class="fecha-documento">
<p class="fecha-documento">LUGAR Y FECHA: {{ address_fields.city }}, {{ doc.get_formatted("posting_date") }}</p>
</span>
<!-- 3.1 La fecha de publicación termina -->
Aquí imprimimos el nombre del cliente, la dirección de facturación y la identificación del contribuyente. Es importante que configure la dirección del cliente con el cheque de "es la dirección de facturación", de lo contrario no aparecerá aquí.
EN:
<!-- 3.2 Customer name Begins -->
<span class="razon-social">
<p class="razon-social">NOMBRE: {{ doc.customer_name }}</p>
</span>
<!-- 3.2 Customer name Ends -->
<!-- 3.3 Customer address Begins -->
<span class="direccion">
<p class="direccion">DIRECCIÓN: {{ doc.billing_address }}</p>
</span>
<!-- 3.3 Customer address Ends -->
<!-- 3.4 Customer Taxpayer ID Begins -->
<span class="nit">
<p class="nit">NIT: {{ frappe.db.get_value("Customer", doc.customer, "tax_id") }}</p>
</span>
<!-- 3.4 Customer Taxpayer ID Ends -->
ES:
<!-- 3.2 El nombre del cliente comienza -->
<span class="razon-social">
<p class="razon-social">NOMBRE: {{ doc.nombre_del_cliente }}</p>
</span>
<!-- 3.2 El nombre del cliente termina -->
<!-- 3.3 La dirección del cliente comienza -->
<span class="direccion">
<p class="direccion">DIRECCION: {{ doc.billing_address }}</p>
</span>
<!-- 3.3 La dirección del cliente termina -->
<!-- 3.4 Comienza la identificación de cliente-contribuyente -->
<span class="nit">
<p class="nit">NIT: {{ frappe.db.get_value("Cliente", doc.cliente, "tax_id") }}</p>
</span>
<!-- 3.4 La identificación del cliente y el contribuyente termina -->
Aquí es donde la mayor parte de la magia ocurre con Jinja, y donde su poder te permite imprimir líneas condicionalmente usando un bucle de for.
EN:
<!-- 3.5 Item Table Begins -->
<span class="articulos">
<table class="items-example">
<tbody>
<tr>
<td class="cantidad-header"><p class="descripcion-header">CANTIDAD</p></td>
<td class="descripcion-header"><p class="descripcion-header">DESCRIPCIÓN</p></td>
<td class="precio_unitario-header"><p class="precio_unitario-header">PRECIO UNITARIO</p></td>
<td class="valor_subtotal-header"><p class="valor_subtotal-header">VALOR</p></td>
</tr>
{%- for item in doc.items -%}
<tr>
<td class="cantidad"><p class="cantidad">{{ item.qty }}</p></td>
<td class="descripcion">
<p class="descripcion">
{%- if item.item_name != item.item_code -%}
{{ item.item_name }}{%- endif -%}
</p>
</td>
<td class="precio_unitario"><p class="precio_unitario">{{ item.get_formatted("rate") }}</p></td>
<td class="valor_subtotal"><p class="valor_subtotal">{{ item.get_formatted("amount") }}</p></td>
</tr>
{%- endfor -%}
</tbody>
</table>
<table class="items-example-footer">
<tbody>
<tr><td></td></tr>
</tbody>
</table>
</span>
<!-- 3.5 Item Table Ends -->
ES:
<!-- 3.5 La tabla de artículos comienza -->
<span class="articulos">
<table class="items-example">
<cuerpo>
<tr>
<td class="cantidad-header"><p class="descripcion-header">CANTIDAD</p></td>
<td class="descripcion-header"><p class="descripcion-header">DESCRIPCIÓN</p></td>
<td class="precio_unitario-header"><p class="precio_unitario-header">PRECIO UNITARIO</p></td>
<td class="valor_subtotal-header"><p class="valor_subtotal-header">VALOR</p></td>
</tr>
{%- para el artículo en doc.items -%}
<tr>
<td class="cantidad"><p class="cantidad">{{{ item.qty }}</p></td>
<td class="descripcion">
<p class="descripcion">
{%- si nombre_de_artículo!= código_de_artículo -%}
{{ item.item_name }}{%- endif -%}
</p>
</td>
<td class="precio_unitario"><p class="precio_unitario">{{{ item.get_formatted("rate") }}</p></td>
<td class="valor_subtotal"><p class="valor_subtotal">{{ item.get_formatted("amount") }}</p></td>
</tr>
{%- endfor -%}
</cuerpo>
</tabla>
<table class="items-example-footer">
<cuerpo>
<tr><td></td></tr>
</cuerpo>
</tabla>
</span>
<!-- 3.5 Fin de la tabla de artículos -->
Esta sección encierra todos los artículos, y es el pie de la tabla.
EN:
<!-- 3.6 Total in Words Begins -->
<span class="total-en-letras">
<!-- ### We opted to cut the first three letters of the format that ERPNext provides for monetary amounts (Three letter code) for this customer, since they will not be accepting any payments in other currencies. ## -->
<!-- ### It is not affected the Global default of "Hide Currency Symbol" / "Ocultar el símbolo de moneda"## , this is why we used it. -->
{% set in_words_without_first_3 = doc.get_formatted("base_in_words") %}
{% set en_letras_length = in_words_without_first_3|length %}
{% set en_letras_limpio = in_words_without_first_3[3:en_letras_length] %}
<p class="total-en-letras">CANTIDAD EN LETRAS: {{ en_letras_limpio }}</p>
</span>
<!-- 3.6 Total in Words Ends -->
ES:
<!-- 3.6 El total en palabras comienza -->
<span class="total-en-letras">
<Optamos por cortar las tres primeras letras del formato que ERPNext proporciona para las cantidades monetarias (Código de tres letras) para este cliente, ya que no aceptarán ningún pago en otras monedas. ## -->
<!-- ### No está afectado el defecto global de "Ocultar el símbolo de moneda" / "Ocultar el símbolo de moneda "## , por eso lo usamos. -->
{% set in_words_without_first_3 = doc.get_formatted("base_in_words") %}
{% set en_letras_longitud = in_words_without_first_3|longitud %}
{% set en_letras_limpio = in_words_without_first_3[3:en_letras_length] %}
<p class="total-en-letras">CANTIDAD EN LETRAS: {{ en_letras_limpio }}</p>
</span>
<!-- 3.6 Total en palabras termina -->
Este es el total de la factura en números.
EN:
<!-- 3.7 Grand Total Begins -->
<span class="total">
<p class="total">TOTAL {{ doc.get_formatted("grand_total") }}</p>
</span>
<!-- 3.7 Grand Total Ends -->
ES:
<!-- 3.7 El gran total comienza -->
<span class="total">
<p class="total">TOTAL {{ doc.get_formatted("grand_total") }}</p>
</span>
<!-- 3.7 Grandes finales totales -->
La firma digital de las facturas que se validaron con la autoridad fiscal mediante un servicio de pago, requisito para la impresión en papel normal.
EN:
<!-- 3.8 Digital Signature Begins -->
<span class="cae">
<p class="cae">{%- if doc.cae_factura_electronica != None -%}
{{ firma_digital }} {{ doc.cae_factura_electronica }}{%- endif -%}</p>
</span>
<!-- 3.8 Digital Signature Ends -->
<!-- 3.9 Fiscally required data Begins -->
<span class="resolucion">
<p class="resolucion">{{ dte_part1 }}{{ config_series_fields.numero_resolucion }}{{ dte_part2 }}{{ config_series_fields.fecha_resolucion }}{{ dte_part3 }}{{ config_series_fields.secuencia_infile }}{{ dte_part4 }} 1 {{ dte_part5 }} 1000000 {{ dte_part6 }} INFILE {{ dte_part7 }} {{ config_facelec.nit_gface}}</p>
</span>
<!-- 3.9 Fiscally required data Ends -->
ES:
<3.8 La firma digital comienza...
<span class="cae">
<p class="cae">{%- if doc.cae_factura_electronica != None -%}
{{ firma_digital }} {{ doc.cae_factura_electronica }}{%- endif -%}</p>
</span>
<3.8 La firma digital termina...
<!-- 3.9 Los datos requeridos fiscalmente comienzan -->
<span class="resolucion">
<p class="resolucion">{{{{ dte_part1 }}{{{ config_series_fields.numero_resolucion }}{{{ dte_part2 }}{{{ config_series_fields.fecha_resolucion }}{{{ dte_part3 }}{{{ config_series_fields.secuencia_infile }}{{{ dte_part4 }} 1 {{ dte_part5 }} 1000000 {{ dte_part6 }} INFILE {{ dte_part7 }} {{ config_facelec.nit_gface}}</p>
</span>
<!-- 3.9 Datos requeridos fiscalmente Finales -->
Finalmente, cierra la tabla </div>
y el contenedor principal </div>
para el documento.
</div>
</div>
Hasta ahora, este documento ha cubierto algunos de los elementos más esenciales para una factura legalmente conforme. Está claro que no es trivial, y la cantidad de código escrito es mucho. Este es mi código inicial ampliado, y seguramente puede ser acortado y hecho más conciso. Opté por compartir esta versión porque muestra la posibilidad de cambiar cada detalle que desees.
- Puede crear un formato de impresión que permita la impresión en un formato de factura existente. Si mide la página existente y las posiciones de los elementos y transfiere esas medidas aquí, podrá imprimirlas con precisión.
- A pesar de la longitud, y de no ser una práctica óptima, es una metodología efectiva y altamente controlable. Utilícela sólo como base para crear sus propios formatos de impresión Jinja. No es de ninguna manera un método definitivo, sino más bien una propuesta de una práctica que nos dio excelentes resultados.
- Siéntase libre de cambiar, mezclar y combinar los elementos de este tutorial según sus necesidades. Inicialmente sugiero cambiar la ubicación de un elemento en la declaración de estilo CSS, y luego ver el resultado. Luego trabajar desde allí para moverlo por la página, cambiar el ancho, alto, colores, fuentes, imágenes, etc. para que puedas reflejar mejor lo que quieres imprimir. Para un formato preimpreso, cambia el borde de la tabla CSS a 0, para que sólo se imprima el texto.
- Si utiliza una impresora monocromática (térmica o de otro tipo), utilice colores de alto contraste: Negro sobre blanco, etc. Sin embargo, no he probado esto en una impresora de alto contraste, y hago prueba y error.
Si no está de acuerdo con alguna parte de esto, las críticas serán bienvenidas a continuación, pero sólo como una sugerencia de funcionamiento efectivo. La crítica por el bien de la crítica no es bienvenida, ya que nuestro objetivo es mejorar este proceso para el beneficio del usuario. Ahorremos cualquier frustración y centrémonos en mejores prácticas para simplificar esto.
Diseño del formato de impresión de los documentos preimpresos - Foros de discusión de ERPNext