diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index f681622..866b155 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -16,9 +16,11 @@
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see
({d['claveprodserv']})",
+ 'descripcion': self._get_description(self, node, d),
+ 'unidad': f"{d['unidad']}
({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_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',
- }
+ 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 = FORMAT.format(float(d['tasaocuota']))
+ 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': v_cancel.get(invoice.cancelada),
+ 'cancelada': VALUES_PDF['CANCEL'].get(invoice.cancelada),
'cfdi_notas': invoice.notas,
}
@@ -3704,17 +3757,18 @@ class Facturas(BaseModel):
data.update({f'cfdi_{k.lower()}': v for k, v in d.items()})
- data['cfdi_tipodecomprobante'] = v_type[data['cfdi_tipodecomprobante']]
+ data['cfdi_tipodecomprobante'] = \
+ VALUES_PDF['TYPE'][data['cfdi_tipodecomprobante']]
if data.get('cfdi_formapago', ''):
- obj = SATFormaPago.get(SATFormaPago.key==invoice.forma_pago)
- data['cfdi_formapago'] = str(obj)
+ 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'], v_method[data['cfdi_metodopago']])
+ data['cfdi_metodopago'],
+ VALUES_PDF['METHOD'][data['cfdi_metodopago']])
- obj = SATMonedas.get(SATMonedas.key==currency)
- data['cfdi_moneda'] = str(obj)
+ 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(
@@ -3725,60 +3779,30 @@ class Facturas(BaseModel):
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()}
- )
+ d = {f'emisor_{k.lower()}': v for k, v in node.attrib.items()}
+ data.update(d)
elif 'Receptor' in node.tag:
- d = util.get_dict(node.attrib)
- data.update(
- {f'receptor_{k.lower()}': v for k, v in d.items()}
- )
+ 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:
- d = util.get_dict(subnode.attrib)
- concepto = {
- 'clave': f"{d['noidentificacion']}
({d['claveprodserv']})",
- 'descripcion': self._get_description(self, subnode, d),
- 'unidad': f"{d['unidad']}
({d['claveunidad']})",
- 'cantidad': d['cantidad'],
- 'valorunitario': util.format_currency(d['valorunitario'], currency),
- 'importe': util.format_currency(d['importe'], currency),
- }
+ concepto = self._get_concepto(self, subnode, 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))
+ self._get_tax(self, subnode, data, currency)
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()}
- )
+ 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)
- 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['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)))
@@ -3786,7 +3810,16 @@ class Facturas(BaseModel):
data['timbre_cadenaoriginal'] = f"""||{data['timbre_version']}|
{data['timbre_uuid']}|{data['timbre_fechatimbrado']}|
{data['timbre_sellocfd']}|{data['timbre_nocertificadosat']}||"""
- # ~ print(data)
+
+ 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
diff --git a/source/app/settings.py b/source/app/settings.py
index 6897b81..13c166c 100644
--- a/source/app/settings.py
+++ b/source/app/settings.py
@@ -47,13 +47,15 @@ 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_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'))
@@ -185,3 +187,21 @@ DEFAULT_SAT_NOMINA = {
API = 'https://api.empresalibre.net{}'
CURRENCY_MN = 'MXN'
+
+# ~ v2
+EXT = {
+ 'PNG': 'png',
+}
+MXN = 'MXN'
+PATHS = {
+ 'STATIC': path_static,
+}
+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',
+ },
+}
diff --git a/source/static/css/estilos.css b/source/static/css/invoice.css
similarity index 95%
rename from source/static/css/estilos.css
rename to source/static/css/invoice.css
index 0dbd2a0..788ac7c 100644
--- a/source/static/css/estilos.css
+++ b/source/static/css/invoice.css
@@ -129,11 +129,12 @@ table.subtotal td{
}
body {
- background-color: rgb(204,204,204);
font-family: 'Avenir Next';
}
#plantilla {
+ border: 1px solid #fff;
+ border-color: rgb(204,204,204);
background: #fff;
display: block;
margin: 0 auto;
@@ -435,26 +436,26 @@ table.subtotal td{
float: left;
font-size: 10px;
line-height: 12px;
- width: 85%;
+ width: 80%;
}
.tipocomite, .tipoproceso, .idcontabilidad{
float: right;
font-size: 10px;
line-height: 12px;
- width: 30%;
+ width: 20%;
}
.sello{
- margin: 20px 0;
+ margin: 10px 0;
}
.sello .cbb{
border: 1px solid #000;
height: auto;
- margin-top: 20px;
- margin-right: 5%;
- width: 15%;
+ margin-top: 10px;
+ margin-right: 1%;
+ width: 18%;
}
.sello .cadenas-sello{
@@ -462,7 +463,7 @@ table.subtotal td{
float: right;
font-size: 8px;
font-weight: bold;
- line-height: 16px;
+ line-height: 15px;
width: 79%;
margin-left: 2px;
margin-right: 2px;
@@ -479,20 +480,17 @@ table.subtotal td{
color: #7d1916;
}
-.sello .cadena-original{
+.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;
+ line-height: 15px;
+ width: 99%;
+ margin: 5px;
word-break: break-all;
}
-
-.sello .cadena-original div{
+.cadena-original div{
font-weight: normal;
background-color: #dbc5c6;
color: #7d1916;
@@ -502,7 +500,7 @@ table.subtotal td{
background-color: #dbc5c6;
color: #7d1916;
font-size: 10px;
- line-height: 16px;
+ line-height: 15px;
text-align: center;
- margin-right: 2px;
+ margin: 5px;
}
diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js
index 30fd7e7..c70d80c 100644
--- a/source/static/js/ui/admin.js
+++ b/source/static/js/ui/admin.js
@@ -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: '},
]
diff --git a/source/templates/plantilla_factura.html b/source/templates/plantilla_factura.html
index 84639cb..c66689a 100644
--- a/source/templates/plantilla_factura.html
+++ b/source/templates/plantilla_factura.html
@@ -2,10 +2,10 @@