diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py
index 353f7ad..5952af9 100644
--- a/source/app/controllers/main.py
+++ b/source/app/controllers/main.py
@@ -510,7 +510,7 @@ class AppDocumentos(object):
session = req.env['beaker.session']
req.context['result'], file_name, content_type = \
self._db.get_doc(type_doc, id_doc, session['rfc'])
- if not type_doc in ('pdf', 'pre', 'tpdf', 'pdfpago'):
+ if not type_doc in ('pdf', 'pre', 'tpdf', 'pdfpago', 'html'):
resp.append_header('Content-Disposition',
'attachment; filename={}'.format(file_name))
resp.content_type = content_type
diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index 5338e94..f681622 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -52,6 +52,11 @@ except ImportError:
import pyqrcode
from dateutil import parser
+from lxml import etree
+
+import mako.runtime
+from mako.exceptions import TopLevelLookupException
+mako.runtime.UNDEFINED = ''
from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice, \
SeaFileAPI, PrintTicket
@@ -1576,6 +1581,27 @@ def to_pdf(data, emisor_rfc, ods=False):
return read_file(path)
+def format_currency(value, currency, digits=2):
+ c = {
+ 'MXN': '$',
+ 'USD': '$',
+ 'EUR': '€',
+ }
+ s = c.get(currency, 'MXN')
+ return f'{s} {float(value):,.{digits}f}'
+
+
+def to_html(data):
+ name = f"{data['rfc']}_{data['version']}.html"
+ try:
+ template = template_lookup.get_template(name)
+ except TopLevelLookupException:
+ template = template_lookup.get_template('plantilla_factura.html')
+ data['rfc'] = 'estilos'
+
+ return template.render(**data)
+
+
def import_employees(rfc):
name = '{}_employees.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
@@ -1621,8 +1647,8 @@ def get_dict(data):
return CaseInsensitiveDict(data)
-def to_letters(value, moneda):
- return NumLet(value, moneda).letras
+def to_letters(value, currency):
+ return NumLet(value, currency).letras
def get_qr(data):
@@ -3648,3 +3674,8 @@ def validate_rfc(value):
return ''
except:
return msg
+
+
+def parse_xml2(xml_str):
+ return etree.fromstring(xml_str.encode('utf-8'))
+
diff --git a/source/app/models/main.py b/source/app/models/main.py
index 71eb26f..2d0d909 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -204,12 +204,15 @@ def get_doc(type_doc, id, rfc):
'ods': 'application/octet-stream',
'zip': 'application/octet-stream',
'nomlog': 'application/txt',
+ 'html': 'text/html',
}
content_type = types.get(type_doc, 'application/pdf')
if type_doc == 'xml':
data, file_name = Facturas.get_xml(id)
elif type_doc == 'pdf':
data, file_name = Facturas.get_pdf(id, rfc)
+ elif type_doc == 'html':
+ data, file_name = Facturas.get_html(id)
elif type_doc == 'ods':
data, file_name = Facturas.get_ods(id, rfc)
elif type_doc == 'zip':
@@ -3671,6 +3674,138 @@ class Facturas(BaseModel):
return doc, name
+ def _get_description(self, node, data):
+ return data['descripcion']
+
+ def _get_others_values(self, invoice, emisor):
+ v_cancel = {True: 'inline', False: 'none'}
+ v_type = {'I': 'Ingreso', 'E': 'Egreso', 'T': 'Traslado'}
+ v_method = {
+ 'PUE': 'Pago en una sola exhibición',
+ 'PPD': 'Pago en parcialidades o diferido',
+ }
+ v_tax = {
+ '001': 'ISR',
+ '002': 'IVA',
+ '003': 'IEPS',
+ }
+
+ data = {
+ 'rfc': emisor.rfc.lower(),
+ 'version': invoice.version,
+ 'cancelada': v_cancel.get(invoice.cancelada),
+ 'cfdi_notas': invoice.notas,
+ }
+
+ xml = util.parse_xml2(invoice.xml)
+ d = util.get_dict(xml.attrib)
+ d.pop('Certificado', '')
+ currency = d.get('Moneda', 'MXN')
+
+ data.update({f'cfdi_{k.lower()}': v for k, v in d.items()})
+
+ data['cfdi_tipodecomprobante'] = v_type[data['cfdi_tipodecomprobante']]
+
+ if data.get('cfdi_formapago', ''):
+ obj = SATFormaPago.get(SATFormaPago.key==invoice.forma_pago)
+ data['cfdi_formapago'] = str(obj)
+ if data.get('cfdi_metodopago', ''):
+ data['cfdi_metodopago'] = 'Método de Pago: ({}) {}'.format(
+ data['cfdi_metodopago'], v_method[data['cfdi_metodopago']])
+
+ obj = SATMonedas.get(SATMonedas.key==currency)
+ data['cfdi_moneda'] = str(obj)
+ data['cfdi_tipocambio'] = util.format_currency(
+ data['cfdi_tipocambio'], currency, 4)
+ data['cfdi_totalenletras'] = util.to_letters(
+ float(data['cfdi_total']), currency)
+
+ data['totales'] = [(
+ 'Subtotal', util.format_currency(data['cfdi_subtotal'], currency))]
+
+ for node in xml:
+ if 'Emisor' in node.tag:
+ d = util.get_dict(node.attrib)
+ data.update(
+ {f'emisor_{k.lower()}': v for k, v in d.items()}
+ )
+ elif 'Receptor' in node.tag:
+ d = util.get_dict(node.attrib)
+ data.update(
+ {f'receptor_{k.lower()}': v for k, v in d.items()}
+ )
+ elif 'Conceptos' in node.tag:
+ data['conceptos'] = []
+ for subnode in node:
+ d = util.get_dict(subnode.attrib)
+ concepto = {
+ 'clave': f"{d['noidentificacion']}
({d['claveprodserv']})",
+ 'descripcion': self._get_description(self, subnode, d),
+ 'unidad': f"{d['unidad']}
({d['claveunidad']})",
+ 'cantidad': d['cantidad'],
+ 'valorunitario': util.format_currency(d['valorunitario'], currency),
+ 'importe': util.format_currency(d['importe'], currency),
+ }
+ data['conceptos'].append(concepto)
+ elif 'Impuestos' in node.tag:
+ for subnode in node:
+ if 'Traslados' in subnode.tag:
+ for t in subnode:
+ d = util.get_dict(t.attrib)
+ name = v_tax.get(d['impuesto'])
+ tasa = FORMAT.format(float(d['tasaocuota']))
+ title = f"Traslado {name} {tasa}"
+ importe = util.format_currency(d['importe'], currency)
+ data['totales'].append((title, importe))
+ elif 'Retenciones' in subnode.tag:
+ for r in subnode:
+ d = util.get_dict(r.attrib)
+ name = v_tax.get(d['impuesto'])
+ tasa = FORMAT.format(float(d['tasaocuota']))
+ title = f"Retención {name} {tasa}"
+ importe = util.format_currency(d['importe'], currency)
+ data['totales'].append((title, importe))
+ elif 'Complemento' in node.tag:
+ for subnode in node:
+ if 'TimbreFiscalDigital' in subnode.tag:
+ d = util.get_dict(subnode.attrib)
+ data.update(
+ {f'timbre_{k.lower()}': v for k, v in d.items()}
+ )
+
+ obj = SATRegimenes.get(SATRegimenes.key==data['emisor_regimenfiscal'])
+ data['emisor_regimenfiscal'] = str(obj)
+ data['emisor_logo'] = data['emisor_rfc'].lower()
+
+ obj = SATUsoCfdi.get(SATUsoCfdi.key==data['receptor_usocfdi'])
+ data['receptor_usocfdi'] = str(obj)
+
+ data['totales'].append((
+ 'Total', util.format_currency(data['cfdi_total'], currency)))
+
+ data['timbre_cadenaoriginal'] = f"""||{data['timbre_version']}|
+ {data['timbre_uuid']}|{data['timbre_fechatimbrado']}|
+ {data['timbre_sellocfd']}|{data['timbre_nocertificadosat']}||"""
+ # ~ print(data)
+ return data
+
+ @classmethod
+ def get_html(cls, id):
+ try:
+ emisor = Emisor.select()[0]
+ except IndexError:
+ return '', 'sin_datos_de_emisor.html'
+
+ obj = Facturas.get(Facturas.id==id)
+ name = '{}{}_{}.html'.format(obj.serie, obj.folio, obj.cliente.rfc)
+ if obj.uuid is None:
+ return '', name
+
+ data = cls._get_others_values(cls, obj, emisor)
+ doc = util.to_html(data)
+
+ return doc, name
+
@classmethod
def get_ods(cls, id, rfc):
try:
diff --git a/source/app/settings.py b/source/app/settings.py
index df4130f..6897b81 100644
--- a/source/app/settings.py
+++ b/source/app/settings.py
@@ -74,7 +74,10 @@ TEMPLATE_CANCEL = os.path.abspath(os.path.join(PATH_TEMPLATES, CT))
PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt'))
PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin'))
-template_lookup = TemplateLookup(directories=[PATH_TEMPLATES],
+PATH_TEMPLATES_USER = os.path.abspath(os.path.join(
+ BASE_DIR, '..', 'docs', 'templates'))
+directories=[PATH_TEMPLATES, PATH_TEMPLATES_USER]
+template_lookup = TemplateLookup(directories=directories,
input_encoding='utf-8',
output_encoding='utf-8')
diff --git a/source/static/css/estilos.css b/source/static/css/estilos.css
new file mode 100644
index 0000000..0dbd2a0
--- /dev/null
+++ b/source/static/css/estilos.css
@@ -0,0 +1,508 @@
+.emisor-cintilla{
+ background-color: #975759;
+ color: #fff;
+}
+
+.fiscales-emisor{
+ color: #7d1916;
+}
+
+.fiscales-emisor .telefono, .fiscales-emisor .correo, .fiscales-emisor .web{
+ color: #333;
+}
+
+header .titulo-vertical{
+ background-color: #dbc5c6;
+ color: #000;
+}
+
+.receptor .nombre{
+ color: #000;
+}
+
+.receptor .rfc{
+ color: #000;
+}
+
+.receptor .direccion{
+ color: #000;
+}
+
+.receptor .estado{
+ color: #000;
+}
+
+.cfdi .folio span{
+ color: #ed483d;
+}
+
+.cfdi .tipo span{
+ color: #ed483d;
+}
+
+.cfdi .folio-fiscal span{
+ color: #ed483d;
+}
+
+.conceptos th{
+ background-color: #975759;
+}
+
+.conceptos td.clave, .conceptos td.unidad, .conceptos td.valor-unitario{
+ background-color: #dbc5c6;
+}
+
+.conceptos td.descripcion, .conceptos td.cantidad, .conceptos td.importe{
+ background-color: #f0e7e7;
+}
+
+table.subtotal th{
+ background-color: #dbc5c6;
+}
+
+table.subtotal td{
+ background-color: #f0e7e7;
+}
+
+.sello .cadenas-sello .cadena div {
+ background-color: #dbc5c6;
+ color: #7d1916;
+}
+
+.rfc-pac {
+ background-color: #dbc5c6;
+ color: #7d1916;
+}
+
+.cancelada{
+ color: #ed3833;
+}
+
+@font-face {
+ font-family: 'Avenir Next';
+ src: url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot');
+ src: url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot?#iefix') format('embedded-opentype'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff2') format('woff2'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff') format('woff'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.ttf') format('truetype'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Regular.svg#AvenirNextLTPro-Regular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Avenir Next';
+ src: url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot');
+ src: url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot?#iefix') format('embedded-opentype'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff2') format('woff2'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff') format('woff'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.ttf') format('truetype'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-Bold.svg#AvenirNextLTPro-Bold') format('svg');
+ font-weight: bold;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Avenir Next';
+ src: url('/static/fonts/avenir_next/AvenirNextLTPro-It.eot');
+ src: url('/static/fonts/avenir_next/AvenirNextLTPro-It.eot?#iefix') format('embedded-opentype'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-It.woff2') format('woff2'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-It.woff') format('woff'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-It.ttf') format('truetype'),
+ url('/static/fonts/avenir_next/AvenirNextLTPro-It.svg#AvenirNextLTPro-It') format('svg');
+ font-weight: normal;
+ font-style: italic;
+}
+
+
+@page{
+ size: 8.5in 11in;
+ margin: 1cm ;
+ background-color: white !important;
+}
+
+@media print {
+ thead {display: table-header-group;}
+ body {
+ background-color: #fff;
+ }
+}
+
+body {
+ background-color: rgb(204,204,204);
+ font-family: 'Avenir Next';
+}
+
+#plantilla {
+ background: #fff;
+ display: block;
+ margin: 0 auto;
+ width: 21.5cm;
+ height: 27.9cm;
+ position: relative;
+}
+
+.cancelada{
+ -ms-transform: rotate(320deg);
+ -webkit-transform: rotate(320deg);
+ transform: rotate(320deg);
+ color: #ed3833;
+ font-size: 100px;
+ font-weight: bold;
+ left: 18%;
+ position: absolute;
+ text-align: center;
+ top: 30%;
+ z-index: 1000;
+}
+
+.cancelada div{
+ font-size: 20px;
+}
+
+.emisor-cintilla{
+ background-color: #975759;
+ color: #fff;
+ font-size: 7pt;
+ padding: 5px;
+ text-align: center;
+}
+
+.datos-emisor .logo{
+ margin-left: 20px;
+ max-width: 180px;
+}
+
+.datos-emisor .fiscales-emisor{
+ float: right;
+}
+
+.fiscales-emisor{
+ color: #7d1916;
+ font-weight: bold;
+ margin-top: 20px;
+ text-align: right;
+}
+
+.fiscales-emisor .nombre{
+ font-size: 18px;
+ padding-right: 30px;
+}
+
+.fiscales-emisor .rfc{
+ font-size: 16px;
+ padding-right: 30px;
+}
+
+.fiscales-emisor .regimen{
+ font-size: 12px;
+ padding-right: 30px;
+}
+
+.fiscales-emisor .telefono, .fiscales-emisor .correo, .fiscales-emisor .web{
+ color: #333;
+ font-size: 14px;
+ line-height: 15px;
+ padding-right: 10px;
+}
+
+.fiscales-emisor img{
+ margin-left: 10px;
+ width: 10px;
+}
+
+.clear{
+ clear: both;
+}
+
+header .titulo-vertical{
+ background-color: #dbc5c6;
+ -ms-transform: rotate(270deg);
+ -webkit-transform: rotate(270deg);
+ transform: rotate(270deg);
+ font-size: 15px;
+ font-weight: bold;
+ left: -31px;
+ line-height: 35px;
+ position: absolute;
+ text-align: center;
+ top: 35px;
+ width: 105px;
+}
+
+header .receptor{
+ box-sizing: border-box;
+ float: left;
+ font-size: 10px;
+ height: 106px;
+ margin-top: 10px;
+ position: relative;
+ padding-bottom: 5px;
+ padding-left: 45px;
+ padding-top: 5px;
+ width: 65%;
+}
+
+.receptor .nombre{
+ color: #000;
+ font-size: 17px;
+ font-weight: bold;
+}
+
+.receptor .rfc{
+ color: #000;
+ font-size: 15px;
+ font-weight: bold;
+}
+
+.receptor .direccion,
+.receptor .estado{
+ color: #000;
+ font-size: 10px;
+}
+
+.receptor img{
+ width: 10px;
+}
+
+.receptor .correo,
+.receptor .telefono{
+ float: right;
+ width: 40%;
+}
+
+.receptor .uso-cfdi{
+ color: #000;
+ font-size: 12px;
+ font-weight: bold;
+ float: left;
+ width: 60%;
+}
+
+header .cfdi{
+ box-sizing: border-box;
+ float: right;
+ font-size: 10px;
+ height: 100px;
+ margin-top: 10px;
+ position: relative;
+ padding-bottom: 5px;
+ padding-left: 45px;
+ padding-top: 5px;
+ width: 35%;
+}
+
+.cfdi .folio, .cfdi .tipo{
+ color: #000;
+ float: left;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 15px;
+ width: 50%;
+}
+
+.cfdi .folio span, .cfdi .tipo span{
+ color: #ed483d;
+}
+
+.cfdi .folio span{
+ font-size: 10px;
+}
+
+.cfdi .tipo span{
+ font-size: 10px;
+}
+
+.cfdi .folio-fiscal{
+ color: #000;
+ font-size: 10px;
+ font-weight: bold;
+ line-height: 15px;
+ text-align: center;
+}
+
+.cfdi .folio-fiscal span{
+ color: #ed483d;
+}
+
+.cfdi .fecha-emision, .cfdi .fecha-certificacion, .cfdi .lugar-expedicion{
+ color: #333;
+ font-size: 10px;
+ font-weight: bold;
+ line-height: 15px;
+}
+
+.conceptos{
+ margin-bottom: 10px;
+ margin-top: 20px;
+ width: 100%;
+ text-align: center;
+}
+
+.conceptos th{
+ background-color: #975759;
+ color: #fff;
+ font-size: 11px;
+ line-height: 15px;
+}
+
+.conceptos .clave{
+ width: 10%;
+}
+
+.conceptos .descripcion{
+ width: 45%;
+}
+.conceptos .descripcion div{
+ text-align: justify;
+}
+
+.conceptos .unidad{
+ width: 8%;
+}
+
+.conceptos .cantidad{
+ width: 9%;
+}
+
+.conceptos .valor-unitario{
+ width: 13%;
+}
+
+.conceptos .importe{
+ width: 15%;
+}
+
+.conceptos td{
+ background-color: #975759;
+ color: #000;
+ font-size: 11px;
+ line-height: 15px;
+}
+
+.conceptos td.clave, .conceptos td.unidad, .conceptos td.valor-unitario{
+ background-color: #dbc5c6;
+}
+
+.conceptos td.descripcion, .conceptos td.cantidad, .conceptos td.importe{
+ background-color: #f0e7e7;
+}
+
+.conceptos td.valor-unitario, .conceptos td.importe{
+ text-align: right;
+}
+
+.total-letras{
+ float: left;
+ font-size: 12px;
+ line-height: 15px;
+ margin: 4px 0;
+ width: 63%;
+}
+
+table.subtotal{
+ float: right;
+ font-size: 12px;
+ line-height: 15px;
+ text-align: right;
+ width: 37%;
+ font-weight: bold;
+}
+
+table.subtotal th{
+ background-color: #f0e7e7;
+ width: 60%;
+ padding: 3px 3px 3px 3px;
+}
+
+table.subtotal td{
+ background-color: #dbc5c6;
+ width: 40%;
+ padding: 3px 3px 3px 3px;
+}
+
+.condiciones-pago{
+ font-size: 11px;
+ line-height: 15px;
+}
+
+.notas{
+ font-size: 11px;
+ margin: 20px 0;
+}
+
+.formapago-metodopago, .moneda-tipocambio, .tiporelacion, .relacionados{
+ float: left;
+ font-size: 10px;
+ line-height: 12px;
+ width: 85%;
+}
+
+.tipocomite, .tipoproceso, .idcontabilidad{
+ float: right;
+ font-size: 10px;
+ line-height: 12px;
+ width: 30%;
+}
+
+.sello{
+ margin: 20px 0;
+}
+
+.sello .cbb{
+ border: 1px solid #000;
+ height: auto;
+ margin-top: 20px;
+ margin-right: 5%;
+ width: 15%;
+}
+
+.sello .cadenas-sello{
+ color: #000;
+ float: right;
+ font-size: 8px;
+ font-weight: bold;
+ line-height: 16px;
+ width: 79%;
+ margin-left: 2px;
+ margin-right: 2px;
+ word-break: break-all;
+}
+
+.sello .cadenas-sello .cadena{
+ margin: 10px 0;
+}
+
+.sello .cadenas-sello .cadena div{
+ font-weight: normal;
+ background-color: #dbc5c6;
+ color: #7d1916;
+}
+
+.sello .cadena-original{
+ color: #000;
+ float: right;
+ font-size: 8px;
+ font-weight: bold;
+ line-height: 16px;
+ width: 100%;
+ margin: 10px;
+ margin-left: 5px;
+ margin-right: 2px;
+ word-break: break-all;
+}
+
+.sello .cadena-original div{
+ font-weight: normal;
+ background-color: #dbc5c6;
+ color: #7d1916;
+}
+
+.rfc-pac{
+ background-color: #dbc5c6;
+ color: #7d1916;
+ font-size: 10px;
+ line-height: 16px;
+ text-align: center;
+ margin-right: 2px;
+}
diff --git a/source/static/fonts/avenir_next/.DS_Store b/source/static/fonts/avenir_next/.DS_Store
new file mode 100644
index 0000000..7b40fe9
Binary files /dev/null and b/source/static/fonts/avenir_next/.DS_Store differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot
new file mode 100755
index 0000000..6ff4d6c
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.eot differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.svg b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.svg
new file mode 100755
index 0000000..3d73033
--- /dev/null
+++ b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.svg
@@ -0,0 +1,2842 @@
+
+
+
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.ttf b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.ttf
new file mode 100755
index 0000000..d32eacb
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.ttf differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff
new file mode 100755
index 0000000..82bac28
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff2 b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff2
new file mode 100755
index 0000000..018ab10
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Bold.woff2 differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-It.eot b/source/static/fonts/avenir_next/AvenirNextLTPro-It.eot
new file mode 100755
index 0000000..67eff78
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-It.eot differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-It.svg b/source/static/fonts/avenir_next/AvenirNextLTPro-It.svg
new file mode 100755
index 0000000..7187cbc
--- /dev/null
+++ b/source/static/fonts/avenir_next/AvenirNextLTPro-It.svg
@@ -0,0 +1,1956 @@
+
+
+
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-It.ttf b/source/static/fonts/avenir_next/AvenirNextLTPro-It.ttf
new file mode 100755
index 0000000..433a0a0
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-It.ttf differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-It.woff b/source/static/fonts/avenir_next/AvenirNextLTPro-It.woff
new file mode 100755
index 0000000..4afb82b
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-It.woff differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-It.woff2 b/source/static/fonts/avenir_next/AvenirNextLTPro-It.woff2
new file mode 100755
index 0000000..5c1f3b7
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-It.woff2 differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot
new file mode 100755
index 0000000..f91a884
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.eot differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.svg b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.svg
new file mode 100755
index 0000000..ea10cd4
--- /dev/null
+++ b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.svg
@@ -0,0 +1,2825 @@
+
+
+
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.ttf b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.ttf
new file mode 100755
index 0000000..ad46859
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.ttf differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff
new file mode 100755
index 0000000..f19e41f
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff differ
diff --git a/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff2 b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff2
new file mode 100755
index 0000000..32eccb0
Binary files /dev/null and b/source/static/fonts/avenir_next/AvenirNextLTPro-Regular.woff2 differ
diff --git a/source/static/img/correo.png b/source/static/img/correo.png
new file mode 100644
index 0000000..3e3233f
Binary files /dev/null and b/source/static/img/correo.png differ
diff --git a/source/static/img/tcm970625mb1.png b/source/static/img/tcm970625mb1.png
new file mode 100644
index 0000000..37dbe13
Binary files /dev/null and b/source/static/img/tcm970625mb1.png differ
diff --git a/source/static/img/telefono.png b/source/static/img/telefono.png
new file mode 100644
index 0000000..edccef9
Binary files /dev/null and b/source/static/img/telefono.png differ
diff --git a/source/static/img/web.png b/source/static/img/web.png
new file mode 100644
index 0000000..fe9fde2
Binary files /dev/null and b/source/static/img/web.png differ
diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js
index fac40e6..893312e 100644
--- a/source/static/js/controller/invoices.js
+++ b/source/static/js/controller/invoices.js
@@ -1282,11 +1282,15 @@ function enviar_correo(row){
function get_pdf(id){
- //~ location = '/doc/pdf/' + id
window.open('/doc/pdf/' + id, '_blank')
}
+function get_html(id){
+ window.open('/doc/html/' + id, '_blank')
+}
+
+
function grid_invoices_click(id, e, node){
var row = this.getItem(id)
@@ -1294,6 +1298,8 @@ function grid_invoices_click(id, e, node){
location = '/doc/xml/' + row.id
}else if(id.column == 'pdf'){
get_pdf(row.id)
+ }else if(id.column == 'html'){
+ get_html(row.id)
}else if(id.column == 'ods'){
location = '/doc/ods/' + row.id
}else if(id.column == 'zip'){
diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js
index bae66bd..e74b00e 100644
--- a/source/static/js/controller/util.js
+++ b/source/static/js/controller/util.js
@@ -59,6 +59,7 @@ function get_icon(tipo){
icons = {
xml: 'fa-file-code-o',
pdf: 'fa-file-pdf-o',
+ html: 'fa-html5',
zip: 'fa-file-zip-o',
email: 'fa-envelope-o',
print: 'fa-print',
diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js
index 49fe2f4..09d7e1d 100644
--- a/source/static/js/ui/invoices.js
+++ b/source/static/js/ui/invoices.js
@@ -267,6 +267,7 @@ var grid_invoices_cols = [
fillspace: true, sort: 'string', footer: {text: '$ 0.00', colspan: 2}},
{id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
+ {id: 'html', header: 'HTML', adjust: 'data', template: get_icon('html')},
{id: 'ods', header: 'ODS', adjust: 'data', template: get_icon('table')},
{id: 'zip', header: 'ZIP', adjust: 'data', template: get_icon('zip')},
{id: 'email', header: '', adjust: 'data', template: get_icon('email')}
diff --git a/source/templates/plantilla_factura.html b/source/templates/plantilla_factura.html
new file mode 100644
index 0000000..84639cb
--- /dev/null
+++ b/source/templates/plantilla_factura.html
@@ -0,0 +1,191 @@
+
+
+
Clave | +Descripción | +Unidad | +Cantidad | +Valor Unitario | +Importe | +
---|---|---|---|---|---|
${c['clave']} | +${c['descripcion']} |
+ ${c['unidad']} | +${c['cantidad']} | +${c['valorunitario']} | +${c['importe']} | +
${total[0]} | +${total[1]} | +
---|