diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d58d3c..f2c5570 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,29 @@
+
+v 1.33.0 [22-ene-2020]
+----------------------
+ - Mejora: Cambio del mensaje para cuando se intenta dar de alta un cliente ya existente.
+ - Mejora: Solo los admins pueden ver la nómina.
+ - Se agrega un segundo PAC
+ - Se actualizan los catálogos del SAT
+
+* IMPORTANTE:
+Es necesario actualizar los catálogos del SAT
+```
+git pull origin master
+
+cd source/app/models
+
+python main.py -bk
+
+python main.py -us
+```
+
+
+v 1.32.0 [05-ene-2020]
+----------------------
+ - Mejora: Recuperar facturas no aceptadas para cancelación por el receptor
+
+
v 1.31.2 [28-oct-2019]
----------------------
- Error: Al generar PDF con tags en las series
diff --git a/VERSION b/VERSION
index 359c410..7aa332e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.32.0
+1.33.0
diff --git a/source/app/controllers/comercio/__init__.py b/source/app/controllers/comercio/__init__.py
new file mode 100644
index 0000000..195aadd
--- /dev/null
+++ b/source/app/controllers/comercio/__init__.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python3
+
+from .comercio import PACComercioDigital
diff --git a/source/app/controllers/comercio/comercio.py b/source/app/controllers/comercio/comercio.py
new file mode 100644
index 0000000..a6e625b
--- /dev/null
+++ b/source/app/controllers/comercio/comercio.py
@@ -0,0 +1,338 @@
+#!/usr/bin/env python
+# ~
+# ~ PAC
+# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] net
+# ~
+# ~ This program is free software: you can redistribute it and/or modify
+# ~ it under the terms of the GNU General Public License as published by
+# ~ the Free Software Foundation, either version 3 of the License, or
+# ~ (at your option) any later version.
+# ~
+# ~ This program is distributed in the hope that it will be useful,
+# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# ~ GNU General Public License for more details.
+# ~
+# ~ You should have received a copy of the GNU General Public License
+# ~ along with this program. If not, see .
+
+
+import logging
+
+import lxml.etree as ET
+import requests
+from requests.exceptions import ConnectionError
+
+from .conf import DEBUG, AUTH
+
+
+LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
+LOG_DATE = '%d/%m/%Y %H:%M:%S'
+logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
+logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
+logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
+logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
+log = logging.getLogger(__name__)
+
+logging.getLogger('requests').setLevel(logging.ERROR)
+
+
+TIMEOUT = 10
+
+
+class PACComercioDigital(object):
+ ws = 'https://{}.comercio-digital.mx/{}'
+ api = 'https://app2.comercio-digital.mx/{}'
+ URL = {
+ 'timbra': ws.format('ws', 'timbre/timbrarV5.aspx'),
+ 'cancel': ws.format('cancela', 'cancela3/cancelarUuid'),
+ 'cancelxml': ws.format('cancela', 'cancela3/cancelarXml'),
+ 'client': api.format('x3/altaEmpresa'),
+ 'saldo': api.format('x3/saldo'),
+ 'timbres': api.format('x3/altaTimbres'),
+ }
+ CODES = {
+ '000': '000 Exitoso',
+ '004': '004 RFC {} ya esta dado de alta con Estatus=A',
+ '704': '704 Usuario Invalido',
+ }
+ NS_CFDI = {
+ 'cfdi': 'http://www.sat.gob.mx/cfd/3',
+ 'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
+ }
+
+ if DEBUG:
+ ws = 'https://pruebas.comercio-digital.mx/{}'
+ URL = {
+ 'timbra': ws.format('timbre/timbrarV5.aspx'),
+ 'cancel': ws.format('cancela3/cancelarUuid'),
+ 'cancelxml': ws.format('cancela3/cancelarXml'),
+ 'client': api.format('x3/altaEmpresa'),
+ 'saldo': api.format('x3/saldo'),
+ 'timbres': api.format('x3/altaTimbres'),
+ }
+
+ def __init__(self):
+ self.error = ''
+ self.cfdi_uuid = ''
+ self.date_stamped = ''
+
+ def _error(self, msg):
+ self.error = str(msg)
+ log.error(msg)
+ return
+
+ def _post(self, url, data, headers={}):
+ result = None
+ headers['host'] = url.split('/')[2]
+ headers['Content-type'] = 'text/plain'
+ headers['Connection'] = 'Keep-Alive'
+
+ try:
+ result = requests.post(url, data=data, headers=headers, timeout=TIMEOUT)
+ except ConnectionError as e:
+ self._error(e)
+
+ return result
+
+ def _validate_cfdi(self, xml):
+ """
+ Comercio Digital solo soporta la declaración con doble comilla
+ """
+ tree = ET.fromstring(xml.encode())
+ xml = ET.tostring(tree,
+ pretty_print=True, doctype='')
+ return xml
+
+ def stamp(self, cfdi, auth={}):
+ if DEBUG or not auth:
+ auth = AUTH
+
+ url = self.URL['timbra']
+ headers = {
+ 'usrws': auth['user'],
+ 'pwdws': auth['pass'],
+ 'tipo': 'XML',
+ }
+ cfdi = self._validate_cfdi(cfdi)
+ result = self._post(url, cfdi, headers)
+
+ if result is None:
+ return ''
+
+ if result.status_code != 200:
+ return ''
+
+ if 'errmsg' in result.headers:
+ self._error(result.headers['errmsg'])
+ return ''
+
+ xml = result.content
+ tree = ET.fromstring(xml)
+ self.cfdi_uuid = tree.xpath(
+ 'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
+ namespaces=self.NS_CFDI)
+ self.date_stamped = tree.xpath(
+ 'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@FechaTimbrado)',
+ namespaces=self.NS_CFDI)
+
+ return xml.decode()
+
+ def _get_data_cancel(self, cfdi, info, auth):
+ NS_CFDI = {
+ 'cfdi': 'http://www.sat.gob.mx/cfd/3',
+ 'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
+ }
+ tree = ET.fromstring(cfdi)
+ tipo = tree.xpath(
+ 'string(//cfdi:Comprobante/@TipoDeComprobante)',
+ namespaces=NS_CFDI)
+ total = tree.xpath(
+ 'string(//cfdi:Comprobante/@Total)',
+ namespaces=NS_CFDI)
+ rfc_emisor = tree.xpath(
+ 'string(//cfdi:Comprobante/cfdi:Emisor/@Rfc)',
+ namespaces=NS_CFDI)
+ rfc_receptor = tree.xpath(
+ 'string(//cfdi:Comprobante/cfdi:Receptor/@Rfc)',
+ namespaces=NS_CFDI)
+ uid = tree.xpath(
+ 'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
+ namespaces=NS_CFDI)
+ data = (
+ f"USER={auth['user']}",
+ f"PWDW={auth['pass']}",
+ f"RFCE={rfc_emisor}",
+ f"UUID={uid}",
+ f"PWDK={info['pass']}",
+ f"KEYF={info['key']}",
+ f"CERT={info['cer']}",
+ f"TIPO={info['tipo']}",
+ f"ACUS=SI",
+ f"RFCR={rfc_receptor}",
+ f"TIPOC={tipo}",
+ f"TOTAL={total}",
+ )
+ return '\n'.join(data)
+
+ def cancel(self, cfdi, info, auth={}):
+ if not auth:
+ auth = AUTH
+ url = self.URL['cancel']
+ data = self._get_data_cancel(cfdi, info, auth)
+
+ result = self._post(url, data)
+
+ if result is None:
+ return ''
+
+ if result.status_code != 200:
+ return ''
+
+ if result.headers['codigo'] != '000':
+ self._error(result.headers['errmsg'])
+ return ''
+
+ return result.content
+
+ def _get_headers_cancel_xml(self, cfdi, info, auth):
+ NS_CFDI = {
+ 'cfdi': 'http://www.sat.gob.mx/cfd/3',
+ 'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
+ }
+ tree = ET.fromstring(cfdi)
+ tipo = tree.xpath(
+ 'string(//cfdi:Comprobante/@TipoDeComprobante)',
+ namespaces=NS_CFDI)
+ total = tree.xpath(
+ 'string(//cfdi:Comprobante/@Total)',
+ namespaces=NS_CFDI)
+ rfc_receptor = tree.xpath(
+ 'string(//cfdi:Comprobante/cfdi:Receptor/@Rfc)',
+ namespaces=NS_CFDI)
+
+ headers = {
+ 'usrws': auth['user'],
+ 'pwdws': auth['pass'],
+ 'rfcr': rfc_receptor,
+ 'total': total,
+ 'tipocfdi': tipo,
+ }
+ headers.update(info)
+
+ return headers
+
+ def cancel_xml(self, cfdi, xml, info, auth={}):
+ if not auth:
+ auth = AUTH
+ url = self.URL['cancelxml']
+ headers = self._get_headers_cancel_xml(cfdi, info, auth)
+ result = self._post(url, xml, headers)
+
+ if result is None:
+ return ''
+
+ if result.status_code != 200:
+ return ''
+
+ if result.headers['codigo'] != '000':
+ self._error(result.headers['errmsg'])
+ return ''
+
+ return result.content
+
+ def _get_data_client(self, auth, values):
+ data = [f"usr_ws={auth['user']}", f"pwd_ws={auth['pass']}"]
+ fields = (
+ 'rfc_contribuyente',
+ 'nombre_contribuyente',
+ 'calle',
+ 'noExterior',
+ 'noInterior',
+ 'colonia',
+ 'localidad',
+ 'municipio',
+ 'estado',
+ 'pais',
+ 'cp',
+ 'contacto',
+ 'telefono',
+ 'email',
+ 'rep_nom',
+ 'rep_rfc',
+ 'email_fact',
+ 'pwd_asignado',
+ )
+ data += [f"{k}={values[k]}" for k in fields]
+
+ return '\n'.join(data)
+
+ def client_add(self, data):
+ auth = AUTH
+ url = self.URL['client']
+ data = self._get_data_client(auth, data)
+
+ result = self._post(url, data)
+
+ if result is None:
+ return False
+
+ if result.status_code != 200:
+ self._error(f'Code: {result.status_code}')
+ return False
+
+ if result.text != self.CODES['000']:
+ self._error(result.text)
+ return False
+
+ return True
+
+ def client_balance(self, data):
+ url = self.URL['saldo']
+ host = url.split('/')[2]
+ headers = {
+ 'Content-type': 'text/plain',
+ 'Host': host,
+ 'Connection' : 'Keep-Alive',
+ }
+ try:
+ result = requests.get(url, params=data, headers=headers, timeout=TIMEOUT)
+ except ConnectionError as e:
+ self._error(e)
+ return ''
+
+ if result.status_code != 200:
+ return ''
+
+ if result.text == self.CODES['704']:
+ self._error(result.text)
+ return ''
+
+ return result.text
+
+ def client_add_timbres(self, data, auth={}):
+ if not auth:
+ auth = AUTH
+ url = self.URL['timbres']
+ data = '\n'.join((
+ f"usr_ws={auth['user']}",
+ f"pwd_ws={auth['pass']}",
+ f"rfc_recibir={data['rfc']}",
+ f"num_timbres={data['timbres']}"
+ ))
+
+ result = self._post(url, data)
+
+ if result is None:
+ return False
+
+ if result.status_code != 200:
+ self._error(f'Code: {result.status_code}')
+ return False
+
+ if result.text != self.CODES['000']:
+ self._error(result.text)
+ return False
+
+ return True
+
diff --git a/source/app/controllers/comercio/conf.py.example b/source/app/controllers/comercio/conf.py.example
new file mode 100644
index 0000000..4e89d68
--- /dev/null
+++ b/source/app/controllers/comercio/conf.py.example
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# ~
+# ~ PAC
+# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] net
+# ~
+# ~ This program is free software: you can redistribute it and/or modify
+# ~ it under the terms of the GNU General Public License as published by
+# ~ the Free Software Foundation, either version 3 of the License, or
+# ~ (at your option) any later version.
+# ~
+# ~ This program is distributed in the hope that it will be useful,
+# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# ~ GNU General Public License for more details.
+# ~
+# ~ You should have received a copy of the GNU General Public License
+# ~ along with this program. If not, see .
+
+
+# ~ Siempre consulta la documentación de Finkok
+# ~ AUTH = Puedes usar credenciales genericas para timbrar, o exclusivas para
+# ~ cada emisor
+# ~ RESELLER = Algunos procesos como agregar emisores, solo pueden ser usadas
+# ~ con una cuenta de reseller
+
+
+DEBUG = True
+
+
+AUTH = {
+ 'user': '',
+ 'pass': '',
+}
+
+
+if DEBUG:
+ AUTH = {
+ 'user': 'AAA010101AAA',
+ 'pass': 'PWD',
+ }
diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index a5772b4..3806ab9 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -589,7 +589,7 @@ def timbra_xml(xml, auth):
if not DEBUG and not auth:
msg = 'Sin datos para timbrar'
- result = {'ok': True, 'error': msg}
+ result = {'ok': False, 'error': msg}
return result
result = {'ok': True, 'error': ''}
@@ -2738,521 +2738,6 @@ def sync_cfdi(auth, files):
return
-class ImportFacturaLibreGambas(object):
-
- def __init__(self, conexion, rfc):
- self._rfc = rfc
- self._con = None
- self._cursor = None
- self._error = ''
- self._is_connect = self._connect(conexion)
- self._clientes = []
- self._clientes_rfc = []
-
- @property
- def error(self):
- return self._error
-
- @property
- def is_connect(self):
- return self._is_connect
-
- def _validate_rfc(self):
- sql = "SELECT rfc FROM emisor LIMIT 1"
- self._cursor.execute(sql)
- obj = self._cursor.fetchone()
- if obj is None:
- self._error = 'No se encontró al emisor: {}'.format(self._rfc)
- return False
-
- if not DEBUG:
- if obj['rfc'] != self._rfc:
- self._error = 'Los datos no corresponden al RFC: {}'.format(self._rfc)
- return False
-
- return True
-
- def _connect(self, conexion):
- import psycopg2
- import psycopg2.extras
- try:
- self._con = psycopg2.connect(conexion)
- self._cursor = self._con.cursor(cursor_factory=psycopg2.extras.DictCursor)
- return self._validate_rfc()
- except Exception as e:
- log.error(e)
- self._error = 'No se pudo conectar a la base de datos'
- return False
-
- def close(self):
- try:
- self._cursor.close()
- self._con.close()
- except:
- pass
- return
-
- def import_data(self):
- data = {}
- tables = (
- ('receptores', 'Socios'),
- ('cfdifacturas', 'Facturas'),
- ('categorias', 'Categorias'),
- ('productos', 'Productos'),
- ('tickets', 'Tickets'),
- )
- for source, target in tables:
- data[target] = self._get_table(source)
-
- data['Socios'] += self._clientes
-
- return data
-
- def _get_table(self, table):
- return getattr(self, '_{}'.format(table))()
-
- def _tickets(self):
- sql = "SELECT * FROM tickets"
- self._cursor.execute(sql)
- rows = self._cursor.fetchall()
-
- fields = (
- ('serie', 'serie'),
- ('folio', 'folio'),
- ('fecha', 'fecha'),
- ('formadepago', 'forma_pago'),
- ('subtotal', 'subtotal'),
- ('descuento', 'descuento'),
- ('total', 'total'),
- ('notas', 'notas'),
- ('factura', 'factura'),
- ('cancelada', 'cancelado'),
- ('vendedor', 'vendedor'),
- )
- data = []
- totals = len(rows)
- for i, row in enumerate(rows):
- msg = '\tImportando ticket {} de {}'.format(i+1, totals)
- log.info(msg)
-
- new = {t: row[s] for s, t in fields}
-
- new['notas'] = ''
- new['fecha'] = new['fecha'].replace(microsecond=0)
- new['estatus'] = 'Generado'
- if new['cancelado']:
- new['estatus'] = 'Cancelado'
- new['factura'] = self._get_invoice_ticket(new['factura'])
-
- new['details'] = self._get_details_ticket(row['id'])
- new['taxes'] = self._get_taxes_ticket(row['id'])
- data.append(new)
-
- return data
-
- def _get_invoice_ticket(self, invoice):
- if not invoice:
- return None
-
- sql = "SELECT serie, folio FROM cfdifacturas WHERE id=%s"
- self._cursor.execute(sql, [invoice])
- row = self._cursor.fetchone()
- if row is None:
- return {}
-
- return dict(row)
-
- def _get_details_ticket(self, id):
- sql = "SELECT * FROM t_detalle WHERE id_cfdi=%s"
- self._cursor.execute(sql, [id])
- rows = self._cursor.fetchall()
-
- fields = (
- ('descripcion', 'descripcion'),
- ('cantidad', 'cantidad'),
- ('valorunitario', 'valor_unitario'),
- ('importe', 'importe'),
- ('precio', 'precio_final'),
- )
-
- data = []
- for row in rows:
- new = {t: row[s] for s, t in fields if row[s]}
- data.append(new)
-
- return data
-
- def _get_taxes_ticket(self, id):
- sql = "SELECT * FROM t_impuestos WHERE id_cfdi=%s"
- self._cursor.execute(sql, [id])
- rows = self._cursor.fetchall()
-
- tasas = {
- '0': 0.0,
- '16': 0.16,
- '16.00': 0.16,
- '0.16': 0.16,
- '11': 0.11,
- '-10': 0.10,
- '-2': 0.02,
- '-0.5': 0.005,
- '-2/3': 0.106667,
- '-10.6667': 0.106667,
- '-10.6666': 0.106667,
- '-10.666666': 0.106667,
- '-10.66660': 0.106667,
- }
-
- data = []
- for row in rows:
- filtro = {
- 'name': row['impuesto'],
- 'tasa': tasas[row['tasa']],
- 'tipo': row['tipo'][0],
- }
- new = {
- 'import': row['importe'],
- 'filter': filtro
- }
- data.append(new)
-
- return data
-
- def _productos(self):
- UNIDADES = {
- 'k': 'KGM',
- 'kg': 'KGM',
- 'kg.': 'KGM',
- 'pieza': 'H87',
- 'pza': 'H87',
- 'pz': 'H87',
- 'bulto': 'H87',
- 'b': 'H87',
- 'exb': 'H87',
- 'ex': 'H87',
- 'caja': 'XBX',
- 'c': 'XBX',
- 'rollo': 'XRO',
- 'tira': 'SR',
- 't': 'SR',
- 'cono': 'XAJ',
- 'paquete': 'XPK',
- 'pq': 'XPK',
- }
- sql = "SELECT * FROM productos"
- self._cursor.execute(sql)
- rows = self._cursor.fetchall()
-
- fields = (
- ('id_categoria', 'categoria'),
- ('noidentificacion', 'clave'),
- ('descripcion', 'descripcion'),
- # ~ ('unidad', 'unidad'),
- ('id_unidad', 'unidad'),
- # ~ ('costo', 'ultimo_costo'),
- ('valorunitario', 'valor_unitario'),
- # ~ ('existencia', 'existencia'),
- # ~ ('minimo', 'minimo'),
- ('inventario', 'inventario'),
- ('codigobarras', 'codigo_barras'),
- ('cuentapredial', 'cuenta_predial'),
- )
- data = []
-
- sql = """
- SELECT nombre, tasa, tipo
- FROM impuestos
- WHERE id=%s
- """
- totals = len(rows)
- for i, row in enumerate(rows):
- msg = '\tImportando producto {} de {}'.format(i+1, totals)
- log.info(msg)
- # ~ print (i, dict(row))
- new = {t: row[s] for s, t in fields}
-
- # ~ print (new['unidad'])
- if new['unidad'] == 2:
- new['unidad'] = 'servicio'
-
- u = new['unidad'].lower().strip()
- if u in ('sin',):
- continue
- if not u:
- u = 'pieza'
-
- if not new['categoria']:
- new['categoria'] = None
- new['codigo_barras'] = new['codigo_barras'] or ''
- new['cuenta_predial'] = new['cuenta_predial'] or ''
- new['descripcion'] = ' '.join(new['descripcion'].split())
- new['clave_sat'] = DEFAULT_SAT_PRODUCTO
-
- new['unidad'] = UNIDADES.get(u, new['unidad'])
- self._cursor.execute(sql, [row['id_impuesto1']])
- impuestos = self._cursor.fetchall()
- new['impuestos'] = tuple(impuestos)
- data.append(new)
-
- return data
-
- def _categorias(self):
- sql = "SELECT * FROM categorias ORDER BY id_padre"
- self._cursor.execute(sql)
- rows = self._cursor.fetchall()
-
- fields = (
- ('id', 'id'),
- ('categoria', 'categoria'),
- ('id_padre', 'padre'),
- )
- data = []
-
- for row in rows:
- new = {t: row[s] for s, t in fields}
- if new['padre'] == 0:
- new['padre'] = None
- data.append(new)
-
- return data
-
- def _get_cliente(self, invoice):
- sql = "SELECT rfc, nombre FROM receptores WHERE id=%s"
- self._cursor.execute(sql, [invoice['id_cliente']])
- obj = self._cursor.fetchone()
- if not obj is None:
- data = {
- 'rfc': obj['rfc'],
- 'slug': to_slug(obj['nombre']),
- }
- return data
-
- if not invoice['xml']:
- return {}
-
- doc = parse_xml(invoice['xml'])
- version = doc.attrib['version']
- node = doc.find('{}Receptor'.format(PRE[version]))
- rfc = node.attrib['rfc']
- nombre = node.attrib['nombre']
-
- tipo_persona = 1
- if rfc == 'XEXX010101000':
- tipo_persona = 4
- elif rfc == 'XAXX010101000':
- tipo_persona = 3
- elif len(rfc) == 12:
- tipo_persona = 2
-
- data = {
- 'tipo_persona': tipo_persona,
- 'rfc': rfc,
- 'nombre': nombre,
- 'slug': to_slug(nombre),
- 'es_cliente': True,
- 'es_activo': False,
- }
- if not rfc in self._clientes_rfc:
- self._clientes_rfc.append(rfc)
- self._clientes.append(data)
-
- data = {
- 'rfc': data['rfc'],
- 'slug': data['slug'],
- }
- return data
-
- def _get_detalles(self, id):
- sql = "SELECT * FROM cfdidetalle WHERE id_cfdi=%s"
- self._cursor.execute(sql, [id])
- rows = self._cursor.fetchall()
-
- fields = (
- ('categoria', 'categoria'),
- ('cantidad', 'cantidad'),
- ('unidad', 'unidad'),
- ('noidentificacion', 'clave'),
- ('descripcion', 'descripcion'),
- ('valorunitario', 'valor_unitario'),
- ('importe', 'importe'),
- ('numero', 'pedimento'),
- ('fecha', 'fecha_pedimento'),
- ('aduana', 'aduana'),
- ('cuentapredial', 'cuenta_predial'),
- ('descuento', 'descuento'),
- ('precio', 'precio_final'),
- )
-
- data = []
- for row in rows:
- new = {t: row[s] for s, t in fields if row[s]}
- data.append(new)
-
- return data
-
- def _get_impuestos(self, id):
- sql = "SELECT * FROM cfdiimpuestos WHERE id_cfdi=%s"
- self._cursor.execute(sql, [id])
- rows = self._cursor.fetchall()
-
- tasas = {
- '0': 0.0,
- '16': 0.16,
- '16.00': 0.16,
- '11': 0.11,
- '-10': 0.10,
- '-2': 0.02,
- '-0.5': 0.005,
- '-2/3': 0.106667,
- '-10.6666': 0.106667,
- '-10.666666': 0.106667,
- '-10.66660': 0.106667,
- '-4': 0.04,
- }
-
- data = []
- for row in rows:
- filtro = {
- 'name': row['impuesto'],
- 'tasa': tasas[row['tasa']],
- 'tipo': row['tipo'][0],
- }
- new = {
- 'importe': row['importe'],
- 'filtro': filtro
- }
- data.append(new)
-
- return data
-
- def _cfdifacturas(self):
- sql = "SELECT * FROM cfdifacturas"
- self._cursor.execute(sql)
- rows = self._cursor.fetchall()
- fields = (
- ('version', 'version'),
- ('serie', 'serie'),
- ('folio', 'folio'),
- ('fecha', 'fecha'),
- ('fecha_timbrado', 'fecha_timbrado'),
- ('formadepago', 'forma_pago'),
- ('condicionesdepago', 'condiciones_pago'),
- ('subtotal', 'subtotal'),
- ('descuento', 'descuento'),
- ('tipocambio', 'tipo_cambio'),
- ('moneda', 'moneda'),
- ('total', 'total'),
- ('tipodecomprobante', 'tipo_comprobante'),
- ('metododepago', 'metodo_pago'),
- ('lugarexpedicion', 'lugar_expedicion'),
- ('totalimpuestosretenidos', 'total_retenciones'),
- ('totalimpuestostrasladados', 'total_traslados'),
- ('xml', 'xml'),
- ('id_cliente', 'cliente'),
- ('notas', 'notas'),
- ('uuid', 'uuid'),
- ('cancelada', 'cancelada'),
- )
- data = []
- totals = len(rows)
- for i, row in enumerate(rows):
- msg = '\tImportando factura {} de {}'.format(i+1, totals)
- log.info(msg)
-
- new = {t: row[s] for s, t in fields}
-
- for _, f in fields:
- new[f] = new[f] or ''
-
-
- new['fecha'] = new['fecha'].replace(microsecond=0)
- if new['fecha_timbrado']:
- new['fecha_timbrado'] = new['fecha_timbrado'].replace(microsecond=0)
- else:
- new['fecha_timbrado'] = None
-
- new['estatus'] = 'Timbrada'
- if new['cancelada']:
- new['estatus'] = 'Cancelada'
-
- if not new['uuid']:
- new['uuid'] = None
- elif new['uuid'] in('ok', '123', '??', 'X'):
- new['uuid'] = None
- new['estatus'] = 'Cancelada'
- new['cancelada'] = True
-
- if new['xml'] is None:
- new['xml'] = ''
-
- new['pagada'] = True
- new['total_mn'] = round(row['tipocambio'] * row['total'], 2)
- new['detalles'] = self._get_detalles(row['id'])
- new['impuestos'] = self._get_impuestos(row['id'])
- new['cliente'] = self._get_cliente(row)
- data.append(new)
- return data
-
- def _receptores(self):
- sql = "SELECT * FROM receptores"
- self._cursor.execute(sql)
- rows = self._cursor.fetchall()
-
- fields = (
- ('rfc', 'rfc'),
- ('nombre', 'nombre'),
- ('calle', 'calle'),
- ('noexterior', 'no_exterior'),
- ('nointerior', 'no_interior'),
- ('colonia', 'colonia'),
- ('municipio', 'municipio'),
- ('estado', 'estado'),
- ('pais', 'pais'),
- ('codigopostal', 'codigo_postal'),
- ('extranjero', 'es_extranjero'),
- ('activo', 'es_activo'),
- ('fechaalta', 'fecha_alta'),
- ('notas', 'notas'),
- )
- data = []
-
- sql1 = "SELECT correo FROM correos WHERE id_padre=%s"
- sql2 = "SELECT telefono FROM telefonos WHERE id_padre=%s"
- totals = len(rows)
- for i, row in enumerate(rows):
- msg = '\tImportando cliente {} de {}'.format(i+1, totals)
- log.info(msg)
- new = {t: row[s] for s, t in fields}
- new['slug'] = to_slug(new['nombre'])
- new['es_cliente'] = True
- if new['fecha_alta'] is None:
- new['fecha_alta'] = str(now())
- else:
- new['fecha_alta'] = str(new['fecha_alta'])
-
- for _, f in fields:
- new[f] = new[f] or ''
- if new['es_extranjero']:
- new['tipo_persona'] = 4
- elif new['rfc'] == 'XAXX010101000':
- new['tipo_persona'] = 3
- elif len(new['rfc']) == 12:
- new['tipo_persona'] = 2
-
- self._cursor.execute(sql1, (row['id'],))
- tmp = self._cursor.fetchall()
- if tmp:
- new['correo_facturas'] = ', '.join([r[0] for r in tmp])
-
- self._cursor.execute(sql2, (row['id'],))
- tmp = self._cursor.fetchall()
- if tmp:
- new['telefonos'] = ', '.join([r[0] for r in tmp])
-
- data.append(new)
- return data
-
-
class ImportFacturaLibre(object):
def __init__(self, path, rfc):
diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py
index f653679..c955291 100644
--- a/source/app/controllers/utils.py
+++ b/source/app/controllers/utils.py
@@ -19,10 +19,12 @@
import base64
import collections
import datetime
+import getpass
import json
import logging
import math
import os
+import shlex
import shutil
import smtplib
import sqlite3
@@ -48,6 +50,9 @@ from dateutil import parser
import seafileapi
+from settings import DEBUG, DB_COMPANIES, PATHS
+from .comercio import PACComercioDigital
+
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
@@ -61,7 +66,15 @@ logging.getLogger('peewee').setLevel(logging.WARNING)
TIMEOUT = 10
PATH_INVOICES = 'facturas'
+PG_DUMP = 'pg_dump -U postgres'
+PSQL = 'psql -U postgres'
+if DEBUG:
+ PG_DUMP = 'pg_dump -h localhost -U postgres'
+ PSQL = 'psql -h localhost -U postgres'
+PACS = {
+ 'comercio': PACComercioDigital
+}
#~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
class CaseInsensitiveDict(collections.MutableMapping):
@@ -259,6 +272,16 @@ def _call(args):
return subprocess.check_output(args, shell=True).decode()
+def _run(args, wait=False):
+ result = ''
+ cmd = shlex.split(args)
+ if wait:
+ result = subprocess.run(cmd, shell=True, check=True).stdout.decode()
+ else:
+ subprocess.run(cmd)
+ return result
+
+
def _join(*paths):
return os.path.join(*paths)
@@ -332,62 +355,142 @@ def to_zip(files):
return zip_buffer.getvalue()
-def db_delete(user, path):
- dt = datetime.datetime.now().strftime('%y%m%d_%H%M')
- path_bk = _join(path, 'tmp', '{}_{}.bk'.format(user, dt))
- args = 'pg_dump -U postgres -Fc {} > "{}"'.format(user, path_bk)
- try:
- _call(args)
- except:
- pass
+def dumps(data):
+ return json.dumps(data, default=str)
- args = 'psql -U postgres -c "DROP DATABASE {0};"'.format(user)
- try:
- _call(args)
- except:
- pass
- args = 'psql -U postgres -c "DROP ROLE {0};"'.format(user)
- try:
- _call(args)
- except:
- pass
+def loads(data):
+ return json.loads(data)
+
+
+def json_loads(path):
+ return json.loads(open(path, 'r').read())
+
+
+def _validate_db_rfc():
+ con = sqlite3.connect(DB_COMPANIES)
+ sql = """
+ CREATE TABLE IF NOT EXISTS names(
+ rfc TEXT NOT NULL COLLATE NOCASE UNIQUE,
+ con TEXT NOT NULL
+ );
+ """
+ cursor = con.cursor()
+ cursor.executescript(sql)
+ cursor.close()
+ con.close()
return
+def _sql_companies(sql, args=()):
+ _validate_db_rfc()
+ con = sqlite3.connect(DB_COMPANIES)
+ cursor = con.cursor()
+ try:
+ cursor.execute(sql, args)
+ data = cursor.fetchall()
+ except sqlite3.IntegrityError as e:
+ log.error(e)
+ return False
+
+ con.commit()
+ cursor.close()
+ con.close()
+ return data
+
+
+def get_data_con(rfc):
+ data = rfc_get(rfc)[0][0]
+ return loads(data)
+
+
+def rfc_get(rfc=''):
+ if rfc:
+ sql = "SELECT con FROM names WHERE rfc = ?"
+ w = (rfc,)
+ else:
+ w = ()
+ sql = "SELECT * FROM names"
+ data = _sql_companies(sql, w)
+ return data
+
+
+def rfc_exists(rfc):
+ sql = "SELECT rfc FROM names WHERE rfc = ?"
+ data = _sql_companies(sql, (rfc,))
+ if isinstance(data, bool):
+ return
+ return bool(data)
+
+
+def rfc_add(rfc, con):
+ sql = "INSERT INTO names VALUES (?, ?)"
+ data = _sql_companies(sql, (rfc.upper(), dumps(con)))
+ return True
+
+
+def db_create(user):
+ args = f'{PSQL} -c "CREATE ROLE {user} WITH LOGIN ENCRYPTED PASSWORD \'{user}\';"'
+ _run(args)
+ args = f'{PSQL} -c "CREATE DATABASE {user} WITH OWNER {user};"'
+ _run(args)
+ return True
+
+
+def db_delete(user, path, no_database=False):
+ sql = "DELETE FROM names WHERE rfc = ?"
+ data = _sql_companies(sql, (user,))
+
+ if no_database:
+ return True
+
+ user = user.replace('&', '').lower()
+ dt = now().strftime('%y%m%d_%H%M')
+ path_bk = _join(path, f'{user}_{dt}.bk')
+
+ args = f'{PG_DUMP} -d {user} -Fc -f "{path_bk}"'
+ _run(args)
+
+ args = f'{PSQL} -c "DROP DATABASE {user};"'
+ _run(args)
+
+ args = f'{PSQL} -c "DROP ROLE {user};"'
+ _run(args)
+ return True
+
+
def _get_pass(rfc):
return rfc
-def _backup_db(rfc, data, path_bk, is_mv, url_seafile):
- if data['type'] != 'postgres':
- return
-
- log.info('Generando backup de: {}'.format(rfc))
- bk_name = '{}.bk'.format(rfc.lower())
- path_db = _join(path_bk, bk_name)
- args = 'pg_dump -U postgres -Fc {} > "{}"'.format(data['name'], path_db)
- result = _call(args)
+def _backup_db(rfc, is_mv, url_seafile):
+ log.info(f'Generando backup de: {rfc.upper()}')
+ bk_name = f'{rfc}.bk'
+ path = _join(PATHS['BK'], bk_name)
+ args = f'{PG_DUMP} -d {rfc} -Fc -f "{path}"'
+ _run(args)
log.info('\tBackup local generado...')
+
if is_mv:
- path_target = _join(Path.home(), PATH_INVOICES)
- if Path(path_target).exists():
+ path_target = _validate_path_local()
+ if path_target:
path_target = _join(path_target, bk_name)
- shutil.copy(path_db, path_target)
+ shutil.copy(path, path_target)
else:
log.error('\tNo existe la carpeta compartida...')
- sql = 'select correo_timbrado, token_soporte from emisor;'
- args = 'psql -U postgres -d {} -Atc "{}"'.format(data['name'], sql)
- result = _call(args)
- if not result:
- log.error('\tSin datos para backup remoto')
- return
+ # ~ sql = 'select correo_timbrado, token_soporte from emisor;'
+ # ~ args = 'psql -U postgres -d {} -Atc "{}"'.format(data['name'], sql)
+ # ~ result = _call(args)
+ # ~ if not result:
+ # ~ log.error('\tSin datos para backup remoto')
+ # ~ return
+
+ # ~ data = result.strip().split('|')
+ # ~ if not data[1]:
+ # ~ log.error('\tSin token de soporte')
+ # ~ return
- data = result.strip().split('|')
- if not data[1]:
- log.error('\tSin token de soporte')
- return
# ~ email = data[0]
# ~ uuid = data[1]
# ~ email = 'hola@elmau.net'
@@ -401,22 +504,47 @@ def _backup_db(rfc, data, path_bk, is_mv, url_seafile):
return
-def db_backup(path_companies, path_bk, is_mv, url_seafile):
- con = sqlite3.connect(path_companies)
- cursor = con.cursor()
- sql = "SELECT * FROM names"
- cursor.execute(sql)
- rows = cursor.fetchall()
- if rows is None:
+def db_backup(is_mv, url_seafile):
+ data = rfc_get()
+ if not len(data):
+ msg = 'Sin bases de datos a respaldar'
+ log.info(msg)
return
- cursor.close()
- con.close()
- for rfc, data in rows:
- _backup_db(rfc, json.loads(data), path_bk, is_mv, url_seafile)
+ for rfc, _ in data:
+ _backup_db(rfc.lower(), is_mv, url_seafile)
return
+def _validate_path_local():
+ path_bk = _join(str(Path.home()), PATHS['LOCAL'])
+ if not os.path.isdir(path_bk):
+ path_bk = ''
+ return path_bk
+
+
+def db_backup_local():
+ path_bk = _validate_path_local()
+ if not path_bk:
+ msg = 'No existe la carpeta local'
+ return {'ok': False, 'msg': msg}
+
+ data = rfc_get()
+ if not len(data):
+ msg = 'Sin bases de datos a respaldar'
+ return {'ok': False, 'msg': msg}
+
+ for row in data:
+ user = row[0].lower()
+ db = loads(row[1])['name']
+ path = _join(path_bk, '{}.bk'.format(user))
+ args = f'{PG_DUMP} -d {user} -Fc -f "{path}"'
+ _run(args)
+
+ msg = 'Bases de datos respaldadas correctamente'
+ result = {'ok': True, 'msg': msg}
+ return result
+
def now():
return datetime.datetime.now().replace(microsecond=0)
@@ -424,3 +552,42 @@ def now():
def get_days(date):
return (now() - date).days
+
+
+def get_pass():
+ pass1 = getpass.getpass('Introduce la contraseña: ')
+ pass2 = getpass.getpass('Confirma la contraseña: ')
+
+ if pass1 != pass2:
+ msg = 'Las contraseñas son diferentes'
+ return False, msg
+
+ password = pass1.strip()
+ if not password:
+ msg = 'La contraseña es necesaria'
+ return False, msg
+
+ return True, password
+
+
+def xml_stamp(xml, auth, name):
+ if not DEBUG and not auth:
+ msg = 'Sin datos para timbrar'
+ result = {'ok': False, 'error': msg}
+ return result
+
+ result = {'ok': True, 'error': ''}
+ auth = {'user': auth['USER'], 'pass': auth['PASS']}
+
+ pac = PACS[name]()
+ xml_stamped = pac.stamp(xml, auth)
+
+ if not xml_stamped:
+ result['ok'] = False
+ result['error'] = pac.error
+ return result
+
+ result['xml'] = xml_stamped
+ result['uuid'] = pac.cfdi_uuid
+ result['fecha'] = pac.date_stamped
+ return result
diff --git a/source/app/models/db.py b/source/app/models/db.py
index 78c04da..b302198 100644
--- a/source/app/models/db.py
+++ b/source/app/models/db.py
@@ -34,19 +34,18 @@ class StorageEngine(object):
return main.CfdiNomina.get_by(values)
def empresa_agregar(self, values):
- return main.empresa_agregar(values['alta_rfc'], False)
+ # ~ return main.empresa_agregar(values['alta_rfc'], False)
+ return main._new_client(values['alta_rfc'], False)
def empresa_borrar(self, values):
- return main.empresa_borrar(values['rfc'])
+ # ~ return main.empresa_borrar(values['rfc'])
+ return main._delete_client(values['rfc'], False, False)
def respaldar_dbs(self):
return main.respaldar_dbs()
- def _get_empresas(self, values):
- return main.get_empresas()
-
def get_values(self, table, values=None, session=None):
- if table in ('allusuarios', 'usuarioupdate'):
+ if table in ('allusuarios', 'usuarioupdate', 'main'):
return getattr(self, '_get_{}'.format(table))(values, session)
return getattr(self, '_get_{}'.format(table))(values)
@@ -71,8 +70,8 @@ class StorageEngine(object):
def _get_importinvoice(self, values):
return main.import_invoice()
- def _get_main(self, values):
- return main.config_main()
+ def _get_main(self, values, session):
+ return main.config_main(session['userobj'])
def _get_configtimbrar(self, values):
return main.config_timbrar()
@@ -463,3 +462,7 @@ class StorageEngine(object):
def nomina(self, values, user):
return main.CfdiNomina.post(values, user)
+ # Companies only in MV
+ def _get_empresas(self, values):
+ return main.companies_get()
+
diff --git a/source/app/models/main.py b/source/app/models/main.py
index df26dba..7555088 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -16,9 +16,9 @@
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see .
+import argparse
from decimal import Decimal
import sqlite3
-import click
from peewee import *
from playhouse.fields import PasswordField, ManyToManyField
from playhouse.shortcuts import case, SQL, cast
@@ -31,7 +31,7 @@ if __name__ == '__main__':
from controllers import util
-from settings import log, DEBUG, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI, \
+from settings import log, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI, \
INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS, DEFAULT_SAT_PRODUCTO, \
CANCEL_SIGNATURE, PUBLIC, DEFAULT_SERIE_TICKET, CURRENT_CFDI_NOMINA, \
DEFAULT_SAT_NOMINA, DECIMALES_TAX, TITLE_APP, MV, DECIMALES_PRECIOS, \
@@ -40,6 +40,7 @@ from settings import log, DEBUG, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI,
# ~ v2
from controllers import utils
from settings import (
+ DEBUG,
DB_COMPANIES,
EXT,
IS_MV,
@@ -249,14 +250,18 @@ def get_doc(type_doc, id, rfc):
return data, file_name, content_type
-def config_main():
+def config_main(user):
try:
obj = Emisor.select()[0]
except IndexError:
obj = None
punto_de_venta = Configuracion.get_bool('chk_usar_punto_de_venta')
+
nomina = Configuracion.get_bool('chk_usar_nomina')
+ if not user.es_admin:
+ nomina = False
+
data = {
'empresa': get_title_app(3),
'punto_de_venta': punto_de_venta,
@@ -426,6 +431,7 @@ class Configuracion(BaseModel):
'chk_config_pagos',
'chk_config_divisas',
'chk_cfg_pays_data_bank',
+ 'chk_usar_nomina',
)
data = (Configuracion
.select()
@@ -434,6 +440,9 @@ class Configuracion(BaseModel):
values = {r.clave: util.get_bool(r.valor) for r in data}
fields = (
+ 'txt_ticket_printer',
+ 'txt_config_nomina_serie',
+ 'txt_config_nomina_folio',
'txt_config_cfdipay_serie',
'txt_config_cfdipay_folio',
)
@@ -475,6 +484,17 @@ class Configuracion(BaseModel):
values = {r.clave: util.get_bool(r.valor) for r in data}
return values
+ @classmethod
+ def get_value(cls, key, default=''):
+ value = default
+ data = (Configuracion
+ .select(Configuracion.valor)
+ .where(Configuracion.clave == key)
+ )
+ if data:
+ value = data[0].valor
+ return value
+
@classmethod
def get_(cls, keys):
if isinstance(keys, str):
@@ -535,7 +555,6 @@ class Configuracion(BaseModel):
'chk_ticket_direct_print',
'chk_ticket_edit_cant',
'chk_ticket_total_up',
- 'chk_usar_nomina',
)
data = (Configuracion
.select()
@@ -543,15 +562,10 @@ class Configuracion(BaseModel):
)
values = {r.clave: util.get_bool(r.valor) for r in data}
fields = (
- 'txt_ticket_printer',
- 'txt_config_nomina_serie',
- 'txt_config_nomina_folio',
- 'txt_config_cfdipay_serie',
- 'txt_config_cfdipay_folio',
+ ('lst_pac', 'default'),
)
- # ~ tp = 'txt_ticket_printer'
- for f in fields:
- values[f] = Configuracion.get_(f)
+ for k, d in fields:
+ values[k] = Configuracion.get_value(k, d)
return values
if keys['fields'] == 'path_cer':
@@ -2632,10 +2646,17 @@ class Socios(BaseModel):
def add(cls, values):
accounts = util.loads(values.pop('accounts', '[]'))
fields = cls._clean(cls, values)
+
+ w = ((Socios.rfc==fields['rfc']) & (Socios.slug==fields['slug']))
+ if Socios.select().where(w).exists():
+ msg = 'Ya existe el RFC y Razón Social'
+ data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
+ return data
+
try:
obj = Socios.create(**fields)
except IntegrityError as e:
- msg = 'Ya existe el RFC y Razón Social'
+ msg = 'Ocurrio un error, al dar de alta el emisor'
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
return data
@@ -4987,10 +5008,15 @@ class Facturas(BaseModel):
obj.save()
enviar_correo = util.get_bool(Configuracion.get_('correo_directo'))
+ pac = Configuracion.get_('lst_pac').lower()
anticipo = False
msg = 'Factura timbrada correctamente'
- result = util.timbra_xml(obj.xml, auth)
+ if pac:
+ result = utils.xml_stamp(obj.xml, auth, pac)
+ else:
+ result = util.timbra_xml(obj.xml, auth)
+
if result['ok']:
obj.xml = result['xml']
obj.uuid = result['uuid']
@@ -8996,7 +9022,7 @@ def _init_values(rfc):
pass
if not Certificado.select().count():
- Certificado.create(rfc=rfc)
+ Certificado.create(rfc=rfc.upper())
log.info('Valores iniciales insertados...')
return
@@ -9234,64 +9260,6 @@ def _migrate_tables(rfc=''):
return
-def _agregar_superusuario():
- args = util.get_con()
- if not args:
- return
-
- conectar(args)
- usuario = input('Introduce el nuevo nombre para el superusuario: ').strip()
- if not usuario:
- msg = 'El nombre de usuario es requerido'
- log.erro(msg)
- return
- ok, contraseña = util.get_pass()
- if not ok:
- log.error(contraseña)
- return
- try:
- obj = Usuarios.create(
- usuario=usuario, contraseña=contraseña, es_superusuario=True)
- except IntegrityError:
- msg = 'El usuario ya existe'
- log.error(msg)
- return
-
- log.info('SuperUsuario creado correctamente...')
- return
-
-
-def _cambiar_contraseña():
- args = util.get_con()
- if not args:
- return
-
- conectar(args)
- usuario = input('Introduce el nombre de usuario: ').strip()
- if not usuario:
- msg = 'El nombre de usuario es requerido'
- log.error(msg)
- return
-
- try:
- obj = Usuarios.get(usuario=usuario)
- except Usuarios.DoesNotExist:
- msg = 'El usuario no existe'
- log.error(msg)
- return
-
- ok, contraseña = util.get_pass()
- if not ok:
- log.error(contraseña)
- return
-
- obj.contraseña = contraseña
- obj.save()
-
- log.info('Contraseña cambiada correctamente...')
- return
-
-
def _add_emisor(rfc, args):
util._valid_db_companies()
con = sqlite3.connect(COMPANIES)
@@ -9349,49 +9317,20 @@ def _iniciar_bd():
return
-def _agregar_rfc(no_bd):
- rfc = input('Introduce el nuevo RFC: ').strip().upper()
- if not rfc:
- msg = 'El RFC es requerido'
- log.error(msg)
- return
-
- datos = input('Introduce los datos de conexión: ').strip()
- if not datos:
- msg = 'Los datos de conexión son requeridos'
- log.error(msg)
- return
-
- opt = util.parse_con(datos)
- if not opt:
- log.error('Datos de conexión incompletos')
- return
-
- args = opt.copy()
- if conectar(args):
- if _add_emisor(rfc, util.dumps(opt)):
- if no_bd:
- log.info('RFC agregado correctamente...')
- return
- _crear_tablas(rfc)
- log.info('RFC agregado correctamente...')
- return
-
- log.error('No se pudo agregar el RFC')
- return
-
-
def _borrar_rfc():
- rfc = input('Introduce el RFC a borrar: ').strip().upper()
+ rfc = input('Introduce el RFC a borrar: ').strip().lower()
if not rfc:
msg = 'El RFC es requerido'
log.error(msg)
return
- confirm = input('¿Estás seguro de borrar el RFC?')
+ confirm = input('¿Estás seguro de borrar el RFC? [si]')
+ if confirm != 'si':
+ log.info('Proceso cancelado...')
+ return
- if _delete_emisor(rfc):
- util.delete_db(rfc.lower())
+ if _delete_emisor(rfc.upper()):
+ utils.db_delete(rfc, PATHS['BK'])
log.info('RFC borrado correctamente...')
return
@@ -9460,9 +9399,7 @@ def empresa_agregar(rfc, no_bd):
def empresa_borrar(rfc):
- if _delete_emisor(rfc):
- # ~ util.delete_db(rfc.lower())
- utils.db_delete(rfc.lower(), PATHS['DOCS'])
+ utils.db_delete(rfc.lower(), PATHS['BK'])
return True
@@ -9471,21 +9408,8 @@ def respaldar_dbs():
msg = 'Solo MV'
return {'ok': False, 'msg': msg}
- result = util.validate_path_bk()
- if not result['ok']:
- return result
- path_bk = result['msg']
-
- data = util.get_rfcs()
- if not len(data):
- msg = 'Sin bases de datos a respaldar'
- return {'ok': False, 'msg': msg}
-
- for row in data:
- util.respaldar_db(row, path_bk)
-
- msg = 'Bases de datos respaldadas correctamente'
- return {'ok': True, 'msg': msg}
+ result = utils.db_backup_local()
+ return result
def _importar_valores(archivo='', rfc=''):
@@ -9515,7 +9439,7 @@ def _importar_valores(archivo='', rfc=''):
try:
with database_proxy.atomic() as txn:
table.create(**r)
- except IntegrityError:
+ except:
pass
log.info('Importación terminada...')
@@ -9776,37 +9700,6 @@ def _importar_factura_libre(archivo):
return
-def _importar_factura_libre_gambas(conexion):
- rfc = input('Introduce el RFC: ').strip().upper()
- if not rfc:
- msg = 'El RFC es requerido'
- log.error(msg)
- return
-
- args = util.get_con(rfc)
- if not args:
- return
-
- conectar(args)
-
- log.info('Importando datos...')
- app = util.ImportFacturaLibreGambas(conexion, rfc)
- if not app.is_connect:
- log.error('\t{}'.format(app._error))
- return
-
- data = app.import_data()
-
- _importar_socios(data['Socios'])
- _importar_facturas(data['Facturas'])
- _importar_categorias(data['Categorias'])
- _importar_productos_gambas(data['Productos'])
- _import_tickets(data['Tickets'])
-
- log.info('Importación terminada...')
- return
-
-
def _exist_ticket(row):
filters = (
(Tickets.serie==row['serie']) &
@@ -9856,44 +9749,6 @@ def _import_tickets(rows):
return
-def _importar_productos_gambas(rows):
- log.info('Importando productos...')
-
- KEYS = {
- 'Exento': '000',
- 'ISR': '001',
- 'IVA': '002',
- }
-
- totals = len(rows)
- for i, row in enumerate(rows):
- msg = '\tGuardando producto {} de {}'.format(i+1, totals)
- log.info(msg)
-
- source_taxes = row.pop('impuestos')
- row['unidad'] = SATUnidades.get(SATUnidades.key==row['unidad'])
- taxes = []
- for tax in source_taxes:
- w = {
- 'key': KEYS[tax[0]],
- 'name': tax[0],
- 'tasa': float(tax[1]),
- 'tipo': tax[2][0],
- }
- taxes.append(SATImpuestos.get_o_crea(w))
-
- with database_proxy.transaction():
- try:
- obj = Productos.create(**row)
- obj.impuestos = taxes
- except IntegrityError as e:
- msg = '\tProducto ya existe'
- log.info(msg)
-
- log.info('Importación terminada...')
- return
-
-
def _importar_productos(archivo):
rfc = input('Introduce el RFC: ').strip().upper()
if not rfc:
@@ -10074,183 +9929,406 @@ def _exportar_documentos():
return
-def _test():
- rfc = input('Introduce el RFC: ').strip().upper()
+# ~ v2
+
+def companies_get():
+ data = utils.rfc_get()
+ rows = []
+ for row in data:
+ rows.append({'delete': '-', 'rfc': row[0].upper()})
+ return tuple(rows)
+
+
+def _list_clients():
+ rows = utils.rfc_get()
+ for row in rows:
+ msg = f'RFC: {row[0].upper()}'
+ print(msg)
+ return
+
+
+def _new_client(rfc, no_database):
+ rfc = rfc.lower()
if not rfc:
- msg = 'El RFC es requerido'
+ log.error('Falta el RFC')
+ return
+
+ if utils.rfc_exists(rfc):
+ msg = 'El RFC ya esta dado de alta'
+ log.error(msg)
+ return {'ok': False, 'msg': msg}
+
+ user = rfc.replace('&', '').lower()
+ if not no_database:
+ if not utils.db_create(user):
+ msg = 'No se pudo crear la base de datos'
+ log.error(msg)
+ return {'ok': False, 'msg': msg}
+
+ args = {
+ 'type': 'postgres',
+ 'name': user,
+ 'user': user,
+ 'password': user,
+ }
+ if not conectar(args.copy()):
+ msg = 'No se pudo conectar a la base de datos'
+ log.error(msg)
+ return {'ok': False, 'msg': msg}
+
+ if not utils.rfc_add(rfc, args):
+ msg = 'No se pudo guardar el nuevo emisor'
+ log.error(msg)
+ return {'ok': False, 'msg': msg}
+
+ if not no_database:
+ if not _crear_tablas(rfc):
+ msg = 'No se pudo crear las tablas'
+ log.error(msg)
+ return {'ok': False, 'msg': msg}
+
+ desconectar()
+ msg = 'Emisor dado de alta correctamente'
+ row = {'delete': '-', 'rfc': rfc.upper()}
+ result = {'ok': True, 'msg': msg, 'row': row}
+ return result
+
+
+def _delete_client(rfc, no_database, ask=True):
+ rfc = rfc.lower()
+ if not rfc:
+ log.error('Falta el RFC')
+ return
+
+ if not utils.rfc_exists(rfc):
+ msg = 'El RFC no esta dado de alta'
+ log.error(msg)
+ return {'ok': False, 'msg': msg}
+
+ if ask:
+ confirm = input('¿Estás seguro de borrar el RFC? [si]')
+ if confirm != 'si':
+ log.info('Proceso cancelado...')
+ return False
+
+ if utils.db_delete(rfc, PATHS['BK'], no_database):
+ log.info('RFC borrado correctamente...')
+ result = True
+ else:
+ log.error('No se pudo borrar el RFC')
+ result = False
+
+ return result
+
+
+def _update_sat():
+ clients = utils.rfc_get()
+ tables = utils.json_loads(PATHS['SAT'])
+ for rfc, con in clients:
+ log.info(f'Importando datos en: {rfc.upper()}')
+ conectar(utils.loads(con))
+ for table in tables:
+ t = globals()[table['tabla']]
+ with database_proxy.atomic():
+ for r in table['datos']:
+ try:
+ t.get_or_create(**r)
+ except:
+ pass
+ log.info(f"\tTabla importada: {table['tabla']}")
+ desconectar()
+ log.info('Importación terminada...')
+ return
+
+
+def _new_superuser(rfc):
+ rfc = rfc.lower()
+ if not rfc:
+ log.error('Falta el RFC')
+ return
+
+ if not utils.rfc_exists(rfc):
+ msg = 'El RFC no esta dado de alta'
log.error(msg)
return
- args = util.get_con(rfc)
- if not args:
+ args = utils.get_data_con(rfc)
+
+ user = input('Introduce el nuevo nombre para el superusuario: ').strip()
+ if not user:
+ msg = 'El nombre de usuario es requerido'
+ log.error(msg)
+ return
+
+ ok, password = utils.get_pass()
+ if not ok:
+ log.error(password)
return
conectar(args)
+ try:
+ obj = Usuarios.create(
+ usuario=user, contraseña=password, es_superusuario=True)
+ msg = 'SuperUsuario creado correctamente...'
+ log.info(msg)
+ except IntegrityError:
+ msg = 'El usuario ya existe'
+ log.error(msg)
+
+ desconectar()
+ return
+
+
+def _change_pass(rfc):
+ rfc = rfc.lower()
+ if not rfc:
+ log.error('Falta el RFC')
+ return
+
+ if not utils.rfc_exists(rfc):
+ msg = 'El RFC no esta dado de alta'
+ log.error(msg)
+ return
+
+ args = utils.get_data_con(rfc)
+ conectar(args)
+
+ user = input('Introduce el nombre de usuario: ').strip()
+ if not user:
+ msg = 'El nombre de usuario es requerido'
+ log.error(msg)
+ desconectar()
+ return
+
+ try:
+ obj = Usuarios.get(usuario=user)
+ except Usuarios.DoesNotExist:
+ msg = 'El usuario no existe'
+ log.error(msg)
+ desconectar()
+ return
+
+ ok, password = utils.get_pass()
+ if not ok:
+ log.error(password)
+ desconectar()
+ return
+
+ obj.contraseña = password
+ obj.save()
+
+ desconectar()
+ log.info('Contraseña cambiada correctamente...')
+ return
+
+
+def _process_command_line_arguments():
+ parser = argparse.ArgumentParser(
+ description='Empresa Libre')
+ parser.add_argument('-lc', '--list-clients', dest='list_clients',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-nc', '--new-client', dest='new_client',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-dc', '--delete-client', dest='delete_client',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-ndb', '--no-database', dest='no_database',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-m', '--migrate', dest='migrate',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-us', '--update-sat', dest='update_sat',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-ns', '--new-superuser', dest='new_superuser',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-cp', '--change-pass', dest='change_pass',
+ action='store_true', default=False, required=False)
+ parser.add_argument('-bk', '--backup', dest='backup',
+ action='store_true', default=False, required=False)
+
+ parser.add_argument('-r', '--rfc', dest='rfc', default='')
+ return parser.parse_args()
+
+
+def main(args):
+ if args.list_clients:
+ _list_clients()
+ return
+
+ if args.new_client:
+ _new_client(args.rfc, args.no_database)
+ return
+
+ if args.delete_client:
+ _delete_client(args.rfc, args.no_database)
+ return
+
+ if args.migrate:
+ _migrate_tables(args.rfc)
+ return
+
+ if args.update_sat:
+ _update_sat()
+ return
+
+ if args.new_superuser:
+ _new_superuser(args.rfc)
+ return
+
+ if args.change_pass:
+ _change_pass(args.rfc)
+ return
+
+ if args.backup:
+ utils.db_backup(IS_MV, URL['SEAFILE'])
return
-CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
-help_create_tables = 'Crea las tablas en la base de datos'
-help_migrate_db = 'Migra las tablas en la base de datos'
-help_superuser = 'Crea un nuevo super usuario'
-help_change_pass = 'Cambia la contraseña a un usuario'
-help_rfc = 'Agrega un nuevo RFC'
-help_br = 'Elimina un RFC'
-help_lr = 'Listar RFCs'
+# ~ CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
+# ~ help_create_tables = 'Crea las tablas en la base de datos'
+# ~ help_migrate_db = 'Migra las tablas en la base de datos'
+# ~ help_superuser = 'Crea un nuevo super usuario'
+# ~ help_change_pass = 'Cambia la contraseña a un usuario'
+# ~ help_rfc = 'Agrega un nuevo RFC'
+# ~ help_br = 'Elimina un RFC'
+# ~ help_lr = 'Listar RFCs'
-@click.command(context_settings=CONTEXT_SETTINGS)
-@click.option('-bd', '--iniciar-bd',help=help_create_tables,
- is_flag=True, default=False)
-@click.option('-m', '--migrar-bd', help=help_migrate_db,
- is_flag=True, default=False)
-@click.option('-ns', '--nuevo-superusuario', help=help_superuser,
- is_flag=True, default=False)
-@click.option('-cc', '--cambiar-contraseña', help=help_change_pass,
- is_flag=True, default=False)
-@click.option('-ar', '--agregar-rfc', help=help_rfc, is_flag=True, default=False)
-@click.option('-br', '--borrar-rfc', help=help_br, is_flag=True, default=False)
-@click.option('-lr', '--listar-rfc', help=help_lr, is_flag=True, default=False)
-@click.option('-i', '--importar-valores', is_flag=True, default=False)
-@click.option('-f', '--archivo')
-@click.option('-c', '--conexion')
-@click.option('-fl', '--factura-libre', is_flag=True, default=False)
-@click.option('-flg', '--factura-libre-gambas', is_flag=True, default=False)
-@click.option('-t', '--test', is_flag=True, default=False)
-@click.option('-gap', '--generar-archivo-productos', is_flag=True, default=False)
-@click.option('-ip', '--importar-productos', is_flag=True, default=False)
-@click.option('-bk', '--backup-dbs', is_flag=True, default=False)
-@click.option('-n', '--no-bd', is_flag=True, default=False)
-@click.option('-a', '--alta', is_flag=True, default=False)
-@click.option('-r', '--rfc')
-@click.option('-d', '--detalle', is_flag=True, default=False)
-@click.option('-id', '--importar-directorio')
-@click.option('-ed', '--exportar_documentos', is_flag=True, default=False)
-def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña,
- agregar_rfc, borrar_rfc, listar_rfc, importar_valores, archivo, conexion,
- factura_libre, factura_libre_gambas, test, generar_archivo_productos,
- importar_productos, backup_dbs, no_bd, alta, rfc, detalle,
- importar_directorio, exportar_documentos):
+# ~ @click.command(context_settings=CONTEXT_SETTINGS)
+# ~ @click.option('-bd', '--iniciar-bd',help=help_create_tables,
+ # ~ is_flag=True, default=False)
+# ~ @click.option('-m', '--migrar-bd', help=help_migrate_db,
+ # ~ is_flag=True, default=False)
+# ~ @click.option('-ns', '--nuevo-superusuario', help=help_superuser,
+ # ~ is_flag=True, default=False)
+# ~ @click.option('-cc', '--cambiar-contraseña', help=help_change_pass,
+ # ~ is_flag=True, default=False)
+# ~ @click.option('-br', '--borrar-rfc', help=help_br, is_flag=True, default=False)
+# ~ @click.option('-lr', '--listar-rfc', help=help_lr, is_flag=True, default=False)
+# ~ @click.option('-i', '--importar-valores', is_flag=True, default=False)
+# ~ @click.option('-f', '--archivo')
+# ~ @click.option('-c', '--conexion')
+# ~ @click.option('-fl', '--factura-libre', is_flag=True, default=False)
+# ~ @click.option('-t', '--test', is_flag=True, default=False)
+# ~ @click.option('-gap', '--generar-archivo-productos', is_flag=True, default=False)
+# ~ @click.option('-ip', '--importar-productos', is_flag=True, default=False)
+# ~ @click.option('-bk', '--backup-dbs', is_flag=True, default=False)
+# ~ @click.option('-n', '--no-bd', is_flag=True, default=False)
+# ~ @click.option('-a', '--alta', is_flag=True, default=False)
+# ~ @click.option('-r', '--rfc')
+# ~ @click.option('-d', '--detalle', is_flag=True, default=False)
+# ~ @click.option('-id', '--importar-directorio')
+# ~ @click.option('-ed', '--exportar_documentos', is_flag=True, default=False)
+# ~ def main2(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña,
+ # ~ borrar_rfc, listar_rfc, importar_valores, archivo, conexion,
+ # ~ factura_libre, test, generar_archivo_productos,
+ # ~ importar_productos, backup_dbs, no_bd, alta, rfc, detalle,
+ # ~ importar_directorio, exportar_documentos):
- opt = locals()
+ # ~ opt = locals()
- if opt['test']:
- _test()
- sys.exit(0)
+ # ~ if opt['alta']:
+ # ~ if not opt['rfc']:
+ # ~ msg = 'Falta el RFC'
+ # ~ raise click.ClickException(msg)
+ # ~ _new_client(opt['rfc'], no_bd)
+ # ~ sys.exit(0)
- if opt['alta']:
- if not opt['rfc']:
- msg = 'Falta el RFC'
- raise click.ClickException(msg)
- empresa_agregar(opt['rfc'], no_bd)
- sys.exit(0)
+ # ~ if opt['iniciar_bd']:
+ # ~ _iniciar_bd()
+ # ~ sys.exit(0)
- if opt['iniciar_bd']:
- _iniciar_bd()
- sys.exit(0)
+ # ~ if opt['migrar_bd']:
+ # ~ _migrate_tables(rfc)
+ # ~ sys.exit(0)
- if opt['migrar_bd']:
- _migrate_tables(rfc)
- sys.exit(0)
+ # ~ if opt['nuevo_superusuario']:
+ # ~ _agregar_superusuario()
+ # ~ sys.exit(0)
- if opt['nuevo_superusuario']:
- _agregar_superusuario()
- sys.exit(0)
+ # ~ if opt['cambiar_contraseña']:
+ # ~ _cambiar_contraseña()
+ # ~ sys.exit(0)
- if opt['cambiar_contraseña']:
- _cambiar_contraseña()
- sys.exit(0)
+ # ~ if opt['borrar_rfc']:
+ # ~ _borrar_rfc()
+ # ~ sys.exit(0)
- if opt['agregar_rfc']:
- _agregar_rfc(no_bd)
- sys.exit(0)
+ # ~ if opt['listar_rfc']:
+ # ~ _listar_rfc(opt['detalle'])
+ # ~ sys.exit(0)
- if opt['borrar_rfc']:
- _borrar_rfc()
- sys.exit(0)
+ # ~ if opt['importar_valores']:
+ # ~ if not opt['archivo']:
+ # ~ msg = 'Falta la ruta del archivo importar'
+ # ~ raise click.ClickException(msg)
+ # ~ if not util.is_file(opt['archivo']):
+ # ~ msg = 'No es un archivo'
+ # ~ raise click.ClickException(msg)
- if opt['listar_rfc']:
- _listar_rfc(opt['detalle'])
- sys.exit(0)
+ # ~ _importar_valores(opt['archivo'])
+ # ~ sys.exit(0)
- if opt['importar_valores']:
- if not opt['archivo']:
- msg = 'Falta la ruta del archivo importar'
- raise click.ClickException(msg)
- if not util.is_file(opt['archivo']):
- msg = 'No es un archivo'
- raise click.ClickException(msg)
+ # ~ if opt['factura_libre']:
+ # ~ if not opt['archivo']:
+ # ~ msg = 'Falta la ruta de la base de datos'
+ # ~ raise click.ClickException(msg)
+ # ~ if not util.is_file(opt['archivo']):
+ # ~ msg = 'No es un archivo'
+ # ~ raise click.ClickException(msg)
+ # ~ _, _, _, ext = util.get_path_info(opt['archivo'])
+ # ~ if ext != '.sqlite':
+ # ~ msg = 'No es una base de datos'
+ # ~ raise click.ClickException(msg)
- _importar_valores(opt['archivo'])
- sys.exit(0)
+ # ~ _importar_factura_libre(opt['archivo'])
+ # ~ sys.exit(0)
- if opt['factura_libre']:
- if not opt['archivo']:
- msg = 'Falta la ruta de la base de datos'
- raise click.ClickException(msg)
- if not util.is_file(opt['archivo']):
- msg = 'No es un archivo'
- raise click.ClickException(msg)
- _, _, _, ext = util.get_path_info(opt['archivo'])
- if ext != '.sqlite':
- msg = 'No es una base de datos'
- raise click.ClickException(msg)
+ # ~ if opt['generar_archivo_productos']:
+ # ~ if not opt['archivo']:
+ # ~ msg = 'Falta la ruta de la base de datos'
+ # ~ raise click.ClickException(msg)
+ # ~ if not util.is_file(opt['archivo']):
+ # ~ msg = 'No es un archivo'
+ # ~ raise click.ClickException(msg)
+ # ~ _, _, _, ext = util.get_path_info(opt['archivo'])
+ # ~ if ext != '.sqlite':
+ # ~ msg = 'No es una base de datos'
+ # ~ raise click.ClickException(msg)
- _importar_factura_libre(opt['archivo'])
- sys.exit(0)
+ # ~ _generar_archivo_productos(opt['archivo'])
+ # ~ sys.exit(0)
- if opt['factura_libre_gambas']:
- if not opt['conexion']:
- msg = 'Falta los datos de conexión'
- raise click.ClickException(msg)
- _importar_factura_libre_gambas(opt['conexion'])
- sys.exit(0)
+ # ~ if opt['importar_productos']:
+ # ~ if not opt['archivo']:
+ # ~ msg = 'Falta la ruta del archivo'
+ # ~ raise click.ClickException(msg)
+ # ~ if not util.is_file(opt['archivo']):
+ # ~ msg = 'No es un archivo'
+ # ~ raise click.ClickException(msg)
+ # ~ _, _, _, ext = util.get_path_info(opt['archivo'])
+ # ~ if ext != '.txt':
+ # ~ msg = 'No es un archivo de texto'
+ # ~ raise click.ClickException(msg)
- if opt['generar_archivo_productos']:
- if not opt['archivo']:
- msg = 'Falta la ruta de la base de datos'
- raise click.ClickException(msg)
- if not util.is_file(opt['archivo']):
- msg = 'No es un archivo'
- raise click.ClickException(msg)
- _, _, _, ext = util.get_path_info(opt['archivo'])
- if ext != '.sqlite':
- msg = 'No es una base de datos'
- raise click.ClickException(msg)
+ # ~ _importar_productos(opt['archivo'])
+ # ~ sys.exit(0)
- _generar_archivo_productos(opt['archivo'])
- sys.exit(0)
+ # ~ if opt['importar_directorio']:
+ # ~ _import_from_folder(opt['importar_directorio'])
+ # ~ sys.exit(0)
- if opt['importar_productos']:
- if not opt['archivo']:
- msg = 'Falta la ruta del archivo'
- raise click.ClickException(msg)
- if not util.is_file(opt['archivo']):
- msg = 'No es un archivo'
- raise click.ClickException(msg)
- _, _, _, ext = util.get_path_info(opt['archivo'])
- if ext != '.txt':
- msg = 'No es un archivo de texto'
- raise click.ClickException(msg)
+ # ~ if opt['backup_dbs']:
+ # ~ utils.db_backup(DB_COMPANIES, PATHS['BK'], IS_MV, URL['SEAFILE'])
+ # ~ sys.exit(0)
- _importar_productos(opt['archivo'])
- sys.exit(0)
+ # ~ if opt['exportar_documentos']:
+ # ~ _exportar_documentos()
- if opt['importar_directorio']:
- _import_from_folder(opt['importar_directorio'])
- sys.exit(0)
-
- if opt['backup_dbs']:
- # ~ util.backup_dbs()
- utils.db_backup(DB_COMPANIES, PATHS['BK'], IS_MV, URL['SEAFILE'])
- sys.exit(0)
-
- if opt['exportar_documentos']:
- _exportar_documentos()
-
- return
+ # ~ return
if __name__ == '__main__':
- main()
+ args = _process_command_line_arguments()
+ main(args)
+ # ~ main2()
+
diff --git a/source/app/settings.py b/source/app/settings.py
index fcb3570..91ae2c8 100644
--- a/source/app/settings.py
+++ b/source/app/settings.py
@@ -47,7 +47,7 @@ except ImportError:
DEBUG = DEBUG
-VERSION = '1.32.0'
+VERSION = '1.33.0'
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
@@ -200,6 +200,8 @@ CURRENCY_MN = 'MXN'
IS_MV = MV
DB_COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
path_bk = os.path.join(path_docs, 'tmp')
+path_local = 'facturas'
+path_sat = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'valores_iniciales.json'))
EXT = {
'CSS': 'css',
@@ -218,6 +220,8 @@ PATHS = {
'USER': path_user_template,
'LOGOS': path_user_logos,
'BK': path_bk,
+ 'LOCAL': path_local,
+ 'SAT': path_sat,
}
VALUES_PDF = {
diff --git a/source/db/valores_iniciales.json b/source/db/valores_iniciales.json
index d6083f2..5b63380 100644
--- a/source/db/valores_iniciales.json
+++ b/source/db/valores_iniciales.json
@@ -117,102 +117,543 @@
]
},
{
- "tabla": "SATBancos",
"datos": [
- {"key": "002", "name": "BANAMEX", "razon_social": "Banco Nacional de México, S.A., Institución de Banca Múltiple, Grupo Financiero Banamex"},
- {"key": "006", "name": "BANCOMEXT", "razon_social": "Banco Nacional de Comercio Exterior, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
- {"key": "009", "name": "BANOBRAS", "razon_social": "Banco Nacional de Obras y Servicios Públicos, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
- {"key": "012", "name": "BBVABANCOMER", "razon_social": "BBVA Bancomer, S.A., Institución de Banca Múltiple, Grupo Financiero BBVA Bancomer"},
- {"key": "014", "name": "SANTANDER", "razon_social": "Banco Santander (México), S.A., Institución de Banca Múltiple, Grupo Financiero Santander"},
- {"key": "019", "name": "BANJERCITO", "razon_social": "Banco Nacional del Ejército, Fuerza Aérea y Armada, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
- {"key": "021", "name": "HSBC", "razon_social": "HSBC México, S.A., institución De Banca Múltiple, Grupo Financiero HSBC"},
- {"key": "030", "name": "BAJIO", "razon_social": "Banco del Bajío, S.A., Institución de Banca Múltiple"},
- {"key": "106", "name": "BAMSA", "razon_social": "Bank of America México, S.A., Institución de Banca Múltiple, Grupo Financiero Bank of America"},
- {"key": "032", "name": "IXE", "razon_social": "IXE Banco, S.A., Institución de Banca Múltiple, IXE Grupo Financiero"},
- {"key": "036", "name": "INBURSA", "razon_social": "Banco Inbursa, S.A., Institución de Banca Múltiple, Grupo Financiero Inbursa"},
- {"key": "037", "name": "INTERACCIONES", "razon_social": "Banco Interacciones, S.A., Institución de Banca Múltiple"},
- {"key": "042", "name": "MIFEL", "razon_social": "Banca Mifel, S.A., Institución de Banca Múltiple, Grupo Financiero Mifel"},
- {"key": "044", "name": "SCOTIABANK", "razon_social": "Scotiabank Inverlat, S.A."},
- {"key": "059", "name": "INVEX", "razon_social": "Banco Invex, S.A., Institución de Banca Múltiple, Invex Grupo Financiero"},
- {"key": "058", "name": "BANREGIO", "razon_social": "Banco Regional de Monterrey, S.A., Institución de Banca Múltiple, Banregio Grupo Financiero"},
- {"key": "060", "name": "BANSI", "razon_social": "Bansi, S.A., Institución de Banca Múltiple"},
- {"key": "062", "name": "AFIRME", "razon_social": "Banca Afirme, S.A., Institución de Banca Múltiple"},
- {"key": "072", "name": "BANORTE", "razon_social": "Banco Mercantil del Norte, S.A., Institución de Banca Múltiple, Grupo Financiero Banorte"},
- {"key": "102", "name": "THE ROYAL BANK", "razon_social": "The Royal Bank of Scotland México, S.A., Institución de Banca Múltiple"},
- {"key": "103", "name": "AMERICAN EXPRESS", "razon_social": "American Express Bank (México), S.A., Institución de Banca Múltiple"},
- {"key": "108", "name": "TOKYO", "razon_social": "Bank of Tokyo-Mitsubishi UFJ (México), S.A."},
- {"key": "110", "name": "JP MORGAN", "razon_social": "Banco J.P. Morgan, S.A., Institución de Banca Múltiple, J.P. Morgan Grupo Financiero"},
- {"key": "112", "name": "BMONEX", "razon_social": "Banco Monex, S.A., Institución de Banca Múltiple"},
- {"key": "113", "name": "VE POR MAS", "razon_social": "Banco Ve Por Mas, S.A. Institución de Banca Múltiple"},
- {"key": "116", "name": "ING", "razon_social": "ING Bank (México), S.A., Institución de Banca Múltiple, ING Grupo Financiero"},
- {"key": "124", "name": "DEUTSCHE", "razon_social": "Deutsche Bank México, S.A., Institución de Banca Múltiple"},
- {"key": "126", "name": "CREDIT SUISSE", "razon_social": "Banco Credit Suisse (México), S.A. Institución de Banca Múltiple, Grupo Financiero Credit Suisse (México)"},
- {"key": "127", "name": "AZTECA", "razon_social": "Banco Azteca, S.A. Institución de Banca Múltiple."},
- {"key": "128", "name": "AUTOFIN", "razon_social": "Banco Autofin México, S.A. Institución de Banca Múltiple"},
- {"key": "129", "name": "BARCLAYS", "razon_social": "Barclays Bank México, S.A., Institución de Banca Múltiple, Grupo Financiero Barclays México"},
- {"key": "130", "name": "COMPARTAMOS", "razon_social": "Banco Compartamos, S.A., Institución de Banca Múltiple"},
- {"key": "131", "name": "BANCO FAMSA", "razon_social": "Banco Ahorro Famsa, S.A., Institución de Banca Múltiple"},
- {"key": "132", "name": "BMULTIVA", "razon_social": "Banco Multiva, S.A., Institución de Banca Múltiple, Multivalores Grupo Financiero"},
- {"key": "133", "name": "ACTINVER", "razon_social": "Banco Actinver, S.A. Institución de Banca Múltiple, Grupo Financiero Actinver"},
- {"key": "134", "name": "WAL-MART", "razon_social": "Banco Wal-Mart de México Adelante, S.A., Institución de Banca Múltiple"},
- {"key": "135", "name": "NAFIN", "razon_social": "Nacional Financiera, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
- {"key": "136", "name": "INTERBANCO", "razon_social": "Inter Banco, S.A. Institución de Banca Múltiple"},
- {"key": "137", "name": "BANCOPPEL", "razon_social": "BanCoppel, S.A., Institución de Banca Múltiple"},
- {"key": "138", "name": "ABC CAPITAL", "razon_social": "ABC Capital, S.A., Institución de Banca Múltiple"},
- {"key": "139", "name": "UBS BANK", "razon_social": "UBS Bank México, S.A., Institución de Banca Múltiple, UBS Grupo Financiero"},
- {"key": "140", "name": "CONSUBANCO", "razon_social": "Consubanco, S.A. Institución de Banca Múltiple"},
- {"key": "141", "name": "VOLKSWAGEN", "razon_social": "Volkswagen Bank, S.A., Institución de Banca Múltiple"},
- {"key": "143", "name": "CIBANCO", "razon_social": "CIBanco, S.A."},
- {"key": "145", "name": "BBASE", "razon_social": "Banco Base, S.A., Institución de Banca Múltiple"},
- {"key": "166", "name": "BANSEFI", "razon_social": "Banco del Ahorro Nacional y Servicios Financieros, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
- {"key": "168", "name": "HIPOTECARIA FEDERAL", "razon_social": "Sociedad Hipotecaria Federal, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
- {"key": "600", "name": "MONEXCB", "razon_social": "Monex Casa de Bolsa, S.A. de C.V. Monex Grupo Financiero"},
- {"key": "601", "name": "GBM", "razon_social": "GBM Grupo Bursátil Mexicano, S.A. de C.V. Casa de Bolsa"},
- {"key": "602", "name": "MASARI", "razon_social": "Masari Casa de Bolsa, S.A."},
- {"key": "605", "name": "VALUE", "razon_social": "Value, S.A. de C.V. Casa de Bolsa"},
- {"key": "606", "name": "ESTRUCTURADORES", "razon_social": "Estructuradores del Mercado de Valores Casa de Bolsa, S.A. de C.V."},
- {"key": "607", "name": "TIBER", "razon_social": "Casa de Cambio Tiber, S.A. de C.V."},
- {"key": "608", "name": "VECTOR", "razon_social": "Vector Casa de Bolsa, S.A. de C.V."},
- {"key": "610", "name": "B&B", "razon_social": "B y B, Casa de Cambio, S.A. de C.V."},
- {"key": "614", "name": "ACCIVAL", "razon_social": "Acciones y Valores Banamex, S.A. de C.V., Casa de Bolsa"},
- {"key": "615", "name": "MERRILL LYNCH", "razon_social": "Merrill Lynch México, S.A. de C.V. Casa de Bolsa"},
- {"key": "616", "name": "FINAMEX", "razon_social": "Casa de Bolsa Finamex, S.A. de C.V."},
- {"key": "617", "name": "VALMEX", "razon_social": "Valores Mexicanos Casa de Bolsa, S.A. de C.V."},
- {"key": "618", "name": "UNICA", "razon_social": "Unica Casa de Cambio, S.A. de C.V."},
- {"key": "619", "name": "MAPFRE", "razon_social": "MAPFRE Tepeyac, S.A."},
- {"key": "620", "name": "PROFUTURO", "razon_social": "Profuturo G.N.P., S.A. de C.V., Afore"},
- {"key": "621", "name": "CB ACTINVER", "razon_social": "Actinver Casa de Bolsa, S.A. de C.V."},
- {"key": "622", "name": "OACTIN", "razon_social": "OPERADORA ACTINVER, S.A. DE C.V."},
- {"key": "623", "name": "SKANDIA", "razon_social": "Skandia Vida, S.A. de C.V."},
- {"key": "626", "name": "CBDEUTSCHE", "razon_social": "Deutsche Securities, S.A. de C.V. CASA DE BOLSA"},
- {"key": "627", "name": "ZURICH", "razon_social": "Zurich Compañía de Seguros, S.A."},
- {"key": "628", "name": "ZURICHVI", "razon_social": "Zurich Vida, Compañía de Seguros, S.A."},
- {"key": "629", "name": "SU CASITA", "razon_social": "Hipotecaria Su Casita, S.A. de C.V. SOFOM ENR"},
- {"key": "630", "name": "CB INTERCAM", "razon_social": "Intercam Casa de Bolsa, S.A. de C.V."},
- {"key": "631", "name": "CI BOLSA", "razon_social": "CI Casa de Bolsa, S.A. de C.V."},
- {"key": "632", "name": "BULLTICK CB", "razon_social": "Bulltick Casa de Bolsa, S.A., de C.V."},
- {"key": "633", "name": "STERLING", "razon_social": "Sterling Casa de Cambio, S.A. de C.V."},
- {"key": "634", "name": "FINCOMUN", "razon_social": "Fincomún, Servicios Financieros Comunitarios, S.A. de C.V."},
- {"key": "636", "name": "HDI SEGUROS", "razon_social": "HDI Seguros, S.A. de C.V."},
- {"key": "637", "name": "ORDER", "razon_social": "Order Express Casa de Cambio, S.A. de C.V"},
- {"key": "638", "name": "AKALA", "razon_social": "Akala, S.A. de C.V., Sociedad Financiera Popular"},
- {"key": "640", "name": "CB JPMORGAN", "razon_social": "J.P. Morgan Casa de Bolsa, S.A. de C.V. J.P. Morgan Grupo Financiero"},
- {"key": "642", "name": "REFORMA", "razon_social": "Operadora de Recursos Reforma, S.A. de C.V., S.F.P."},
- {"key": "646", "name": "STP", "razon_social": "Sistema de Transferencias y Pagos STP, S.A. de C.V.SOFOM ENR"},
- {"key": "647", "name": "TELECOMM", "razon_social": "Telecomunicaciones de México"},
- {"key": "648", "name": "EVERCORE", "razon_social": "Evercore Casa de Bolsa, S.A. de C.V."},
- {"key": "649", "name": "SKANDIA", "razon_social": "Skandia Operadora de Fondos, S.A. de C.V."},
- {"key": "651", "name": "SEGMTY", "razon_social": "Seguros Monterrey New York Life, S.A de C.V"},
- {"key": "652", "name": "ASEA", "razon_social": "Solución Asea, S.A. de C.V., Sociedad Financiera Popular"},
- {"key": "653", "name": "KUSPIT", "razon_social": "Kuspit Casa de Bolsa, S.A. de C.V."},
- {"key": "655", "name": "SOFIEXPRESS", "razon_social": "J.P. SOFIEXPRESS, S.A. de C.V., S.F.P."},
- {"key": "656", "name": "UNAGRA", "razon_social": "UNAGRA, S.A. de C.V., S.F.P."},
- {"key": "659", "name": "OPCIONES EMPRESARIALES DEL NOROESTE", "razon_social": "OPCIONES EMPRESARIALES DEL NORESTE, S.A. DE C.V., S.F.P."},
- {"key": "670", "name": "LIBERTAD", "razon_social": "Libertad Servicios Financieros, S.A. De C.V."},
- {"key": "901", "name": "CLS", "razon_social": "Cls Bank International"},
- {"key": "902", "name": "INDEVAL", "razon_social": "SD. Indeval, S.A. de C.V."},
- {"key": "999", "name": "N/A"}
- ]
+ {
+ "key": "002",
+ "name": "BANAMEX",
+ "razon_social": "Banco Nacional de M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Banamex"
+ },
+ {
+ "key": "006",
+ "name": "BANCOMEXT",
+ "razon_social": "Banco Nacional de Comercio Exterior, Sociedad Nacional de Cr\u00e9dito, Instituci\u00f3n de Banca de Desarrollo"
+ },
+ {
+ "key": "009",
+ "name": "BANOBRAS",
+ "razon_social": "Banco Nacional de Obras y Servicios P\u00fablicos, Sociedad Nacional de Cr\u00e9dito, Instituci\u00f3n de Banca de Desarrollo"
+ },
+ {
+ "key": "012",
+ "name": "BBVA BANCOMER",
+ "razon_social": "BBVA Bancomer, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero BBVA Bancomer"
+ },
+ {
+ "key": "014",
+ "name": "SANTANDER",
+ "razon_social": "Banco Santander (M\u00e9xico), S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Santander"
+ },
+ {
+ "key": "019",
+ "name": "BANJERCITO",
+ "razon_social": "Banco Nacional del Ej\u00e9rcito, Fuerza A\u00e9rea y Armada, Sociedad Nacional de Cr\u00e9dito, Instituci\u00f3n de Banca de Desarrollo"
+ },
+ {
+ "key": "021",
+ "name": "HSBC",
+ "razon_social": "HSBC M\u00e9xico, S.A., instituci\u00f3n De Banca M\u00faltiple, Grupo Financiero HSBC"
+ },
+ {
+ "key": "030",
+ "name": "BAJIO",
+ "razon_social": "Banco del Baj\u00edo, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "032",
+ "name": "IXE",
+ "razon_social": "IXE Banco, S.A., Instituci\u00f3n de Banca M\u00faltiple, IXE Grupo Financiero"
+ },
+ {
+ "key": "036",
+ "name": "INBURSA",
+ "razon_social": "Banco Inbursa, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Inbursa"
+ },
+ {
+ "key": "037",
+ "name": "INTERACCIONES",
+ "razon_social": "Banco Interacciones, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "042",
+ "name": "MIFEL",
+ "razon_social": "Banca Mifel, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Mifel"
+ },
+ {
+ "key": "044",
+ "name": "SCOTIABANK",
+ "razon_social": "Scotiabank Inverlat, S.A."
+ },
+ {
+ "key": "058",
+ "name": "BANREGIO",
+ "razon_social": "Banco Regional de Monterrey, S.A., Instituci\u00f3n de Banca M\u00faltiple, Banregio Grupo Financiero"
+ },
+ {
+ "key": "059",
+ "name": "INVEX",
+ "razon_social": "Banco Invex, S.A., Instituci\u00f3n de Banca M\u00faltiple, Invex Grupo Financiero"
+ },
+ {
+ "key": "060",
+ "name": "BANSI",
+ "razon_social": "Bansi, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "062",
+ "name": "AFIRME",
+ "razon_social": "Banca Afirme, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "072",
+ "name": "BANORTE/IXE",
+ "razon_social": "Banco Mercantil del Norte, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Banorte"
+ },
+ {
+ "key": "102",
+ "name": "THE ROYAL BANK",
+ "razon_social": "The Royal Bank of Scotland M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "103",
+ "name": "AMERICAN EXPRESS",
+ "razon_social": "American Express Bank (M\u00e9xico), S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "106",
+ "name": "BAMSA",
+ "razon_social": "Bank of America M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Bank of America"
+ },
+ {
+ "key": "108",
+ "name": "TOKYO",
+ "razon_social": "Bank of Tokyo-Mitsubishi UFJ (M\u00e9xico), S.A."
+ },
+ {
+ "key": "110",
+ "name": "JP MORGAN",
+ "razon_social": "Banco J.P. Morgan, S.A., Instituci\u00f3n de Banca M\u00faltiple, J.P. Morgan Grupo Financiero"
+ },
+ {
+ "key": "112",
+ "name": "BMONEX",
+ "razon_social": "Banco Monex, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "113",
+ "name": "VE POR MAS",
+ "razon_social": "Banco Ve Por Mas, S.A. Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "116",
+ "name": "ING",
+ "razon_social": "ING Bank (M\u00e9xico), S.A., Instituci\u00f3n de Banca M\u00faltiple, ING Grupo Financiero"
+ },
+ {
+ "key": "124",
+ "name": "DEUTSCHE",
+ "razon_social": "Deutsche Bank M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "126",
+ "name": "CREDIT SUISSE",
+ "razon_social": "Banco Credit Suisse (M\u00e9xico), S.A. Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Credit Suisse (M\u00e9xico)"
+ },
+ {
+ "key": "127",
+ "name": "AZTECA",
+ "razon_social": "Banco Azteca, S.A. Instituci\u00f3n de Banca M\u00faltiple."
+ },
+ {
+ "key": "128",
+ "name": "AUTOFIN",
+ "razon_social": "Banco Autofin M\u00e9xico, S.A. Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "129",
+ "name": "BARCLAYS",
+ "razon_social": "Barclays Bank M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Barclays M\u00e9xico"
+ },
+ {
+ "key": "130",
+ "name": "COMPARTAMOS",
+ "razon_social": "Banco Compartamos, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "131",
+ "name": "BANCO FAMSA",
+ "razon_social": "Banco Ahorro Famsa, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "132",
+ "name": "BMULTIVA",
+ "razon_social": "Banco Multiva, S.A., Instituci\u00f3n de Banca M\u00faltiple, Multivalores Grupo Financiero"
+ },
+ {
+ "key": "133",
+ "name": "ACTINVER",
+ "razon_social": "Banco Actinver, S.A. Instituci\u00f3n de Banca M\u00faltiple, Grupo Financiero Actinver"
+ },
+ {
+ "key": "134",
+ "name": "WAL-MART",
+ "razon_social": "Banco Wal-Mart de M\u00e9xico Adelante, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "135",
+ "name": "NAFIN",
+ "razon_social": "Nacional Financiera, Sociedad Nacional de Cr\u00e9dito, Instituci\u00f3n de Banca de Desarrollo"
+ },
+ {
+ "key": "136",
+ "name": "INTERCAM BANCO",
+ "razon_social": "Intercam Banco, S.A., Instituci\u00f3n de Banca M\u00faltiple, Intercam Grupo Financiero"
+ },
+ {
+ "key": "137",
+ "name": "BANCOPPEL",
+ "razon_social": "BanCoppel, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "138",
+ "name": "ABC CAPITAL",
+ "razon_social": "ABC Capital, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "139",
+ "name": "UBS BANK",
+ "razon_social": "UBS Bank M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple, UBS Grupo Financiero"
+ },
+ {
+ "key": "140",
+ "name": "CONSUBANCO",
+ "razon_social": "Consubanco, S.A. Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "141",
+ "name": "VOLKSWAGEN",
+ "razon_social": "Volkswagen Bank, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "143",
+ "name": "CIBANCO",
+ "razon_social": "CIBanco, S.A."
+ },
+ {
+ "key": "145",
+ "name": "BBASE",
+ "razon_social": "Banco Base, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "147",
+ "name": "BANKAOOL",
+ "razon_social": "Bankaool, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "148",
+ "name": "PAGATODO",
+ "razon_social": "Banco PagaTodo, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "149",
+ "name": "FORJADORES",
+ "razon_social": "Banco Forjadores, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "150",
+ "name": "INMOBILIARIO",
+ "razon_social": "Banco Inmobiliario Mexicano, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "151",
+ "name": "DOND\u00c9",
+ "razon_social": "Fundaci\u00f3n Dond\u00e9 Banco, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "152",
+ "name": "BANCREA",
+ "razon_social": "Banco Bancrea, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "153",
+ "name": "PROGRESO",
+ "razon_social": "Banco Progreso Chihuahua, S.A."
+ },
+ {
+ "key": "154",
+ "name": "BANCO FINTERRA",
+ "razon_social": "Banco Finterra, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "155",
+ "name": "ICBC",
+ "razon_social": "Industrial and Commercial Bank of China M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "156",
+ "name": "SABADELL",
+ "razon_social": "Banco Sabadell, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "157",
+ "name": "SHINHAN",
+ "razon_social": "Banco Shinhan de M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "158",
+ "name": "MIZUHO BANK",
+ "razon_social": "Mizuho Bank M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "159",
+ "name": "BANK OF CHINA",
+ "razon_social": "Bank of China M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "160",
+ "name": "BANCO S3",
+ "razon_social": "Banco S3 M\u00e9xico, S.A., Instituci\u00f3n de Banca M\u00faltiple"
+ },
+ {
+ "key": "166",
+ "name": "BANSEFI",
+ "razon_social": "Banco del Ahorro Nacional y Servicios Financieros, Sociedad Nacional de Cr\u00e9dito, Instituci\u00f3n de Banca de Desarrollo"
+ },
+ {
+ "key": "168",
+ "name": "HIPOTECARIA FEDERAL",
+ "razon_social": "Sociedad Hipotecaria Federal, Sociedad Nacional de Cr\u00e9dito, Instituci\u00f3n de Banca de Desarrollo"
+ },
+ {
+ "key": "600",
+ "name": "MONEXCB",
+ "razon_social": "Monex Casa de Bolsa, S.A. de C.V. Monex Grupo Financiero"
+ },
+ {
+ "key": "601",
+ "name": "GBM",
+ "razon_social": "GBM Grupo Burs\u00e1til Mexicano, S.A. de C.V. Casa de Bolsa"
+ },
+ {
+ "key": "602",
+ "name": "MASARI",
+ "razon_social": "Masari Casa de Bolsa, S.A."
+ },
+ {
+ "key": "605",
+ "name": "VALUE",
+ "razon_social": "Value, S.A. de C.V. Casa de Bolsa"
+ },
+ {
+ "key": "606",
+ "name": "ESTRUCTURADORES",
+ "razon_social": "Estructuradores del Mercado de Valores Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "607",
+ "name": "TIBER",
+ "razon_social": "Casa de Cambio Tiber, S.A. de C.V."
+ },
+ {
+ "key": "608",
+ "name": "VECTOR",
+ "razon_social": "Vector Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "610",
+ "name": "B&B",
+ "razon_social": "B y B, Casa de Cambio, S.A. de C.V."
+ },
+ {
+ "key": "614",
+ "name": "ACCIVAL",
+ "razon_social": "Acciones y Valores Banamex, S.A. de C.V., Casa de Bolsa"
+ },
+ {
+ "key": "615",
+ "name": "MERRILL LYNCH",
+ "razon_social": "Merrill Lynch M\u00e9xico, S.A. de C.V. Casa de Bolsa"
+ },
+ {
+ "key": "616",
+ "name": "FINAMEX",
+ "razon_social": "Casa de Bolsa Finamex, S.A. de C.V."
+ },
+ {
+ "key": "617",
+ "name": "VALMEX",
+ "razon_social": "Valores Mexicanos Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "618",
+ "name": "UNICA",
+ "razon_social": "Unica Casa de Cambio, S.A. de C.V."
+ },
+ {
+ "key": "619",
+ "name": "MAPFRE",
+ "razon_social": "MAPFRE Tepeyac, S.A."
+ },
+ {
+ "key": "620",
+ "name": "PROFUTURO",
+ "razon_social": "Profuturo G.N.P., S.A. de C.V., Afore"
+ },
+ {
+ "key": "621",
+ "name": "CB ACTINVER",
+ "razon_social": "Actinver Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "622",
+ "name": "OACTIN",
+ "razon_social": "OPERADORA ACTINVER, S.A. DE C.V."
+ },
+ {
+ "key": "623",
+ "name": "SKANDIA",
+ "razon_social": "Skandia Vida, S.A. de C.V."
+ },
+ {
+ "key": "626",
+ "name": "CBDEUTSCHE",
+ "razon_social": "Deutsche Securities, S.A. de C.V. CASA DE BOLSA"
+ },
+ {
+ "key": "627",
+ "name": "ZURICH",
+ "razon_social": "Zurich Compa\u00f1\u00eda de Seguros, S.A."
+ },
+ {
+ "key": "628",
+ "name": "ZURICHVI",
+ "razon_social": "Zurich Vida, Compa\u00f1\u00eda de Seguros, S.A."
+ },
+ {
+ "key": "629",
+ "name": "SU CASITA",
+ "razon_social": "Hipotecaria Su Casita, S.A. de C.V. SOFOM ENR"
+ },
+ {
+ "key": "630",
+ "name": "CB INTERCAM",
+ "razon_social": "Intercam Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "631",
+ "name": "CI BOLSA",
+ "razon_social": "CI Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "632",
+ "name": "BULLTICK CB",
+ "razon_social": "Bulltick Casa de Bolsa, S.A., de C.V."
+ },
+ {
+ "key": "633",
+ "name": "STERLING",
+ "razon_social": "Sterling Casa de Cambio, S.A. de C.V."
+ },
+ {
+ "key": "634",
+ "name": "FINCOMUN",
+ "razon_social": "Fincom\u00fan, Servicios Financieros Comunitarios, S.A. de C.V."
+ },
+ {
+ "key": "636",
+ "name": "HDI SEGUROS",
+ "razon_social": "HDI Seguros, S.A. de C.V."
+ },
+ {
+ "key": "637",
+ "name": "ORDER",
+ "razon_social": "Order Express Casa de Cambio, S.A. de C.V"
+ },
+ {
+ "key": "638",
+ "name": "AKALA",
+ "razon_social": "Akala, S.A. de C.V., Sociedad Financiera Popular"
+ },
+ {
+ "key": "640",
+ "name": "CB JPMORGAN",
+ "razon_social": "J.P. Morgan Casa de Bolsa, S.A. de C.V. J.P. Morgan Grupo Financiero"
+ },
+ {
+ "key": "642",
+ "name": "REFORMA",
+ "razon_social": "Operadora de Recursos Reforma, S.A. de C.V., S.F.P."
+ },
+ {
+ "key": "646",
+ "name": "STP",
+ "razon_social": "Sistema de Transferencias y Pagos STP, S.A. de C.V.SOFOM ENR"
+ },
+ {
+ "key": "647",
+ "name": "TELECOMM",
+ "razon_social": "Telecomunicaciones de M\u00e9xico"
+ },
+ {
+ "key": "648",
+ "name": "EVERCORE",
+ "razon_social": "Evercore Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "649",
+ "name": "SKANDIA",
+ "razon_social": "Skandia Operadora de Fondos, S.A. de C.V."
+ },
+ {
+ "key": "651",
+ "name": "SEGMTY",
+ "razon_social": "Seguros Monterrey New York Life, S.A de C.V"
+ },
+ {
+ "key": "652",
+ "name": "ASEA",
+ "razon_social": "Soluci\u00f3n Asea, S.A. de C.V., Sociedad Financiera Popular"
+ },
+ {
+ "key": "653",
+ "name": "KUSPIT",
+ "razon_social": "Kuspit Casa de Bolsa, S.A. de C.V."
+ },
+ {
+ "key": "655",
+ "name": "SOFIEXPRESS",
+ "razon_social": "J.P. SOFIEXPRESS, S.A. de C.V., S.F.P."
+ },
+ {
+ "key": "656",
+ "name": "UNAGRA",
+ "razon_social": "UNAGRA, S.A. de C.V., S.F.P."
+ },
+ {
+ "key": "659",
+ "name": "OPCIONES EMPRESARIALES DEL NOROESTE",
+ "razon_social": "OPCIONES EMPRESARIALES DEL NORESTE, S.A. DE C.V., S.F.P."
+ },
+ {
+ "key": "670",
+ "name": "LIBERTAD",
+ "razon_social": "Libertad Servicios Financieros, S.A. De C.V."
+ },
+ {
+ "key": "901",
+ "name": "CLS",
+ "razon_social": "Cls Bank International"
+ },
+ {
+ "key": "902",
+ "name": "INDEVAL",
+ "razon_social": "SD. Indeval, S.A. de C.V."
+ },
+ {
+ "key": "999",
+ "name": "N/A"
+ }
+ ],
+ "tabla": "SATBancos"
},
{
"tabla": "SATUsoCfdi",
@@ -660,12 +1101,12 @@
},
{
"key": "QC",
- "name": "Quebec\u00a0",
+ "name": "\u00a0Quebec\u00a0",
"pais": "CAN"
},
{
"key": "NS",
- "name": "Nueva Escocia",
+ "name": "\u00a0Nueva Escocia",
"pais": "CAN"
},
{
@@ -675,47 +1116,47 @@
},
{
"key": "MB",
- "name": "Manitoba",
+ "name": "\u00a0Manitoba",
"pais": "CAN"
},
{
"key": "BC",
- "name": "Columbia Brit\u00e1nica",
+ "name": "\u00a0Columbia Brit\u00e1nica",
"pais": "CAN"
},
{
"key": "PE",
- "name": "Isla del Pr\u00edncipe Eduardo",
+ "name": "\u00a0Isla del Pr\u00edncipe Eduardo",
"pais": "CAN"
},
{
"key": "SK",
- "name": "Saskatchewan",
+ "name": "\u00a0Saskatchewan",
"pais": "CAN"
},
{
"key": "AB",
- "name": "Alberta",
+ "name": "\u00a0Alberta",
"pais": "CAN"
},
{
"key": "NL",
- "name": "Terranova y Labrador",
+ "name": "\u00a0Terranova y Labrador",
"pais": "CAN"
},
{
"key": "NT",
- "name": "Territorios del Noroeste",
+ "name": "\u00a0Territorios del Noroeste",
"pais": "CAN"
},
{
"key": "YT",
- "name": "Yuk\u00f3n",
+ "name": "\u00a0Yuk\u00f3n",
"pais": "CAN"
},
{
"key": "UN",
- "name": "Nunavut",
+ "name": "\u00a0Nunavut",
"pais": "CAN"
}
],
@@ -865,6 +1306,155 @@
],
"tabla": "SATTipoContrato"
},
+{
+ "datos": [
+ {
+ "key": "01",
+ "name": "Dobles"
+ },
+ {
+ "key": "02",
+ "name": "Triples"
+ },
+ {
+ "key": "03",
+ "name": "Simples"
+ }
+ ],
+ "tabla": "SATTipoHoras"
+},
+{
+ "datos": [
+ {
+ "key": "01",
+ "name": "Riesgo de trabajo."
+ },
+ {
+ "key": "02",
+ "name": "Enfermedad en general."
+ },
+ {
+ "key": "03",
+ "name": "Maternidad."
+ },
+ {
+ "key": "04",
+ "name": "Licencia por cuidados m\u00e9dicos de hijos diagnosticados con c\u00e1ncer."
+ }
+ ],
+ "tabla": "SATTipoIncapacidad"
+},
+{
+ "datos": [
+ {
+ "key": "01",
+ "name": "Diurna"
+ },
+ {
+ "key": "02",
+ "name": "Nocturna"
+ },
+ {
+ "key": "03",
+ "name": "Mixta"
+ },
+ {
+ "key": "04",
+ "name": "Por hora"
+ },
+ {
+ "key": "05",
+ "name": "Reducida"
+ },
+ {
+ "key": "06",
+ "name": "Continuada"
+ },
+ {
+ "key": "07",
+ "name": "Partida"
+ },
+ {
+ "key": "08",
+ "name": "Por turnos"
+ },
+ {
+ "key": "99",
+ "name": "Otra Jornada"
+ }
+ ],
+ "tabla": "SATTipoJornada"
+},
+{
+ "datos": [
+ {
+ "key": "O",
+ "name": "N\u00f3mina ordinaria"
+ },
+ {
+ "key": "E",
+ "name": "N\u00f3mina extraordinaria"
+ }
+ ],
+ "tabla": "SATTipoNomina"
+},
+{
+ "datos": [
+ {
+ "key": "02",
+ "name": "Sueldos"
+ },
+ {
+ "key": "03",
+ "name": "Jubilados"
+ },
+ {
+ "key": "04",
+ "name": "Pensionados"
+ },
+ {
+ "key": "05",
+ "name": "Asimilados Miembros Sociedades Cooperativas Produccion"
+ },
+ {
+ "key": "06",
+ "name": "Asimilados Integrantes Sociedades Asociaciones Civiles"
+ },
+ {
+ "key": "07",
+ "name": "Asimilados Miembros consejos"
+ },
+ {
+ "key": "08",
+ "name": "Asimilados comisionistas"
+ },
+ {
+ "key": "09",
+ "name": "Asimilados Honorarios"
+ },
+ {
+ "key": "10",
+ "name": "Asimilados acciones"
+ },
+ {
+ "key": "11",
+ "name": "Asimilados otros"
+ },
+ {
+ "key": "12",
+ "name": "Jubilados o Pensionados"
+ },
+ {
+ "key": "13",
+ "name": "Indemnización o Separación"
+ },
+ {
+ "key": "99",
+ "name": "Otro Regimen"
+ }
+ ],
+ "tabla": "SATTipoRegimen"
+},
{
"datos": [
{
@@ -1125,11 +1715,11 @@
},
{
"key": "065",
- "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro Exento"
+ "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro en una sola exhibici\u00f3n Exento "
},
{
"key": "066",
- "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro Gravado"
+ "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro en una sola exhibici\u00f3n Gravado"
},
{
"key": "067",
@@ -1141,11 +1731,11 @@
},
{
"key": "069",
- "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro Acumulable"
+ "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro en parcialidades Exento"
},
{
"key": "070",
- "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro No acumulable"
+ "name": "Ajuste en Jubilaciones, pensiones o haberes de retiro en parcialidades Gravado"
},
{
"key": "071",
@@ -1273,115 +1863,31 @@
},
{
"key": "102",
- "name": "Ajuste a pagos por gratificaciones, primas, compensaciones, recompensas u otros a extrabajadores derivados de jubilación en parcialidades, gravados"
+ "name": "Ajuste a pagos por gratificaciones, primas, compensaciones, recompensas u otros a extrabajadores derivados de jubilaci\u00f3n en parcialidades, gravados"
},
{
"key": "103",
- "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilación en parcialidades derivados de la ejecución de una resolución judicial o de un laudo gravados"
+ "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilaci\u00f3n en parcialidades derivados de la ejecuci\u00f3n de una resoluci\u00f3n judicial o de un laudo gravados"
},
{
"key": "104",
- "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilación en parcialidades derivados de la ejecución de una resolución judicial o de un laudo exentos"
+ "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilaci\u00f3n en parcialidades derivados de la ejecuci\u00f3n de una resoluci\u00f3n judicial o de un laudo exentos"
},
{
"key": "105",
- "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilación en una sola exhibición derivados de la ejecución de una resolución judicial o de un laudo gravados"
+ "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilaci\u00f3n en una sola exhibici\u00f3n derivados de la ejecuci\u00f3n de una resoluci\u00f3n judicial o de un laudo gravados"
},
{
"key": "106",
- "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilación en una sola exhibición derivados de la ejecución de una resolución judicial o de un laudo exentos"
+ "name": "Ajuste a pagos que se realicen a extrabajadores que obtengan una jubilaci\u00f3n en una sola exhibici\u00f3n derivados de la ejecuci\u00f3n de una resoluci\u00f3n judicial o de un laudo exentos"
+ },
+ {
+ "key": "107",
+ "name": "Ajuste al Subsidio Causado "
}
],
"tabla": "SATTipoDeduccion"
},
-{
- "datos": [
- {
- "key": "01",
- "name": "Dobles"
- },
- {
- "key": "02",
- "name": "Triples"
- },
- {
- "key": "03",
- "name": "Simples"
- }
- ],
- "tabla": "SATTipoHoras"
-},
-{
- "datos": [
- {
- "key": "01",
- "name": "Riesgo de trabajo."
- },
- {
- "key": "02",
- "name": "Enfermedad en general."
- },
- {
- "key": "03",
- "name": "Maternidad."
- }
- ],
- "tabla": "SATTipoIncapacidad"
-},
-{
- "datos": [
- {
- "key": "01",
- "name": "Diurna"
- },
- {
- "key": "02",
- "name": "Nocturna"
- },
- {
- "key": "03",
- "name": "Mixta"
- },
- {
- "key": "04",
- "name": "Por hora"
- },
- {
- "key": "05",
- "name": "Reducida"
- },
- {
- "key": "06",
- "name": "Continuada"
- },
- {
- "key": "07",
- "name": "Partida"
- },
- {
- "key": "08",
- "name": "Por turnos"
- },
- {
- "key": "99",
- "name": "Otra Jornada"
- }
- ],
- "tabla": "SATTipoJornada"
-},
-{
- "datos": [
- {
- "key": "O",
- "name": "N\u00f3mina ordinaria"
- },
- {
- "key": "E",
- "name": "N\u00f3mina extraordinaria"
- }
- ],
- "tabla": "SATTipoNomina"
-},
{
"datos": [
{
@@ -1404,6 +1910,18 @@
"key": "005",
"name": "Reintegro de ISR retenido en exceso de ejercicio anterior (siempre que no haya sido enterado al SAT)."
},
+ {
+ "key": "006",
+ "name": "Alimentos en bienes (Servicios de comedor y comida) Art 94 \u00faltimo p\u00e1rrafo LISR"
+ },
+ {
+ "key": "007",
+ "name": "ISR ajustado por subsidio"
+ },
+ {
+ "key": "008",
+ "name": "Subsidio efectivamente entregado que no correspond\u00eda (Aplica s\u00f3lo cuando haya ajuste al cierre de mes en relaci\u00f3n con el Ap\u00e9ndice 7 de la gu\u00eda de llenado de n\u00f3mina)"
+ },
{
"key": "999",
"name": "Pagos distintos a los listados y que no deben considerarse como ingreso por sueldos, salarios o ingresos asimilados."
@@ -1563,7 +2081,7 @@
},
{
"key": "047",
- "name": "Alimentaci\u00f3n"
+ "name": "Alimentaci\u00f3n diferentes a los establecidos en el Art 94 \u00faltimo p\u00e1rrafo LISR"
},
{
"key": "048",
@@ -1579,74 +2097,17 @@
},
{
"key": "051",
- "name": "Pagos por gratificaciones, primas, compensaciones, recompensas u otros a extrabajadores derivados de jubilación en parcialidades"
+ "name": "Pagos por gratificaciones, primas, compensaciones, recompensas u otros a extrabajadores derivados de jubilaci\u00f3n en parcialidades"
},
{
"key": "052",
- "name": "Pagos que se realicen a extrabajadores que obtengan una jubilación en parcialidades derivados de la ejecución de resoluciones judicial o de un laudo"
+ "name": "Pagos que se realicen a extrabajadores que obtengan una jubilaci\u00f3n en parcialidades derivados de la ejecuci\u00f3n de resoluciones judicial o de un laudo"
},
{
"key": "053",
- "name": "Pagos que se realicen a extrabajadores que obtengan una jubilación en una sola exhibición derivados de la ejecución de resoluciones judicial o de un laudo"
+ "name": "Pagos que se realicen a extrabajadores que obtengan una jubilaci\u00f3n en una sola exhibici\u00f3n derivados de la ejecuci\u00f3n de resoluciones judicial o de un laudo"
}
],
"tabla": "SATTipoPercepcion"
-},
-{
- "datos": [
- {
- "key": "02",
- "name": "Sueldos"
- },
- {
- "key": "03",
- "name": "Jubilados"
- },
- {
- "key": "04",
- "name": "Pensionados"
- },
- {
- "key": "05",
- "name": "Asimilados Miembros Sociedades Cooperativas Produccion"
- },
- {
- "key": "06",
- "name": "Asimilados Integrantes Sociedades Asociaciones Civiles"
- },
- {
- "key": "07",
- "name": "Asimilados Miembros consejos"
- },
- {
- "key": "08",
- "name": "Asimilados comisionistas"
- },
- {
- "key": "09",
- "name": "Asimilados Honorarios"
- },
- {
- "key": "10",
- "name": "Asimilados acciones"
- },
- {
- "key": "11",
- "name": "Asimilados otros"
- },
- {
- "key": "12",
- "name": "Jubilados o Pensionados"
- },
- {
- "key": "13",
- "name": "Indemnización o Separación"
- },
- {
- "key": "99",
- "name": "Otro Regimen"
- }
- ],
- "tabla": "SATTipoRegimen"
}
]
diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js
index 650b713..73597a4 100644
--- a/source/static/js/controller/admin.js
+++ b/source/static/js/controller/admin.js
@@ -120,6 +120,7 @@ var controllers = {
$$('txt_config_cfdipay_serie').attachEvent('onKeyPress', txt_config_cfdipay_serie_press)
$$('txt_config_cfdipay_folio').attachEvent('onKeyPress', txt_config_cfdipay_folio_press)
$$('chk_usar_nomina').attachEvent('onItemClick', chk_config_item_click)
+ $$('lst_pac').attachEvent('onChange', lst_pac_on_change)
$$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click)
$$('cmd_subir_cfdixml').attachEvent('onItemClick', cmd_subir_cfdixml_click)
@@ -464,7 +465,11 @@ function get_config_values(opt){
success: function(text, data, xhr) {
var values = data.json()
Object.keys(values).forEach(function(key){
- $$(key).setValue(values[key])
+ if(key=='lst_pac'){
+ set_value(key, values[key])
+ }else{
+ $$(key).setValue(values[key])
+ }
})
}
})
@@ -2534,3 +2539,32 @@ function opt_make_pdf_from_on_change(new_value, old_value){
}
+function lst_pac_on_change(nv, ov){
+ if(nv=='default'){
+ webix.ajax().del('/config', {id: 'lst_pac'}, function(text, xml, xhr){
+ var msg = 'PAC predeterminado establecido correctamente'
+ if(xhr.status == 200){
+ msg_ok(msg)
+ }else{
+ msg = 'No se pudo eliminar'
+ msg_error(msg)
+ }
+ })
+ }else{
+ webix.ajax().post('/config', {'lst_pac': nv}, {
+ 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 = 'PAC establecido correctamente'
+ msg_ok(msg)
+ }else{
+ msg_error(values.msg)
+ }
+ }
+ })
+ }
+}
diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js
index e74b00e..9150608 100644
--- a/source/static/js/controller/util.js
+++ b/source/static/js/controller/util.js
@@ -579,3 +579,12 @@ function lst_clear(lst){
function lst_parse(lst, values){
lst.getList().parse(values)
}
+
+
+function set_value(control, value){
+ obj = $$(control)
+ obj.blockEvent()
+ obj.setValue(value)
+ obj.unblockEvent()
+}
+
diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js
index da12a80..4affa44 100644
--- a/source/static/js/ui/admin.js
+++ b/source/static/js/ui/admin.js
@@ -643,6 +643,12 @@ var options_templates = [
{}]
+var options_pac = [
+ {id: 'default', value: 'Predeterminado'},
+ {id: 'comercio', value: 'Comercio Digital'},
+]
+
+
var options_admin_otros = [
{maxHeight: 15},
{template: 'Facturación', type: 'section'},
@@ -674,6 +680,13 @@ var options_admin_otros = [
{view: 'checkbox', id: 'chk_config_decimales_precios', labelWidth: 0,
labelRight: 'Precios con 4 decimales'}, {},
]},
+ {maxHeight: 15},
+ {cols: [{maxWidth: 15},
+ {view: 'richselect', id: 'lst_pac', name: 'lst_pac', width: 300,
+ label: 'PAC: ', value: 'default', required: false,
+ options: options_pac},
+ {},
+ ]},
{maxHeight: 20},
{template: 'Ayudas varias', type: 'section'},
{cols: [{maxWidth: 15},
@@ -700,15 +713,6 @@ var options_admin_otros = [
labelRight: 'Mostrar total arriba'},
{}]},
{maxHeight: 20},
- {template: 'Nómina', type: 'section'},
- {cols: [{maxWidth: 15},
- {view: 'checkbox', id: 'chk_usar_nomina', labelWidth: 0,
- labelRight: 'Usar timbrado de Nómina'},
- {view: 'text', id: 'txt_config_nomina_serie', name: 'config_nomina_serie',
- label: 'Serie', labelWidth: 50, labelAlign: 'right'},
- {view: 'text', id: 'txt_config_nomina_folio', name: 'config_nomina_folio',
- label: 'Folio', labelWidth: 50, labelAlign: 'right'},
- {}]},
{}]
@@ -738,12 +742,15 @@ var options_admin_products = [
var options_admin_complements = [
{maxHeight: 20},
+ {template: 'Complemento de Nómina', type: 'section'},
{cols: [{maxWidth: 15},
- {view: 'checkbox', id: 'chk_config_ine', labelWidth: 0,
- labelRight: 'Usar el complemento INE'},
- {view: 'checkbox', id: 'chk_config_edu', labelWidth: 0,
- labelRight: 'Usar el complemento EDU'},
- {}]},
+ {view: 'checkbox', id: 'chk_usar_nomina', labelWidth: 0,
+ labelRight: 'Usar complemento de Nómina'},
+ {view: 'text', id: 'txt_config_nomina_serie', name: 'config_nomina_serie',
+ label: 'Serie', labelWidth: 50, labelAlign: 'right'},
+ {view: 'text', id: 'txt_config_nomina_folio', name: 'config_nomina_folio',
+ label: 'Folio', labelWidth: 50, labelAlign: 'right'},
+ {maxWidth: 15}]},
{maxHeight: 20},
{template: 'Complemento de Pagos', type: 'section'},
{cols: [{maxWidth: 15},
@@ -762,6 +769,18 @@ var options_admin_complements = [
{view: 'checkbox', id: 'chk_config_divisas', labelWidth: 0,
labelRight: 'Usar complemento de divisas'},
{maxWidth: 15}]},
+ {maxHeight: 20},
+ {template: 'Complemento INE', type: 'section'},
+ {cols: [{maxWidth: 15},
+ {view: 'checkbox', id: 'chk_config_ine', labelWidth: 0,
+ labelRight: 'Usar el complemento INE'},
+ {maxWidth: 15}]},
+ {maxHeight: 20},
+ {template: 'Complemento para escuelas EDU', type: 'section'},
+ {cols: [{maxWidth: 15},
+ {view: 'checkbox', id: 'chk_config_edu', labelWidth: 0,
+ labelRight: 'Usar el complemento EDU'},
+ {maxWidth: 15}]},
]