Soporte para factura en HTML
|
@ -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
|
||||
|
|
|
@ -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'))
|
||||
|
||||
|
|
|
@ -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']}<BR>({d['claveprodserv']})",
|
||||
'descripcion': self._get_description(self, subnode, d),
|
||||
'unidad': f"{d['unidad']}<BR>({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:
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
After Width: | Height: | Size: 188 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 191 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
|
@ -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'){
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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')}
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/${rfc}.css">
|
||||
<title>Empresa Libre</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="plantilla">
|
||||
<div class="cancelada" style="display: ${cancelada}">
|
||||
Cancelada
|
||||
<div>Sin valor fiscal</div>
|
||||
</div>
|
||||
<header>
|
||||
<section class="emisor-cintilla">
|
||||
${emisor_calle} ${emisor_noexterior} ${emisor_nointerior}
|
||||
${emisor_colonia} ${emisor_codigopostal} ${emisor_municipio}
|
||||
${emisor_estado} ${emisor_pais}
|
||||
</section>
|
||||
<section class="datos-emisor">
|
||||
<img src="/static/img/${emisor_logo}.png" class="logo"/>
|
||||
<section class="fiscales-emisor">
|
||||
<div class="nombre">
|
||||
${emisor_nombre}
|
||||
</div>
|
||||
<div class="rfc">
|
||||
${emisor_rfc}
|
||||
</div>
|
||||
<div class="regimen">
|
||||
${emisor_regimenfiscal}
|
||||
</div>
|
||||
<div class="telefono">
|
||||
(55) 5810-8088
|
||||
<img src="/static/img/telefono.png" />
|
||||
</div>
|
||||
<div class="correo">
|
||||
administracion@empresalibre.net
|
||||
<img src="/static/img/correo.png" />
|
||||
</div>
|
||||
<div class="web">
|
||||
https://empresalibre.net
|
||||
<img src="/static/img/web.png" />
|
||||
</div>
|
||||
</section>
|
||||
<div class="clear"></div>
|
||||
<section class="receptor">
|
||||
<div class="titulo-vertical">Receptor</div>
|
||||
<div class="nombre">
|
||||
${receptor_nombre}
|
||||
</div>
|
||||
<div class="rfc">
|
||||
${receptor_rfc}
|
||||
</div>
|
||||
<div class="direccion">
|
||||
${receptor_calle} ${receptor_noexterior} ${receptor_nointerior}
|
||||
${receptor_colonia} ${receptor_codigopostal}
|
||||
</div>
|
||||
<div class="estado">
|
||||
${receptor_municipio} ${receptor_estado} ${receptor_pais}
|
||||
</div>
|
||||
<div class="correo">
|
||||
<img src="/static/img/correo.png" /> ${receptor_correo_1}
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="uso-cfdi">
|
||||
${receptor_usocfdi}
|
||||
</div>
|
||||
<div class="telefono">
|
||||
<img src="/static/img/telefono.png" /> ${receptor_telefono_1}
|
||||
</div>
|
||||
</section>
|
||||
<section class="cfdi">
|
||||
<div class="titulo-vertical">Datos CFDI</div>
|
||||
<div class="folio">Folio: <span>${cfdi_serie}-${cfdi_folio}</span></div>
|
||||
<div class="tipo">Tipo: <span>${cfdi_tipodecomprobante}</span></div>
|
||||
<div class="clear"></div>
|
||||
<div class="folio-fiscal">
|
||||
Folio Fiscal <br>
|
||||
<span>${timbre_uuid}</span>
|
||||
</div>
|
||||
<div class="fecha-emision">Fecha Emisión: ${cfdi_fecha}</div>
|
||||
<div class="fecha-certificacion">Fecha Certificacion: ${timbre_fechatimbrado}</div>
|
||||
<div class="lugar-expedicion">CP de Expedición: ${cfdi_lugarexpedicion}</div>
|
||||
</section>
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<div class="clear"></div>
|
||||
<table class="conceptos">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="clave">Clave</th>
|
||||
<th class="descripcion">Descripción</th>
|
||||
<th class="unidad">Unidad</th>
|
||||
<th class="cantidad">Cantidad</th>
|
||||
<th class="valor-unitario">Valor Unitario</th>
|
||||
<th class="importe">Importe</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
% for c in conceptos:
|
||||
<tr>
|
||||
<td class="clave">${c['clave']}</td>
|
||||
<td class="descripcion"><div>${c['descripcion']}</div></td>
|
||||
<td class="unidad">${c['unidad']}</td>
|
||||
<td class="cantidad">${c['cantidad']}</td>
|
||||
<td class="valor-unitario">${c['valorunitario']}</td>
|
||||
<td class="importe">${c['importe']}</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="total-letras">
|
||||
${cfdi_totalenletras}
|
||||
</div>
|
||||
<table class="subtotal">
|
||||
% for total in totales:
|
||||
<tr>
|
||||
<th>${total[0]}</th>
|
||||
<td>${total[1]}</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</table>
|
||||
<div class="clear"></div>
|
||||
<div class="condiciones-pago">
|
||||
${cfdi_condicionespago}
|
||||
</div>
|
||||
<div class="notas">
|
||||
${cfdi_notas}
|
||||
</div>
|
||||
<div class="formapago-metodopago">
|
||||
${cfdi_formapago}<BR>${cfdi_metodopago}
|
||||
</div>
|
||||
<div class="moneda-tipocambio">
|
||||
${cfdi_moneda}, Tipo de Cambio: ${cfdi_tipocambio}
|
||||
</div>
|
||||
<div class="tipocomite">
|
||||
{ine.tipocomite}
|
||||
</div>
|
||||
<div class="tiporelacion">
|
||||
{cfdi.tiporelacion}
|
||||
</div>
|
||||
<div class="tipoproceso">
|
||||
{ine.tipoproceso}
|
||||
</div>
|
||||
<div class="relacionados">
|
||||
{cfdi.relacionados}
|
||||
</div>
|
||||
<div class="idcontabilidad">
|
||||
{ine.idcontabilidad}
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
<div class="sello">
|
||||
<img class="cbb" src="img/cbb.png" />
|
||||
|
||||
<div class="cadenas-sello">
|
||||
<div class="cadena">
|
||||
Sello Digital del CFDI:
|
||||
<div>
|
||||
${cfdi_sello}
|
||||
</div>
|
||||
</div>
|
||||
<div class="cadena">
|
||||
Sello del SAT:
|
||||
<div>
|
||||
${timbre_sellosat}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sello">
|
||||
<div class="cadena-original">
|
||||
Cadena original del complemento de certificación digital del SAT:
|
||||
<div>
|
||||
${timbre_cadenaoriginal}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
<div class="rfc-pac">
|
||||
RFC del PAC que Certifico: ${timbre_rfcprovcertif}
|
||||
Serie CSD SAT: ${timbre_nocertificadosat}
|
||||
Serie CSD Emisor: ${cfdi_nocertificado}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|