|
@ -17,6 +17,7 @@ source/media
|
|||
docs/bk/
|
||||
source/docs/
|
||||
site/
|
||||
vedev/
|
||||
cache/
|
||||
credenciales.conf
|
||||
*.sqlite
|
||||
|
@ -24,4 +25,5 @@ credenciales.conf
|
|||
*.service
|
||||
*.orig
|
||||
rfc.db
|
||||
Dockerfile
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
v 1.26.0 [15-ene-2019]
|
||||
----------------------
|
||||
- Mejora: Generar PDF desde plantilla JSON
|
||||
|
||||
|
||||
v 1.25.0 [07-nov-2018]
|
||||
----------------------
|
||||
- Fix: Al importar días de pago en nómina
|
||||
|
|
|
@ -29,6 +29,9 @@ from reportlab.platypus import Paragraph, Table, TableStyle, Spacer
|
|||
from reportlab.pdfgen import canvas
|
||||
|
||||
|
||||
CANCEL = False
|
||||
|
||||
|
||||
#~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
|
||||
class CaseInsensitiveDict(collections.MutableMapping):
|
||||
"""A case-insensitive ``dict``-like object.
|
||||
|
@ -340,6 +343,7 @@ class SendMail(object):
|
|||
|
||||
class NumberedCanvas(canvas.Canvas):
|
||||
X = 20.59 * cm
|
||||
XC = 21.6 * cm / 2 + 1.5 * cm
|
||||
Y = 1.5 * cm
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -357,19 +361,33 @@ class NumberedCanvas(canvas.Canvas):
|
|||
for state in self._saved_page_states:
|
||||
self.__dict__.update(state)
|
||||
self.draw_page_number(page_count)
|
||||
self.draw_cancel()
|
||||
canvas.Canvas.showPage(self)
|
||||
canvas.Canvas.save(self)
|
||||
return
|
||||
|
||||
def draw_page_number(self, page_count):
|
||||
self.setFont('Helvetica', 8)
|
||||
self.setFillColor(colors.darkred)
|
||||
text = 'Página {} de {}'.format(self._pageNumber, page_count)
|
||||
# ~ self.setFillColor(colors.darkred)
|
||||
text = f'Página {self._pageNumber} de {page_count}'
|
||||
self.drawRightString(self.X, self.Y, text)
|
||||
text = 'Factura elaborada con software libre: www.empresalibre.net'
|
||||
|
||||
text = 'Este documento es una representación impresa de un CFDI'
|
||||
self.drawCentredString(self.XC, self.Y, text)
|
||||
|
||||
text = 'Factura elaborada con software libre'
|
||||
self.drawString(1.5 * cm, 1.5 * cm, text)
|
||||
return
|
||||
|
||||
def draw_cancel(self):
|
||||
global CANCEL
|
||||
if CANCEL:
|
||||
self.rotate(45)
|
||||
self.setFont('Helvetica', 100)
|
||||
self.setFillColor(colors.red)
|
||||
text = 'Cancelada'
|
||||
self.drawCentredString(20 * cm, 3 * cm, text)
|
||||
return
|
||||
|
||||
class TemplateInvoice(BaseDocTemplate):
|
||||
|
||||
|
@ -418,6 +436,9 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
def _emisor(self, styles, data):
|
||||
logo_path = data.pop('logo', '')
|
||||
logo_style = styles.pop('logo', {})
|
||||
logo_path2 = data.pop('logo2', '')
|
||||
logo_style2 = styles.pop('logo2', {})
|
||||
sucursales = styles.pop('sucursales', {})
|
||||
|
||||
for k, v in styles.items():
|
||||
self._set_text(styles[k], data.get(k, ''))
|
||||
|
@ -428,6 +449,28 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
for k in keys:
|
||||
rect[k] = rect[k] * cm
|
||||
self.canv.drawImage(logo_path, **rect)
|
||||
|
||||
if logo_path2 and logo_style2:
|
||||
rect = logo_style2['rectangulo']
|
||||
keys = ('x', 'y', 'width', 'height')
|
||||
for k in keys:
|
||||
rect[k] = rect[k] * cm
|
||||
self.canv.drawImage(logo_path2, **rect)
|
||||
|
||||
if sucursales:
|
||||
for k, sucursal in sucursales.items():
|
||||
values = sucursal.pop('textos')
|
||||
x = sucursal['rectangulo']['x']
|
||||
y = sucursal['rectangulo']['y']
|
||||
w = sucursal['rectangulo']['width']
|
||||
h = sucursal['rectangulo']['height']
|
||||
for v in values:
|
||||
self._set_text(sucursal, v)
|
||||
y -= h
|
||||
sucursal['rectangulo']['x'] = x
|
||||
sucursal['rectangulo']['y'] = y
|
||||
sucursal['rectangulo']['width'] = w
|
||||
sucursal['rectangulo']['height'] = h
|
||||
return
|
||||
|
||||
def _receptor(self, styles, data):
|
||||
|
@ -449,6 +492,7 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
|
||||
def _comprobante1(self, styles, data):
|
||||
title = styles.pop('titulo', {})
|
||||
self.canv.setTitle(f"Factura {data.get('seriefolio', '')}")
|
||||
|
||||
for k, v in styles.items():
|
||||
self._set_text(styles[k], data.get(k, ''))
|
||||
|
@ -480,6 +524,12 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
fields = ('valorunitario', 'importe')
|
||||
if field in fields:
|
||||
return self._currency(value)
|
||||
|
||||
if field == 'descripcion':
|
||||
html = '<font color="black" size=7>{}</font>'
|
||||
style_bt = getSampleStyleSheet()['BodyText']
|
||||
return Paragraph(html.format(value), style_bt)
|
||||
|
||||
return value
|
||||
|
||||
def _conceptos(self, conceptos):
|
||||
|
@ -515,8 +565,8 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
('GRID', (0, 0), (-1, -1), 0.05 * cm, colors.white),
|
||||
('ALIGN', (1, 0), (-1, -1), 'RIGHT'),
|
||||
('FONTSIZE', (0, 0), (-1, -1), 8),
|
||||
('BACKGROUND', (1, 0), (-1, -1), colors.linen),
|
||||
('TEXTCOLOR', (1, 0), (-1, -1), colors.darkred),
|
||||
('BACKGROUND', (1, 0), (-1, -1), colors.lightgrey),
|
||||
('TEXTCOLOR', (1, 0), (-1, -1), colors.black),
|
||||
('FACE', (1, 0), (-1, -1), 'Helvetica-Bold'),
|
||||
]
|
||||
table = Table(rows, colWidths=widths, spaceBefore=0.25*cm)
|
||||
|
@ -524,7 +574,7 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
return table
|
||||
|
||||
def _comprobante2(self, styles, data):
|
||||
leyenda = styles.pop('leyenda', {})
|
||||
leyendas = styles.pop('leyendas', {})
|
||||
|
||||
ls = []
|
||||
for k, v in styles.items():
|
||||
|
@ -542,7 +592,7 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
style_bt = getSampleStyleSheet()['BodyText']
|
||||
style_bt.leading = 8
|
||||
html_t = '<b><font size=6>{}</font></b>'
|
||||
html = '<font color="darkred" size=5>{}</font>'
|
||||
html = '<font color="black" size=5>{}</font>'
|
||||
msg = 'Cadena original del complemento de certificación digital del SAT'
|
||||
rows = [
|
||||
(cbb, Paragraph(html_t.format('Sello Digital del CFDI'), style_bt)),
|
||||
|
@ -558,13 +608,13 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
('FONTSIZE', (0, 0), (-1, -1), 6),
|
||||
('SPAN', (0, 0), (0, -1)),
|
||||
('FACE', (1, 0), (1, 0), 'Helvetica-Bold'),
|
||||
('BACKGROUND', (1, 1), (1, 1), colors.linen),
|
||||
('BACKGROUND', (1, 1), (1, 1), colors.lightgrey),
|
||||
('TEXTCOLOR', (1, 1), (1, 1), colors.darkred),
|
||||
('FACE', (1, 2), (1, 2), 'Helvetica-Bold'),
|
||||
('BACKGROUND', (1, 3), (1, 3), colors.linen),
|
||||
('BACKGROUND', (1, 3), (1, 3), colors.lightgrey),
|
||||
('TEXTCOLOR', (1, 3), (1, 3), colors.darkred),
|
||||
('FACE', (1, 4), (1, 4), 'Helvetica-Bold'),
|
||||
('BACKGROUND', (1, 5), (1, 5), colors.linen),
|
||||
('BACKGROUND', (1, 5), (1, 5), colors.lightgrey),
|
||||
('TEXTCOLOR', (1, 5), (1, 5), colors.darkred),
|
||||
('ALIGN', (0, 0), (0, 0), 'CENTER'),
|
||||
('VALIGN', (0, 0), (0, 0), 'MIDDLE'),
|
||||
|
@ -573,14 +623,14 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
table.setStyle(TableStyle(table_styles))
|
||||
ls.append(table)
|
||||
|
||||
if leyenda:
|
||||
if 'spaceBefore' in leyenda['estilo']:
|
||||
leyenda['estilo']['spaceBefore'] = \
|
||||
leyenda['estilo']['spaceBefore'] * cm
|
||||
msg = 'Este documento es una representación impresa de un CFDI'
|
||||
ps = ParagraphStyle(**leyenda['estilo'])
|
||||
p = Paragraph(msg, ps)
|
||||
ls.append(p)
|
||||
if leyendas:
|
||||
if 'spaceBefore' in leyendas['estilo']:
|
||||
leyendas['estilo']['spaceBefore'] = \
|
||||
leyendas['estilo']['spaceBefore'] * cm
|
||||
for t in leyendas['textos']:
|
||||
ps = ParagraphStyle(**leyendas['estilo'])
|
||||
p = Paragraph(t, ps)
|
||||
ls.append(p)
|
||||
|
||||
return ls
|
||||
|
||||
|
@ -596,8 +646,10 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
return self._data
|
||||
@data.setter
|
||||
def data(self, values):
|
||||
#~ print (values)
|
||||
global CANCEL
|
||||
# ~ print (values)
|
||||
self._data = values
|
||||
CANCEL = self._data['cancelada']
|
||||
|
||||
rows = self._conceptos(self._data['conceptos'])
|
||||
widths = [2 * cm, 9 * cm, 1.5 * cm, 2 * cm, 2 * cm, 3 * cm]
|
||||
|
@ -607,13 +659,13 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('FACE', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('BACKGROUND', (0, 0), (-1, 0), colors.darkred),
|
||||
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 7),
|
||||
('VALIGN', (0, 1), (-1, -1), 'TOP'),
|
||||
('ALIGN', (0, 1), (0, -1), 'CENTER'),
|
||||
('ALIGN', (2, 1), (2, -1), 'CENTER'),
|
||||
('ALIGN', (3, 1), (5, -1), 'RIGHT'),
|
||||
('LINEBELOW', (0, 1), (-1, -1), 0.05 * cm, colors.darkred),
|
||||
('LINEBELOW', (0, 1), (-1, -1), 0.05 * cm, colors.grey),
|
||||
('LINEBEFORE', (0, 1), (-1, -1), 0.05 * cm, colors.white),
|
||||
]
|
||||
table_conceptos = Table(rows, colWidths=widths, repeatRows=1)
|
||||
|
@ -623,7 +675,7 @@ class TemplateInvoice(BaseDocTemplate):
|
|||
comprobante = self._comprobante2(
|
||||
self._custom_styles['comprobante'], self.data['comprobante'])
|
||||
|
||||
self._rows = [Spacer(0, 6*cm), table_conceptos, totales] + comprobante
|
||||
self._rows = [Spacer(0, 5.7 * cm), table_conceptos, totales] + comprobante
|
||||
|
||||
def render(self):
|
||||
frame = Frame(self.leftMargin, self.bottomMargin,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
# ~ You should have received a copy of the GNU General Public License
|
||||
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import getpass
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import locale
|
||||
import mimetypes
|
||||
|
@ -29,6 +31,7 @@ import sqlite3
|
|||
import socket
|
||||
import subprocess
|
||||
import tempfile
|
||||
import textwrap
|
||||
import threading
|
||||
import time
|
||||
import unicodedata
|
||||
|
@ -41,6 +44,10 @@ from pathlib import Path
|
|||
from xml.etree import ElementTree as ET
|
||||
from xml.dom.minidom import parseString
|
||||
|
||||
# ~ import pdfkit
|
||||
# ~ from weasyprint import HTML, CSS
|
||||
# ~ from weasyprint.fonts import FontConfiguration
|
||||
|
||||
try:
|
||||
import uno
|
||||
from com.sun.star.beans import PropertyValue
|
||||
|
@ -52,6 +59,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
|
||||
|
@ -63,6 +75,14 @@ from settings import SEAFILE_SERVER, USAR_TOKEN, API, DECIMALES_TAX
|
|||
from .configpac import AUTH
|
||||
|
||||
|
||||
# ~ v2
|
||||
from settings import (
|
||||
EXT,
|
||||
MXN,
|
||||
PATHS,
|
||||
)
|
||||
|
||||
|
||||
def _call(args):
|
||||
return subprocess.check_output(args, shell=True).decode()
|
||||
|
||||
|
@ -645,7 +665,7 @@ class LIBO(object):
|
|||
self._ctx = None
|
||||
self._sm = None
|
||||
self._desktop = None
|
||||
self._currency = 'MXN'
|
||||
self._currency = MXN
|
||||
self._total_cantidades = 0
|
||||
if self.is_running:
|
||||
ctx = uno.getComponentContext()
|
||||
|
@ -1541,7 +1561,7 @@ class LIBO(object):
|
|||
return rows, ''
|
||||
|
||||
|
||||
def to_pdf(data, emisor_rfc, ods=False):
|
||||
def to_pdf(data, emisor_rfc, ods=False, pdf_from='1'):
|
||||
rfc = data['emisor']['rfc']
|
||||
if DEBUG:
|
||||
rfc = emisor_rfc
|
||||
|
@ -1554,6 +1574,9 @@ def to_pdf(data, emisor_rfc, ods=False):
|
|||
version = '1.0'
|
||||
pagos = 'pagos_'
|
||||
|
||||
if pdf_from == '2':
|
||||
return to_pdf_from_json(rfc, version, data)
|
||||
|
||||
if APP_LIBO:
|
||||
app = LIBO()
|
||||
if app.is_running:
|
||||
|
@ -1565,9 +1588,19 @@ def to_pdf(data, emisor_rfc, ods=False):
|
|||
if path:
|
||||
return app.pdf(path, data, ods)
|
||||
|
||||
return to_pdf_from_json(rfc, version, data)
|
||||
|
||||
|
||||
def to_pdf_from_json(rfc, version, data):
|
||||
rfc = rfc.lower()
|
||||
name = '{}_{}.json'.format(rfc, version)
|
||||
custom_styles = get_custom_styles(name)
|
||||
|
||||
path_logo = _join(PATHS['IMG'], f"{rfc}.png")
|
||||
data['emisor']['logo'] = path_logo
|
||||
path_logo = _join(PATHS['IMG'], f"{rfc}_2.png")
|
||||
data['emisor']['logo2'] = path_logo
|
||||
|
||||
path = get_path_temp()
|
||||
pdf = TemplateInvoice(path)
|
||||
pdf.custom_styles = custom_styles
|
||||
|
@ -1576,6 +1609,56 @@ 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'] = 'invoice'
|
||||
|
||||
# ~ data['cfdi_sello'] = textwrap.fill(data['cfdi_sello'], 50)
|
||||
# ~ data['timbre_sellosat'] = textwrap.fill(data['timbre_sellosat'], 110)
|
||||
# ~ data['timbre_cadenaoriginal'] = textwrap.fill(data['timbre_cadenaoriginal'], 140)
|
||||
|
||||
# ~ data['cfdi_sello'] = 'X'*100 + '<BR>' + 'X'*100 + '<BR>' + 'X'*100
|
||||
# ~ data['timbre_sellosat'] = 'X'*100 + '<BR>' + 'X'*100 + '<BR>' + 'X'*100
|
||||
|
||||
return template.render(**data)
|
||||
|
||||
|
||||
def html_to_pdf(data):
|
||||
path_pdf = '/home/mau/test.pdf'
|
||||
css = '/home/mau/projects/empresa-libre/source/static/css/invoice.css'
|
||||
|
||||
# ~ font_config = FontConfiguration()
|
||||
# ~ html = HTML(string=data)
|
||||
# ~ css = CSS(filename=path_css)
|
||||
# ~ html.write_pdf(path_pdf, stylesheets=[css], font_config=font_config)
|
||||
options = {
|
||||
'page-size': 'Letter',
|
||||
'margin-top': '0.50in',
|
||||
'margin-right': '0.50in',
|
||||
'margin-bottom': '0.50in',
|
||||
'margin-left': '0.50in',
|
||||
'encoding': "UTF-8",
|
||||
}
|
||||
|
||||
pdfkit.from_string(data.decode(), path_pdf, options=options, css=css)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def import_employees(rfc):
|
||||
name = '{}_employees.ods'.format(rfc.lower())
|
||||
path = _join(PATH_MEDIA, 'tmp', name)
|
||||
|
@ -1621,15 +1704,20 @@ 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):
|
||||
path = get_path_temp('.qr')
|
||||
def get_qr(data, p=True):
|
||||
qr = pyqrcode.create(data, mode='binary')
|
||||
qr.png(path, scale=7)
|
||||
return path
|
||||
if p:
|
||||
path = get_path_temp('.qr')
|
||||
qr.png(path, scale=7)
|
||||
return path
|
||||
|
||||
buffer = io.BytesIO()
|
||||
qr.png(buffer, scale=8)
|
||||
return base64.b64encode(buffer.getvalue()).decode()
|
||||
|
||||
|
||||
def _get_relacionados(doc, version):
|
||||
|
@ -1881,6 +1969,7 @@ def _timbre(doc, version, values):
|
|||
'sello': '&fe={}'.format(data['sellocfd'][-8:]),
|
||||
}
|
||||
qr_data = '{url}{uuid}{emisor}{receptor}{total}{sello}'.format(**qr_data)
|
||||
|
||||
data['path_cbb'] = get_qr(qr_data)
|
||||
data['cadenaoriginal'] = CADENA.format(**data)
|
||||
return data
|
||||
|
@ -2083,39 +2172,58 @@ def get_date(value, next_day=False):
|
|||
return d
|
||||
|
||||
|
||||
class UpFile(object):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self._init_values()
|
||||
|
||||
def _init_values(self):
|
||||
|
||||
return
|
||||
|
||||
|
||||
def upload_file(rfc, opt, file_obj):
|
||||
if opt == 'emisorlogo':
|
||||
tmp = file_obj.filename.split('.')
|
||||
name = '{}.{}'.format(rfc.lower(), tmp[-1].lower())
|
||||
path = _join(PATH_MEDIA, 'logos', name)
|
||||
elif opt == 'txt_plantilla_factura_32':
|
||||
tmp = file_obj.filename.split('.')
|
||||
ext = tmp[-1].lower()
|
||||
if ext != 'ods':
|
||||
msg = 'Extensión de archivo incorrecta, selecciona un archivo ODS'
|
||||
rfc = rfc.lower()
|
||||
tmp = file_obj.filename.split('.')
|
||||
ext = tmp[-1].lower()
|
||||
|
||||
EXTENSIONS = {
|
||||
'txt_plantilla_factura_32': EXT['ODS'],
|
||||
'txt_plantilla_factura_33': EXT['ODS'],
|
||||
'txt_plantilla_factura_html': EXT['HTML'],
|
||||
'txt_plantilla_factura_css': EXT['CSS'],
|
||||
'txt_plantilla_factura_json': EXT['JSON'],
|
||||
}
|
||||
|
||||
if opt in EXTENSIONS:
|
||||
if ext != EXTENSIONS[opt]:
|
||||
msg = (
|
||||
f"Extensión de archivo incorrecta, "
|
||||
f"selecciona un archivo {EXTENSIONS[opt].upper()}"
|
||||
)
|
||||
return {'status': 'server', 'name': msg, 'ok': False}
|
||||
|
||||
name = '{}_3.2.ods'.format(rfc.lower())
|
||||
path = _join(PATH_MEDIA, 'templates', name)
|
||||
elif opt == 'txt_plantilla_factura_33':
|
||||
tmp = file_obj.filename.split('.')
|
||||
ext = tmp[-1].lower()
|
||||
if ext != 'ods':
|
||||
msg = 'Extensión de archivo incorrecta, selecciona un archivo ODS'
|
||||
return {'status': 'server', 'name': msg, 'ok': False}
|
||||
NAMES = {
|
||||
'txt_plantilla_factura_32': f"{rfc}_3.2.ods",
|
||||
'txt_plantilla_factura_33': f"{rfc}_3.3.ods",
|
||||
'txt_plantilla_factura_html': f"{rfc}_3.3.html",
|
||||
'txt_plantilla_factura_css': f"{rfc}.css",
|
||||
'txt_plantilla_factura_json': f"{rfc}_3.3.json",
|
||||
}
|
||||
name = NAMES[opt]
|
||||
paths = {
|
||||
'txt_plantilla_factura_32': _join(PATHS['USER'], name),
|
||||
'txt_plantilla_factura_33': _join(PATHS['USER'], name),
|
||||
'txt_plantilla_factura_html': _join(PATHS['USER'], name),
|
||||
'txt_plantilla_factura_css': _join(PATHS['CSS'], name),
|
||||
'txt_plantilla_factura_json': _join(PATHS['USER'], name),
|
||||
}
|
||||
if save_file(paths[opt], file_obj.file.read()):
|
||||
return {'status': 'server', 'name': file_obj.filename, 'ok': True}
|
||||
return {'status': 'error', 'ok': False}
|
||||
|
||||
name = '{}_3.3.ods'.format(rfc.lower())
|
||||
path = _join(PATH_MEDIA, 'templates', name)
|
||||
elif opt == 'txt_plantilla_factura_33j':
|
||||
tmp = file_obj.filename.split('.')
|
||||
ext = tmp[-1].lower()
|
||||
if ext != 'json':
|
||||
msg = 'Extensión de archivo incorrecta, selecciona un archivo JSON'
|
||||
return {'status': 'server', 'name': msg, 'ok': False}
|
||||
|
||||
name = '{}_3.3.json'.format(rfc.lower())
|
||||
path = _join(PATH_MEDIA, 'templates', name)
|
||||
elif opt == 'txt_plantilla_ticket':
|
||||
if opt == 'txt_plantilla_ticket':
|
||||
tmp = file_obj.filename.split('.')
|
||||
ext = tmp[-1].lower()
|
||||
if ext != 'ods':
|
||||
|
@ -3648,3 +3756,8 @@ def validate_rfc(value):
|
|||
return ''
|
||||
except:
|
||||
return msg
|
||||
|
||||
|
||||
def parse_xml2(xml_str):
|
||||
return etree.fromstring(xml_str.encode('utf-8'))
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ from middleware import (
|
|||
AuthMiddleware,
|
||||
JSONTranslator,
|
||||
ConnectionMiddleware,
|
||||
static,
|
||||
handle_404
|
||||
)
|
||||
from models.db import StorageEngine
|
||||
|
@ -64,9 +63,6 @@ api.add_route('/satformapago', AppSATFormaPago(db))
|
|||
api.add_route('/socioscb', AppSociosCuentasBanco(db))
|
||||
|
||||
|
||||
# ~ Activa si usas waitress y NO estas usando servidor web
|
||||
# ~ api.add_sink(static, '/static')
|
||||
|
||||
session_options = {
|
||||
'session.type': 'file',
|
||||
'session.cookie_expires': True,
|
||||
|
|
|
@ -8,4 +8,4 @@ threads = 4
|
|||
py-autoreload = 1
|
||||
thunder-lock = true
|
||||
static-map = /static=../static
|
||||
http-timeout = 300
|
||||
http-timeout = 300
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import falcon
|
||||
from controllers import util
|
||||
from models import main
|
||||
from settings import MV, PATH_STATIC
|
||||
from settings import MV
|
||||
|
||||
|
||||
def handle_404(req, resp):
|
||||
|
@ -20,14 +20,14 @@ def get_template(req, resp, resource):
|
|||
resp.body = util.get_template(resource.template, data)
|
||||
|
||||
|
||||
def static(req, res):
|
||||
path = PATH_STATIC + req.path
|
||||
if util.is_file(path):
|
||||
res.content_type = util.get_mimetype(path)
|
||||
res.stream, res.stream_len = util.get_stream(path)
|
||||
res.status = falcon.HTTP_200
|
||||
else:
|
||||
res.status = falcon.HTTP_404
|
||||
# ~ def static(req, res):
|
||||
# ~ path = PATH_STATIC + req.path
|
||||
# ~ if util.is_file(path):
|
||||
# ~ res.content_type = util.get_mimetype(path)
|
||||
# ~ res.stream, res.stream_len = util.get_stream(path)
|
||||
# ~ res.status = falcon.HTTP_200
|
||||
# ~ else:
|
||||
# ~ res.status = falcon.HTTP_404
|
||||
|
||||
|
||||
class AuthMiddleware(object):
|
||||
|
|
|
@ -37,6 +37,12 @@ from settings import log, DEBUG, VERSION, PATH_CP, COMPANIES, PRE, CURRENT_CFDI,
|
|||
DEFAULT_SAT_NOMINA, DECIMALES_TAX, TITLE_APP, MV, DECIMALES_PRECIOS, \
|
||||
DEFAULT_CFDIPAY, CURRENCY_MN
|
||||
|
||||
from settings import (
|
||||
EXT,
|
||||
MXN,
|
||||
PATHS,
|
||||
VALUES_PDF,
|
||||
)
|
||||
|
||||
FORMAT = '{0:.2f}'
|
||||
FORMAT3 = '{0:.3f}'
|
||||
|
@ -97,9 +103,11 @@ def upload_file(rfc, opt, file_obj):
|
|||
else:
|
||||
return Facturas.import_cfdi(xml, sxml)
|
||||
|
||||
if opt == 'emisorlogo':
|
||||
return Emisor.save_logo(file_obj)
|
||||
|
||||
result = util.upload_file(rfc, opt, file_obj)
|
||||
if result['ok']:
|
||||
|
||||
names = ('bdfl', 'employees', 'nomina', 'products', 'invoiceods')
|
||||
if not opt in names:
|
||||
Configuracion.add({opt: file_obj.filename})
|
||||
|
@ -204,12 +212,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':
|
||||
|
@ -303,9 +314,38 @@ class Configuracion(BaseModel):
|
|||
clave = TextField(unique=True)
|
||||
valor = TextField(default='')
|
||||
|
||||
class Meta:
|
||||
order_by = ('clave',)
|
||||
indexes = (
|
||||
(('clave', 'valor'), True),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '{} = {}'.format(self.clave, self.valor)
|
||||
|
||||
def _clean_email(self, values={}):
|
||||
fields = (
|
||||
'correo_asunto',
|
||||
'correo_confirmacion',
|
||||
'correo_contra',
|
||||
'correo_directo',
|
||||
'correo_mensaje',
|
||||
'correo_puerto',
|
||||
'correo_servidor',
|
||||
'correo_ssl',
|
||||
'correo_usuario',
|
||||
)
|
||||
|
||||
q = (Configuracion
|
||||
.update(**{'valor': ''})
|
||||
.where(Configuracion.clave.in_(fields))
|
||||
)
|
||||
result = q.execute()
|
||||
msg = 'Configuración guardada correctamente'
|
||||
if not result:
|
||||
msg = 'No se pudo guardar la configuración'
|
||||
return {'ok': result, 'msg': msg}
|
||||
|
||||
@classmethod
|
||||
def get_bool(cls, key):
|
||||
data = (Configuracion
|
||||
|
@ -460,11 +500,14 @@ class Configuracion(BaseModel):
|
|||
fields = (
|
||||
'txt_plantilla_factura_32',
|
||||
'txt_plantilla_factura_33',
|
||||
'txt_plantilla_factura_33j',
|
||||
'txt_plantilla_factura_html',
|
||||
'txt_plantilla_factura_css',
|
||||
'txt_plantilla_factura_json',
|
||||
'txt_plantilla_nomina1233',
|
||||
'txt_plantilla_pagos10',
|
||||
'txt_plantilla_ticket',
|
||||
'txt_plantilla_donataria',
|
||||
'make_pdf_from',
|
||||
)
|
||||
data = (Configuracion
|
||||
.select()
|
||||
|
@ -488,6 +531,10 @@ class Configuracion(BaseModel):
|
|||
|
||||
@classmethod
|
||||
def add(cls, values):
|
||||
opt = values.pop('opt', '')
|
||||
if opt:
|
||||
return getattr(cls, f'_{opt}')(cls, values)
|
||||
|
||||
# ~ print (values)
|
||||
try:
|
||||
for k, v in values.items():
|
||||
|
@ -505,12 +552,6 @@ class Configuracion(BaseModel):
|
|||
q = Configuracion.delete().where(Configuracion.clave==key)
|
||||
return bool(q.execute())
|
||||
|
||||
class Meta:
|
||||
order_by = ('clave',)
|
||||
indexes = (
|
||||
(('clave', 'valor'), True),
|
||||
)
|
||||
|
||||
|
||||
class Tags(BaseModel):
|
||||
tag = TextField(index=True, unique=True)
|
||||
|
@ -762,6 +803,10 @@ class SATRegimenes(BaseModel):
|
|||
def __str__(self):
|
||||
return '{} ({})'.format(self.name, self.key)
|
||||
|
||||
@classmethod
|
||||
def get_by_key(cls, key):
|
||||
return SATRegimenes.get(SATRegimenes.key==key)
|
||||
|
||||
@classmethod
|
||||
def get_(cls, ids):
|
||||
if isinstance(ids, int):
|
||||
|
@ -814,12 +859,30 @@ class Emisor(BaseModel):
|
|||
registro_patronal = TextField(default='')
|
||||
regimenes = ManyToManyField(SATRegimenes, related_name='emisores')
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
t = '{} ({})'
|
||||
return t.format(self.nombre, self.rfc)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
@classmethod
|
||||
def save_logo(cls, file_obj):
|
||||
result = {'status': 'error', 'ok': False}
|
||||
name = file_obj.filename.split('.')
|
||||
if name[-1].lower() != EXT['PNG']:
|
||||
return result
|
||||
|
||||
emisor = Emisor.select()[0]
|
||||
rfc = emisor.rfc.lower()
|
||||
name = f'{rfc}.png'
|
||||
path = util._join(PATHS['IMG'], name)
|
||||
if util.save_file(path, file_obj.file.read()):
|
||||
emisor.logo = file_obj.filename
|
||||
emisor.save()
|
||||
result = {'status': 'server', 'name': file_obj.filename, 'ok': True}
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_(cls, rfc):
|
||||
|
@ -847,6 +910,7 @@ class Emisor(BaseModel):
|
|||
'emisor_telefono': obj.telefono,
|
||||
'emisor_correo': obj.correo,
|
||||
'emisor_web': obj.web,
|
||||
'emisor_logo': obj.logo,
|
||||
'es_escuela': obj.es_escuela,
|
||||
'es_ong': obj.es_ong,
|
||||
'ong_autorizacion': obj.autorizacion,
|
||||
|
@ -1366,6 +1430,10 @@ class SATMonedas(BaseModel):
|
|||
def __str__(self):
|
||||
return 'Moneda: ({}) {}'.format(self.key, self.name)
|
||||
|
||||
@classmethod
|
||||
def get_by_key(cls, key):
|
||||
return SATMonedas.get(SATMonedas.key==key)
|
||||
|
||||
@classmethod
|
||||
def get_multi_currency(cls):
|
||||
count_currencies = len(cls.get_activos())
|
||||
|
@ -1946,6 +2014,10 @@ class SATUsoCfdi(BaseModel):
|
|||
def __str__(self):
|
||||
return 'Uso del CFDI: {} ({})'.format(self.name, self.key)
|
||||
|
||||
@classmethod
|
||||
def get_by_key(cls, key):
|
||||
return SATUsoCfdi.get(SATUsoCfdi.key==key)
|
||||
|
||||
@classmethod
|
||||
def actualizar(self, values):
|
||||
id = int(values['id'])
|
||||
|
@ -3647,6 +3719,7 @@ class Facturas(BaseModel):
|
|||
values['receptor'] = {}
|
||||
for k, v in receptor.items():
|
||||
values['receptor'][k] = v
|
||||
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
|
@ -3661,9 +3734,10 @@ class Facturas(BaseModel):
|
|||
if obj.uuid is None:
|
||||
return b'', name
|
||||
|
||||
pdf_from = Configuracion.get_('make_pdf_from') or '1'
|
||||
values = cls._get_not_in_xml(cls, obj, emisor)
|
||||
data = util.get_data_from_xml(obj, values)
|
||||
doc = util.to_pdf(data, emisor.rfc)
|
||||
doc = util.to_pdf(data, emisor.rfc, pdf_from=pdf_from)
|
||||
|
||||
if sync:
|
||||
target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/')
|
||||
|
@ -3671,6 +3745,137 @@ class Facturas(BaseModel):
|
|||
|
||||
return doc, name
|
||||
|
||||
def _get_concepto(self, node, currency):
|
||||
d = util.get_dict(node.attrib)
|
||||
concepto = {
|
||||
'clave': f"{d['noidentificacion']}<BR>({d['claveprodserv']})",
|
||||
'descripcion': self._get_description(self, node, 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),
|
||||
}
|
||||
return concepto
|
||||
|
||||
def _get_description(self, node, data):
|
||||
return data['descripcion']
|
||||
|
||||
def _get_tax(self, node, data, currency):
|
||||
type_tax = 'Traslado'
|
||||
if 'Retenciones' in node.tag:
|
||||
type_tax = 'Retención'
|
||||
|
||||
for n in node:
|
||||
d = util.get_dict(n.attrib)
|
||||
name = VALUES_PDF['TAX'].get(d['impuesto'])
|
||||
tasa = f"{float(d['tasaocuota']):.2f}"
|
||||
title = f"{type_tax} {name} {tasa}"
|
||||
importe = util.format_currency(d['importe'], currency)
|
||||
data['totales'].append((title, importe))
|
||||
return
|
||||
|
||||
def _get_others_values(self, invoice, emisor):
|
||||
data = {
|
||||
'rfc': emisor.rfc.lower(),
|
||||
'version': invoice.version,
|
||||
'cancelada': VALUES_PDF['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'] = \
|
||||
VALUES_PDF['TYPE'][data['cfdi_tipodecomprobante']]
|
||||
|
||||
if data.get('cfdi_formapago', ''):
|
||||
data['cfdi_formapago'] = str(
|
||||
SATFormaPago.get_by_key(data['cfdi_formapago']))
|
||||
if data.get('cfdi_metodopago', ''):
|
||||
data['cfdi_metodopago'] = 'Método de Pago: ({}) {}'.format(
|
||||
data['cfdi_metodopago'],
|
||||
VALUES_PDF['METHOD'][data['cfdi_metodopago']])
|
||||
|
||||
data['cfdi_moneda'] = str(SATMonedas.get_by_key(currency))
|
||||
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 = {f'emisor_{k.lower()}': v for k, v in node.attrib.items()}
|
||||
data.update(d)
|
||||
elif 'Receptor' in node.tag:
|
||||
d = {f'receptor_{k.lower()}': v for k, v in node.attrib.items()}
|
||||
data.update(d)
|
||||
elif 'Conceptos' in node.tag:
|
||||
data['conceptos'] = []
|
||||
for subnode in node:
|
||||
concepto = self._get_concepto(self, subnode, currency)
|
||||
data['conceptos'].append(concepto)
|
||||
elif 'Impuestos' in node.tag:
|
||||
for subnode in node:
|
||||
self._get_tax(self, subnode, data, currency)
|
||||
elif 'Complemento' in node.tag:
|
||||
for sn in node:
|
||||
if 'TimbreFiscalDigital' in sn.tag:
|
||||
d = {f'timbre_{k.lower()}': v for k, v in sn.attrib.items()}
|
||||
data.update(d)
|
||||
|
||||
data['emisor_logo'] = data['emisor_rfc'].lower()
|
||||
data['emisor_regimenfiscal'] = str(
|
||||
SATRegimenes.get_by_key(data['emisor_regimenfiscal']))
|
||||
data['receptor_usocfdi'] = str(
|
||||
SATUsoCfdi.get_by_key(data['receptor_usocfdi']))
|
||||
|
||||
data['totales'].append((
|
||||
'Total', util.format_currency(data['cfdi_total'], currency)))
|
||||
|
||||
data['timbre_cadenaoriginal'] = (
|
||||
f"||{data['timbre_version']}|"
|
||||
f"{data['timbre_uuid']}|"
|
||||
f"{data['timbre_fechatimbrado']}|"
|
||||
f"{data['timbre_sellocfd']}|"
|
||||
f"{data['timbre_nocertificadosat']}||"
|
||||
)
|
||||
|
||||
qr_data = (
|
||||
f"https://verificacfdi.facturaelectronica.sat.gob.mx/"
|
||||
f"default.aspx?&id={data['timbre_uuid']}&re={data['emisor_rfc']}"
|
||||
f"&rr={data['receptor_rfc']}&tt={data['cfdi_total']}"
|
||||
f"&fe={data['cfdi_sello'][-8:]}"
|
||||
)
|
||||
cbb = util.get_qr(qr_data, False)
|
||||
data['cbb'] = f'data:image/png;base64,{cbb}'
|
||||
|
||||
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)
|
||||
# ~ util.html_to_pdf(doc)
|
||||
|
||||
return doc, name
|
||||
|
||||
@classmethod
|
||||
def get_ods(cls, id, rfc):
|
||||
try:
|
||||
|
|
|
@ -47,13 +47,21 @@ except ImportError:
|
|||
|
||||
|
||||
DEBUG = DEBUG
|
||||
VERSION = '1.25.0'
|
||||
VERSION = '1.26.0'
|
||||
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
|
||||
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
|
||||
|
||||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
PATH_STATIC = os.path.abspath(os.path.join(BASE_DIR, '..'))
|
||||
path_static = os.path.abspath(os.path.join(BASE_DIR, '..', 'static'))
|
||||
path_docs = os.path.abspath(os.path.join(BASE_DIR, '..', 'docs'))
|
||||
|
||||
path_css = os.path.join(path_static, 'css')
|
||||
path_img = os.path.join(path_static, 'img')
|
||||
path_user_template = os.path.join(path_docs, 'templates')
|
||||
|
||||
# ~ PATH_STATIC = os.path.abspath(os.path.join(BASE_DIR, '..'))
|
||||
|
||||
PATH_TEMPLATES = os.path.abspath(os.path.join(BASE_DIR, '..', 'templates'))
|
||||
PATH_MEDIA = os.path.abspath(os.path.join(BASE_DIR, '..', 'docs'))
|
||||
|
||||
|
@ -74,7 +82,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')
|
||||
|
||||
|
@ -182,3 +193,29 @@ DEFAULT_SAT_NOMINA = {
|
|||
API = 'https://api.empresalibre.net{}'
|
||||
|
||||
CURRENCY_MN = 'MXN'
|
||||
|
||||
# ~ v2
|
||||
EXT = {
|
||||
'CSS': 'css',
|
||||
'HTML': 'html',
|
||||
'ODS': 'ods',
|
||||
'PNG': 'png',
|
||||
'JSON': 'json',
|
||||
}
|
||||
MXN = 'MXN'
|
||||
PATHS = {
|
||||
'STATIC': path_static,
|
||||
'CSS': path_css,
|
||||
'IMG': path_img,
|
||||
'DOCS': path_docs,
|
||||
'USER': path_user_template,
|
||||
}
|
||||
VALUES_PDF = {
|
||||
'CANCEL': {True: 'inline', False: 'none'},
|
||||
'TYPE': {'I': 'Ingreso', 'E': 'Egreso', 'T': 'Traslado'},
|
||||
'TAX': {'001': 'ISR', '002': 'IVA', '003': 'IEPS'},
|
||||
'METHOD': {
|
||||
'PUE': 'Pago en una sola exhibición',
|
||||
'PPD': 'Pago en parcialidades o diferido',
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,519 @@
|
|||
@page{
|
||||
size: Letter;
|
||||
margin: 0.5cm;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
thead {display: table-header-group;}
|
||||
body {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
/*
|
||||
font-family: 'Avenir Next';
|
||||
*/
|
||||
}
|
||||
|
||||
#plantilla {
|
||||
border: 1px solid #fff;
|
||||
border-color: rgb(204,204,204);
|
||||
background: #fff;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 19.5cm;
|
||||
height: 25.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: 60%;
|
||||
}
|
||||
|
||||
.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: 9px;
|
||||
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: 5px 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: 14px;
|
||||
}
|
||||
|
||||
.notas{
|
||||
float: left;
|
||||
font-size: 11px;
|
||||
margin: 20px 0;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.formapago-metodopago, .moneda-tipocambio, .tiporelacion, .relacionados{
|
||||
float: left;
|
||||
font-size: 11px;
|
||||
line-height: 12px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.factura-info{
|
||||
float: left;
|
||||
font-size: 11px;
|
||||
line-height: 13px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.tipocomite, .tipoproceso, .idcontabilidad{
|
||||
float: right;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.sello{
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.sello .cbb{
|
||||
border: 1px solid #000;
|
||||
height: auto;
|
||||
margin-top: 10px;
|
||||
margin-right: 1px;
|
||||
width: 18%;
|
||||
}
|
||||
|
||||
.sello .cadenas-sello{
|
||||
color: #000;
|
||||
float: right;
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
line-height: 15px;
|
||||
width: 79%;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.sello .cadenas-sello .cadena{
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.sello .cadenas-sello .cadena div{
|
||||
font-weight: normal;
|
||||
background-color: #dbc5c6;
|
||||
color: #7d1916;
|
||||
}
|
||||
|
||||
.cadena-original{
|
||||
color: #000;
|
||||
float: right;
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
line-height: 15px;
|
||||
width: 99%;
|
||||
margin: 5px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.cadena-original div{
|
||||
font-weight: normal;
|
||||
background-color: #dbc5c6;
|
||||
color: #7d1916;
|
||||
}
|
||||
|
||||
.rfc-pac{
|
||||
background-color: #dbc5c6;
|
||||
color: #7d1916;
|
||||
font-size: 10px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
margin: 5px;
|
||||
}
|
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 |
|
@ -37,6 +37,7 @@ var controllers = {
|
|||
$$('grid_folios').attachEvent('onItemClick', grid_folios_click)
|
||||
$$('cmd_probar_correo').attachEvent('onItemClick', cmd_probar_correo_click)
|
||||
$$('cmd_guardar_correo').attachEvent('onItemClick', cmd_guardar_correo_click)
|
||||
$$('cmd_clean_email').attachEvent('onItemClick', cmd_clean_email_click)
|
||||
$$('emisor_logo').attachEvent('onItemClick', emisor_logo_click)
|
||||
$$('cmd_emisor_agregar_cuenta').attachEvent('onItemClick', cmd_emisor_agregar_cuenta_click)
|
||||
$$('cmd_emisor_eliminar_cuenta').attachEvent('onItemClick', cmd_emisor_eliminar_cuenta_click)
|
||||
|
@ -69,11 +70,15 @@ var controllers = {
|
|||
tb_options.attachEvent('onChange', tab_options_change)
|
||||
$$('txt_plantilla_factura_32').attachEvent('onItemClick', txt_plantilla_factura_32_click)
|
||||
$$('txt_plantilla_factura_33').attachEvent('onItemClick', txt_plantilla_factura_33_click)
|
||||
$$('txt_plantilla_factura_33j').attachEvent('onItemClick', txt_plantilla_factura_33j_click)
|
||||
$$('txt_plantilla_factura_html').attachEvent('onItemClick', txt_plantilla_factura_html_click)
|
||||
$$('txt_plantilla_factura_json').attachEvent('onItemClick', txt_plantilla_factura_json_click)
|
||||
$$('txt_plantilla_factura_css').attachEvent('onItemClick', txt_plantilla_factura_css_click)
|
||||
$$('txt_plantilla_ticket').attachEvent('onItemClick', txt_plantilla_ticket_click)
|
||||
$$('txt_plantilla_donataria').attachEvent('onItemClick', txt_plantilla_donataria_click)
|
||||
$$('txt_plantilla_nomina1233').attachEvent('onItemClick', txt_plantilla_nomina1233_click)
|
||||
$$('txt_plantilla_pagos10').attachEvent('onItemClick', txt_plantilla_pagos10_click)
|
||||
$$('make_pdf_from').attachEvent('onChange', opt_make_pdf_from_on_change)
|
||||
|
||||
//~ Partners
|
||||
$$('chk_config_change_balance_partner').attachEvent('onItemClick', chk_config_item_click)
|
||||
|
||||
|
@ -840,6 +845,46 @@ function cmd_guardar_correo_click(){
|
|||
}
|
||||
|
||||
|
||||
function clean_config_mail(){
|
||||
var form = $$('form_correo')
|
||||
|
||||
webix.ajax().sync().post('/config', {'opt': 'clean_email'}, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al guardar la configuración'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json();
|
||||
if (values.ok){
|
||||
form.setValues({})
|
||||
msg_ok(values.msg)
|
||||
}else{
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function cmd_clean_email_click(){
|
||||
msg = '¿Estás seguro de limpiar todos los datos?<BR><BR>'
|
||||
msg += 'ESTA ACCIÓN NO SE PUEDE DESHACER'
|
||||
|
||||
webix.confirm({
|
||||
title: 'Configuración de correo',
|
||||
ok: 'Si',
|
||||
cancel: 'No',
|
||||
type: 'confirm-error',
|
||||
text: msg,
|
||||
callback:function(result){
|
||||
if(result){
|
||||
clean_config_mail()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function emisor_logo_click(id, e){
|
||||
|
||||
var w = webix.ui({
|
||||
|
@ -978,13 +1023,98 @@ function txt_plantilla_factura_32_click(e){
|
|||
}
|
||||
|
||||
|
||||
function txt_plantilla_factura_33j_click(e){
|
||||
function txt_plantilla_factura_html_click(e){
|
||||
|
||||
var body_elements = [
|
||||
{cols: [{width: 100}, {view: 'uploader', id: 'up_template', autosend: true, link: 'lst_files',
|
||||
value: 'Seleccionar archivo', upload: '/files/txt_plantilla_factura_33j',
|
||||
width: 200}, {width: 100}]},
|
||||
{view: 'list', id: 'lst_files', type: 'uploader', autoheight:true,
|
||||
{cols: [
|
||||
{width: 100},
|
||||
{view: 'uploader', id: 'up_template', autosend: true,
|
||||
link: 'lst_files', value: 'Seleccionar archivo',
|
||||
upload: '/files/txt_plantilla_factura_html', width: 200},
|
||||
{width: 100}]},
|
||||
{view: 'list', id: 'lst_files', type: 'uploader', autoheight: true,
|
||||
borderless: true},
|
||||
{},
|
||||
{cols: [{}, {view: 'button', label: 'Cerrar', autowidth: true,
|
||||
click:("$$('win_template').close();")}, {}]}
|
||||
]
|
||||
|
||||
var w = webix.ui({
|
||||
view: 'window',
|
||||
id: 'win_template',
|
||||
modal: true,
|
||||
position: 'center',
|
||||
head: 'Subir Plantilla 3.3 HTML',
|
||||
body: {
|
||||
view: 'form',
|
||||
elements: body_elements,
|
||||
}
|
||||
})
|
||||
|
||||
w.show()
|
||||
|
||||
$$('up_template').attachEvent('onUploadComplete', function(response){
|
||||
if(response.ok){
|
||||
$$('txt_plantilla_factura_html').setValue(response.name)
|
||||
msg_ok('Plantilla cargada correctamente')
|
||||
}else{
|
||||
msg_error(response.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function txt_plantilla_factura_css_click(e){
|
||||
|
||||
var body_elements = [
|
||||
{cols: [
|
||||
{width: 100},
|
||||
{view: 'uploader', id: 'up_template', autosend: true,
|
||||
link: 'lst_files', value: 'Seleccionar archivo',
|
||||
upload: '/files/txt_plantilla_factura_css', width: 200},
|
||||
{width: 100}]},
|
||||
{view: 'list', id: 'lst_files', type: 'uploader', autoheight: true,
|
||||
borderless: true},
|
||||
{},
|
||||
{cols: [{}, {view: 'button', label: 'Cerrar', autowidth: true,
|
||||
click:("$$('win_template').close();")}, {}]}
|
||||
]
|
||||
|
||||
var w = webix.ui({
|
||||
view: 'window',
|
||||
id: 'win_template',
|
||||
modal: true,
|
||||
position: 'center',
|
||||
head: 'Subir Archivo de Estilos CSS',
|
||||
body: {
|
||||
view: 'form',
|
||||
elements: body_elements,
|
||||
}
|
||||
})
|
||||
|
||||
w.show()
|
||||
|
||||
$$('up_template').attachEvent('onUploadComplete', function(response){
|
||||
if(response.ok){
|
||||
$$('txt_plantilla_factura_css').setValue(response.name)
|
||||
msg_ok('Plantilla cargada correctamente')
|
||||
}else{
|
||||
msg_error(response.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function txt_plantilla_factura_json_click(e){
|
||||
|
||||
var body_elements = [
|
||||
{cols: [
|
||||
{width: 100},
|
||||
{view: 'uploader', id: 'up_template', autosend: true,
|
||||
link: 'lst_files', value: 'Seleccionar archivo',
|
||||
upload: '/files/txt_plantilla_factura_json', width: 200},
|
||||
{width: 100}]},
|
||||
{view: 'list', id: 'lst_files', type: 'uploader', autoheight: true,
|
||||
borderless: true},
|
||||
{},
|
||||
{cols: [{}, {view: 'button', label: 'Cerrar', autowidth: true,
|
||||
|
@ -1007,7 +1137,7 @@ function txt_plantilla_factura_33j_click(e){
|
|||
|
||||
$$('up_template').attachEvent('onUploadComplete', function(response){
|
||||
if(response.ok){
|
||||
$$('txt_plantilla_factura_33j').setValue(response.name)
|
||||
$$('txt_plantilla_factura_json').setValue(response.name)
|
||||
msg_ok('Plantilla cargada correctamente')
|
||||
}else{
|
||||
msg_error(response.name)
|
||||
|
@ -2344,3 +2474,24 @@ function grid_admin_bancos_before_edit_stop(state, editor){
|
|||
admin_sat_bank_update_rfc(row.id, state.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function opt_make_pdf_from_on_change(new_value, old_value){
|
||||
var values = {'make_pdf_from': new_value}
|
||||
|
||||
webix.ajax().sync().post('/config', values, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al guardar la configuración'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json();
|
||||
if (!values.ok){
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -204,7 +204,7 @@ var emisor_otros_datos= [
|
|||
{template: 'Generales', type: 'section'},
|
||||
{cols: [
|
||||
{view: 'search', id: 'emisor_logo', icon: 'file-image-o',
|
||||
name: 'emisor_logo', label: 'Logotipo: '},
|
||||
name: 'emisor_logo', label: 'Logotipo: ', placeholder: 'Solo formato PNG'},
|
||||
{view: 'text', id: 'emisor_nombre_comercial',
|
||||
name: 'emisor_nombre_comercial', label: 'Nombre comercial: '},
|
||||
]},
|
||||
|
@ -244,7 +244,7 @@ var emisor_otros_datos= [
|
|||
{view: 'text', id: 'token_timbrado',
|
||||
name: 'token_timbrado', label: 'Token de Timbrado: '},
|
||||
{view: 'text', id: 'token_soporte',
|
||||
name: 'token_soporte', label: 'Token para Respaldos: '},
|
||||
name: 'token_soporte', label: 'Token de Soporte: '},
|
||||
]
|
||||
|
||||
|
||||
|
@ -511,9 +511,12 @@ var emisor_correo = [
|
|||
{cols: [{},
|
||||
{view: 'button', id: 'cmd_probar_correo', label: 'Probar Configuración',
|
||||
autowidth: true, type: 'form'},
|
||||
{maxWidth: 100},
|
||||
{maxWidth: 50},
|
||||
{view: 'button', id: 'cmd_guardar_correo', label: 'Guardar Configuración',
|
||||
autowidth: true, type: 'form'},
|
||||
{maxWidth: 50},
|
||||
{view: 'button', id: 'cmd_clean_email', label: 'Limpiar Configuración',
|
||||
autowidth: true, type: 'form'},
|
||||
{}]}
|
||||
]
|
||||
|
||||
|
@ -579,33 +582,49 @@ var form_correo = {
|
|||
}
|
||||
|
||||
|
||||
var type_make_pdf = [
|
||||
{id: 1, value: 'ODS'},
|
||||
{id: 2, value: 'JSON'},
|
||||
]
|
||||
|
||||
|
||||
var options_templates = [
|
||||
{maxHeight: 15},
|
||||
{cols: [{maxWidth: 15},
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'search', id: 'txt_plantilla_factura_32', name: 'plantilla_factura_32',
|
||||
label: 'Plantilla Factura v3.2 (ODS): ', labelPosition: 'top',
|
||||
icon: 'file'}, {}]},
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 15},
|
||||
icon: 'file'}, {maxWidth: 25},
|
||||
{view: 'search', id: 'txt_plantilla_factura_33', labelPosition: 'top',
|
||||
label: 'Plantilla Factura v3.3 (ODS): ', icon: 'file'}, {}]},
|
||||
label: 'Plantilla Factura v3.3 (ODS): ', icon: 'file'},
|
||||
{maxWidth: 20} ]},
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 15},
|
||||
{view: 'search', id: 'txt_plantilla_factura_33j', name: 'plantilla_factura_33j',
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'search', id: 'txt_plantilla_factura_html', name: 'plantilla_factura_html',
|
||||
label: 'Plantilla Factura v3.3 (HTML): ', labelPosition: 'top',
|
||||
icon: 'file'}, {maxWidth: 25},
|
||||
{view: 'search', id: 'txt_plantilla_factura_css', name: 'plantilla_factura_css',
|
||||
label: 'Archivo de estilos (CSS): ', labelPosition: 'top',
|
||||
icon: 'file'}, {maxWidth: 20} ]},
|
||||
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'search', id: 'txt_plantilla_factura_json', name: 'plantilla_factura_json',
|
||||
label: 'Plantilla Factura v3.3 (JSON): ', labelPosition: 'top',
|
||||
icon: 'file'}, {}]},
|
||||
icon: 'file'}, {maxWidth: 25},
|
||||
{}, {maxWidth: 20} ]},
|
||||
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 15},
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'search', id: 'txt_plantilla_nomina1233', name: 'plantilla_nomina1233',
|
||||
label: 'Plantilla Nomina v1.2 - Cfdi 3.3 (ODS): ', labelPosition: 'top',
|
||||
icon: 'file'}, {}]},
|
||||
icon: 'file'}, {maxWidth: 40}, {}]},
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 15},
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'search', id: 'txt_plantilla_pagos10', name: 'plantilla_pagos10',
|
||||
label: 'Plantilla Factura de Pagos v1.0 - Cfdi 3.3 (ODS): ',
|
||||
labelPosition: 'top', icon: 'file'}, {}]},
|
||||
labelPosition: 'top', icon: 'file'}, {maxWidth: 40}, {}]},
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 15},
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'search', id: 'txt_plantilla_ticket', name: 'plantilla_ticket',
|
||||
label: 'Plantilla para Tickets (ODS): ', labelPosition: 'top',
|
||||
icon: 'file'},
|
||||
|
@ -614,6 +633,10 @@ var options_templates = [
|
|||
icon: 'file'},
|
||||
{}]},
|
||||
{maxHeight: 20},
|
||||
{cols: [{maxWidth: 20},
|
||||
{view: 'radio', id: 'make_pdf_from', name: 'make_pdf_from', labelWidth: 150,
|
||||
label: 'Generar PDF desde: ', value: 1, options: type_make_pdf},
|
||||
{}]},
|
||||
{}]
|
||||
|
||||
|
||||
|
|
|
@ -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,204 @@
|
|||
<!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 class="condiciones-pago">
|
||||
${cfdi_condicionespago}
|
||||
</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="tiporelacion">
|
||||
TIPO de ${cfdi_tiporelacion}
|
||||
</div>
|
||||
<div class="relacionados">
|
||||
RELACIONADS${cfdi_relacionados}
|
||||
NOFzxd
|
||||
</div>
|
||||
<div class="notas">
|
||||
NOTAS ${cfdi_notas}
|
||||
</div>
|
||||
-->
|
||||
<div class="factura-info">
|
||||
${cfdi_condicionespago}<BR>
|
||||
${cfdi_formapago}<BR>
|
||||
${cfdi_metodopago}<BR>
|
||||
${cfdi_moneda}, Tipo de Cambio: ${cfdi_tipocambio}<BR>
|
||||
${cfdi_tiporelacion}<BR>
|
||||
${cfdi_relacionados}<BR>
|
||||
${cfdi_notas}
|
||||
</div>
|
||||
</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="tipocomite">
|
||||
{ine.tipocomite}
|
||||
</div>
|
||||
<div class="tipoproceso">
|
||||
{ine.tipoproceso}
|
||||
</div>
|
||||
<div class="idcontabilidad">
|
||||
{ine.idcontabilidad}
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="clear"></div>
|
||||
<div class="sello">
|
||||
<img id="id_cbb" class="cbb" src="${cbb}" />
|
||||
<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="clear"></div>
|
||||
<div class="cadena-original">
|
||||
Cadena original del complemento de certificación digital del SAT:
|
||||
<div>
|
||||
${timbre_cadenaoriginal}
|
||||
</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>
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"colores": {"piepagina": "darkred"},
|
||||
"encabezado": {
|
||||
"emisor": {
|
||||
"direccion": {
|
||||
|
@ -208,10 +209,9 @@
|
|||
"estilo": {"name": "tipocambio", "fontName": "Helvetica",
|
||||
"fontSize": 7, "alignment": 0, "textColor": "black"}
|
||||
},
|
||||
"leyenda": {
|
||||
"estilo": {"name": "leyenda", "fontName": "Helvetica-Bold",
|
||||
"fontSize": 6, "alignment": 1, "textColor": "black",
|
||||
"spaceBefore": 0.2}
|
||||
"notas": {
|
||||
"estilo": {"name": "notas", "fontName": "Helvetica",
|
||||
"fontSize": 7, "alignment": 0, "textColor": "black"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|