Soporte basico para Comercio Exterior
This commit is contained in:
parent
bebcd29710
commit
1991c68b3b
|
@ -1,3 +1,9 @@
|
||||||
|
v 2.0.0 [31-Mar-2022]
|
||||||
|
----------------------
|
||||||
|
- Primera versión de timbrado con CFDI4
|
||||||
|
- **IMPORTANTE** NO intentes timbrar si **antes** no has validado en nuestro demo que puedes timbrar tus CFDIs habituales.
|
||||||
|
|
||||||
|
|
||||||
v 1.47.0 [28-Mar-2022]
|
v 1.47.0 [28-Mar-2022]
|
||||||
----------------------
|
----------------------
|
||||||
- Mejora: Soporte basico para complemento Comercio Exterior.
|
- Mejora: Soporte basico para complemento Comercio Exterior.
|
||||||
|
|
|
@ -25,16 +25,21 @@ from logbook import Logger
|
||||||
|
|
||||||
|
|
||||||
log = Logger('XML')
|
log = Logger('XML')
|
||||||
CFDI_ACTUAL = 'cfdi33'
|
CFDI_ACTUAL = 'cfdi40'
|
||||||
NOMINA_ACTUAL = 'nomina12'
|
NOMINA_ACTUAL = 'nomina12'
|
||||||
|
|
||||||
|
DEFAULT = {
|
||||||
|
'exportacion': '01',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SAT = {
|
SAT = {
|
||||||
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
'cfdi32': {
|
'cfdi40': {
|
||||||
'version': '3.2',
|
'version': '4.0',
|
||||||
'prefix': 'cfdi',
|
'prefix': 'cfdi',
|
||||||
'xmlns': 'http://www.sat.gob.mx/cfd/3',
|
'xmlns': 'http://www.sat.gob.mx/cfd/4',
|
||||||
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd',
|
'schema': 'http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd',
|
||||||
},
|
},
|
||||||
'cfdi33': {
|
'cfdi33': {
|
||||||
'version': '3.3',
|
'version': '3.3',
|
||||||
|
@ -42,6 +47,12 @@ SAT = {
|
||||||
'xmlns': 'http://www.sat.gob.mx/cfd/3',
|
'xmlns': 'http://www.sat.gob.mx/cfd/3',
|
||||||
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd',
|
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd',
|
||||||
},
|
},
|
||||||
|
'cfdi32': {
|
||||||
|
'version': '3.2',
|
||||||
|
'prefix': 'cfdi',
|
||||||
|
'xmlns': 'http://www.sat.gob.mx/cfd/3',
|
||||||
|
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd',
|
||||||
|
},
|
||||||
'nomina11': {
|
'nomina11': {
|
||||||
'version': '1.1',
|
'version': '1.1',
|
||||||
'prefix': 'nomina',
|
'prefix': 'nomina',
|
||||||
|
@ -270,6 +281,10 @@ class CFDI(object):
|
||||||
if not 'Fecha' in attributes:
|
if not 'Fecha' in attributes:
|
||||||
attributes['Fecha'] = self._now()
|
attributes['Fecha'] = self._now()
|
||||||
|
|
||||||
|
# ~ cfdi4
|
||||||
|
if not 'Exportacion' in attributes:
|
||||||
|
attributes['Exportacion'] = DEFAULT['exportacion']
|
||||||
|
|
||||||
self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes)
|
self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -797,3 +797,15 @@ class AppSATUnidadesPeso(object):
|
||||||
user = req.env['beaker.session']['userobj']
|
user = req.env['beaker.session']['userobj']
|
||||||
req.context['result'] = self._db.sat_unidades_peso_post(values, user)
|
req.context['result'] = self._db.sat_unidades_peso_post(values, user)
|
||||||
resp.status = falcon.HTTP_200
|
resp.status = falcon.HTTP_200
|
||||||
|
|
||||||
|
|
||||||
|
class AppSATRegimenes(object):
|
||||||
|
|
||||||
|
def __init__(self, db):
|
||||||
|
self._db = db
|
||||||
|
|
||||||
|
def on_get(self, req, resp):
|
||||||
|
values = req.params
|
||||||
|
user = req.env['beaker.session']['userobj']
|
||||||
|
req.context['result'] = self._db.sat_regimenes_get(values, user)
|
||||||
|
resp.status = falcon.HTTP_200
|
||||||
|
|
|
@ -78,6 +78,7 @@ import segno
|
||||||
from .pacs.cfdi_cert import SATCertificate
|
from .pacs.cfdi_cert import SATCertificate
|
||||||
|
|
||||||
from settings import (
|
from settings import (
|
||||||
|
CFDI_VERSIONS,
|
||||||
EXT,
|
EXT,
|
||||||
MXN,
|
MXN,
|
||||||
PATHS,
|
PATHS,
|
||||||
|
@ -1754,7 +1755,7 @@ def _comprobante(doc, options):
|
||||||
data['tiporelacion'] = options.get('tiporelacion', '')
|
data['tiporelacion'] = options.get('tiporelacion', '')
|
||||||
return data
|
return data
|
||||||
|
|
||||||
if data['version'] == '3.3':
|
if data['version'] in CFDI_VERSIONS:
|
||||||
tipos = {
|
tipos = {
|
||||||
'I': 'ingreso',
|
'I': 'ingreso',
|
||||||
'E': 'egreso',
|
'E': 'egreso',
|
||||||
|
@ -1857,7 +1858,7 @@ def _conceptos(doc, version, options):
|
||||||
data.append(values)
|
data.append(values)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if version == '3.3':
|
if version in CFDI_VERSIONS:
|
||||||
if 'noidentificacion' in values:
|
if 'noidentificacion' in values:
|
||||||
values['noidentificacion'] = '{}\n(SAT {})'.format(
|
values['noidentificacion'] = '{}\n(SAT {})'.format(
|
||||||
values['noidentificacion'], values['ClaveProdServ'])
|
values['noidentificacion'], values['ClaveProdServ'])
|
||||||
|
@ -1921,7 +1922,7 @@ def _totales(doc, cfdi, version):
|
||||||
# ~ for n in node.getchildren():
|
# ~ for n in node.getchildren():
|
||||||
for n in list(node):
|
for n in list(node):
|
||||||
tmp = CaseInsensitiveDict(n.attrib.copy())
|
tmp = CaseInsensitiveDict(n.attrib.copy())
|
||||||
if version == '3.3':
|
if version in CFDI_VERSIONS:
|
||||||
tasa = round(float(tmp['tasaocuota']), DECIMALES)
|
tasa = round(float(tmp['tasaocuota']), DECIMALES)
|
||||||
title = 'Traslado {} {}'.format(tn.get(tmp['impuesto']), tasa)
|
title = 'Traslado {} {}'.format(tn.get(tmp['impuesto']), tasa)
|
||||||
else:
|
else:
|
||||||
|
@ -1933,7 +1934,7 @@ def _totales(doc, cfdi, version):
|
||||||
# ~ for n in node.getchildren():
|
# ~ for n in node.getchildren():
|
||||||
for n in list(node):
|
for n in list(node):
|
||||||
tmp = CaseInsensitiveDict(n.attrib.copy())
|
tmp = CaseInsensitiveDict(n.attrib.copy())
|
||||||
if version == '3.3':
|
if version in CFDI_VERSIONS:
|
||||||
title = 'Retención {} {}'.format(
|
title = 'Retención {} {}'.format(
|
||||||
tn.get(tmp['impuesto']), '')
|
tn.get(tmp['impuesto']), '')
|
||||||
else:
|
else:
|
||||||
|
@ -1965,7 +1966,7 @@ def _totales(doc, cfdi, version):
|
||||||
|
|
||||||
def _timbre(doc, version, values, pdf_from='1'):
|
def _timbre(doc, version, values, pdf_from='1'):
|
||||||
CADENA = '||{version}|{UUID}|{FechaTimbrado}|{selloCFD}|{noCertificadoSAT}||'
|
CADENA = '||{version}|{UUID}|{FechaTimbrado}|{selloCFD}|{noCertificadoSAT}||'
|
||||||
if version == '3.3':
|
if version in CFDI_VERSIONS:
|
||||||
CADENA = '||{Version}|{UUID}|{FechaTimbrado}|{SelloCFD}|{NoCertificadoSAT}||'
|
CADENA = '||{Version}|{UUID}|{FechaTimbrado}|{SelloCFD}|{NoCertificadoSAT}||'
|
||||||
node = doc.find('{}Complemento/{}TimbreFiscalDigital'.format(
|
node = doc.find('{}Complemento/{}TimbreFiscalDigital'.format(
|
||||||
PRE[version], PRE['TIMBRE']))
|
PRE[version], PRE['TIMBRE']))
|
||||||
|
|
|
@ -255,8 +255,11 @@ class SendMail(object):
|
||||||
|
|
||||||
|
|
||||||
class CfdiToDict(object):
|
class CfdiToDict(object):
|
||||||
|
NS_VERSION = {
|
||||||
|
'cfdi3.3': 'http://www.sat.gob.mx/cfd/3',
|
||||||
|
'cfdi4.0': 'http://www.sat.gob.mx/cfd/4',
|
||||||
|
}
|
||||||
NS = {
|
NS = {
|
||||||
'cfdi': 'http://www.sat.gob.mx/cfd/3',
|
|
||||||
'divisas': 'http://www.sat.gob.mx/divisas',
|
'divisas': 'http://www.sat.gob.mx/divisas',
|
||||||
'leyendasFisc': 'http://www.sat.gob.mx/leyendasFiscales',
|
'leyendasFisc': 'http://www.sat.gob.mx/leyendasFiscales',
|
||||||
'cartaporte20': 'http://www.sat.gob.mx/CartaPorte20',
|
'cartaporte20': 'http://www.sat.gob.mx/CartaPorte20',
|
||||||
|
@ -318,6 +321,9 @@ class CfdiToDict(object):
|
||||||
return self._values
|
return self._values
|
||||||
|
|
||||||
def _get_values(self):
|
def _get_values(self):
|
||||||
|
version = self._root.attrib['Version']
|
||||||
|
ns = f'cfdi{version}'
|
||||||
|
self.NS['cfdi'] = self.NS_VERSION[ns]
|
||||||
self._complementos()
|
self._complementos()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ from controllers.main import (AppEmpresas,
|
||||||
AppWareHouse,
|
AppWareHouse,
|
||||||
AppWareHouseProduct,
|
AppWareHouseProduct,
|
||||||
AppSATUnidadesPeso,
|
AppSATUnidadesPeso,
|
||||||
|
AppSATRegimenes,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ api.add_route('/warehouseproduct', AppWareHouseProduct(db))
|
||||||
api.add_route('/ticketsdetails', AppTicketsDetails(db))
|
api.add_route('/ticketsdetails', AppTicketsDetails(db))
|
||||||
api.add_route('/users', AppUsers(db))
|
api.add_route('/users', AppUsers(db))
|
||||||
api.add_route('/satunidadespeso', AppSATUnidadesPeso(db))
|
api.add_route('/satunidadespeso', AppSATUnidadesPeso(db))
|
||||||
|
api.add_route('/satregimenes', AppSATRegimenes(db))
|
||||||
|
|
||||||
|
|
||||||
session_options = {
|
session_options = {
|
||||||
|
|
|
@ -526,6 +526,9 @@ class StorageEngine(object):
|
||||||
def sat_unidades_peso_post(self, args, user):
|
def sat_unidades_peso_post(self, args, user):
|
||||||
return main.SATUnidadesPeso.post(args, user)
|
return main.SATUnidadesPeso.post(args, user)
|
||||||
|
|
||||||
|
def sat_regimenes_get(self, filters, user):
|
||||||
|
return main.SATRegimenes.get_data(filters, user)
|
||||||
|
|
||||||
# Companies only in MV
|
# Companies only in MV
|
||||||
def _get_empresas(self, values):
|
def _get_empresas(self, values):
|
||||||
return main.companies_get()
|
return main.companies_get()
|
||||||
|
|
|
@ -939,6 +939,26 @@ class SATRegimenes(BaseModel):
|
||||||
)
|
)
|
||||||
return tuple(rows)
|
return tuple(rows)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_actives(cls, filters, user):
|
||||||
|
where = ((SATRegimenes.activo==True) & (SATRegimenes.fisica==True))
|
||||||
|
if (filters['morales']=='true'):
|
||||||
|
where = ((SATRegimenes.activo==True) & (SATRegimenes.moral==True))
|
||||||
|
|
||||||
|
rows = (SATRegimenes
|
||||||
|
.select(
|
||||||
|
SATRegimenes.id,
|
||||||
|
SATRegimenes.name.alias('value'))
|
||||||
|
.where(where)
|
||||||
|
.dicts()
|
||||||
|
)
|
||||||
|
return tuple(rows)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_data(cls, filters, user):
|
||||||
|
opt = filters['opt']
|
||||||
|
return getattr(cls, f'_get_{opt}')(filters, user)
|
||||||
|
|
||||||
|
|
||||||
class Emisor(BaseModel):
|
class Emisor(BaseModel):
|
||||||
rfc = TextField(unique=True)
|
rfc = TextField(unique=True)
|
||||||
|
@ -1027,8 +1047,6 @@ class Emisor(BaseModel):
|
||||||
'ong_autorizacion': obj.autorizacion,
|
'ong_autorizacion': obj.autorizacion,
|
||||||
'ong_fecha': obj.fecha_autorizacion,
|
'ong_fecha': obj.fecha_autorizacion,
|
||||||
'ong_fecha_dof': obj.fecha_dof,
|
'ong_fecha_dof': obj.fecha_dof,
|
||||||
# ~ 'correo_timbrado': obj.correo_timbrado,
|
|
||||||
# ~ 'token_timbrado': obj.token_timbrado,
|
|
||||||
'token_soporte': obj.token_soporte,
|
'token_soporte': obj.token_soporte,
|
||||||
'emisor_registro_patronal': obj.registro_patronal,
|
'emisor_registro_patronal': obj.registro_patronal,
|
||||||
'regimenes': [row.id for row in obj.regimenes]
|
'regimenes': [row.id for row in obj.regimenes]
|
||||||
|
@ -2787,6 +2805,13 @@ class Socios(BaseModel):
|
||||||
if fields['pais'] != 'México':
|
if fields['pais'] != 'México':
|
||||||
fields['pais'] = fields['pais'].upper()
|
fields['pais'] = fields['pais'].upper()
|
||||||
|
|
||||||
|
if 'regimenes' in fields:
|
||||||
|
fields['regimenes'] = utils.loads(fields['regimenes'])
|
||||||
|
if isinstance(fields['regimenes'], list):
|
||||||
|
fields['regimenes'] = tuple(map(int, fields['regimenes']))
|
||||||
|
else:
|
||||||
|
fields['regimenes'] = (fields['regimenes'],)
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -2802,18 +2827,9 @@ class Socios(BaseModel):
|
||||||
str(CondicionesPago.get(id=row['condicion_pago']))
|
str(CondicionesPago.get(id=row['condicion_pago']))
|
||||||
row['partner_balance'] = row.pop('saldo_cliente')
|
row['partner_balance'] = row.pop('saldo_cliente')
|
||||||
row['partner_email_fp'] = row.pop('correo_facturasp')
|
row['partner_email_fp'] = row.pop('correo_facturasp')
|
||||||
|
row['regimenes'] = SociosRegimenes.get_by_socio(row['id'])
|
||||||
return row
|
return row
|
||||||
|
|
||||||
#~ return {'data': data['rows'][:100], 'pos':0, 'total_count': 1300}
|
|
||||||
#~ start = 0
|
|
||||||
#~ count = 0
|
|
||||||
#~ end = 100
|
|
||||||
#~ if values:
|
|
||||||
#~ {'start': '100', 'count': '100', 'continue': 'true'}
|
|
||||||
#~ start = int(values['start'])
|
|
||||||
#~ cont = int(values['count'])
|
|
||||||
#~ end = start + count
|
|
||||||
|
|
||||||
total = Socios.select().count()
|
total = Socios.select().count()
|
||||||
|
|
||||||
rows = (Socios
|
rows = (Socios
|
||||||
|
@ -2829,19 +2845,23 @@ class Socios(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_client(cls, values):
|
def get_by_client(cls, values):
|
||||||
id = int(values.get('id', 0))
|
id = int(values.get('id', 0))
|
||||||
|
|
||||||
if id:
|
if id:
|
||||||
row = (Socios
|
row = (Socios
|
||||||
.select(
|
.select(
|
||||||
Socios.id, Socios.nombre, Socios.rfc,
|
Socios.id, Socios.nombre, Socios.rfc,
|
||||||
SATFormaPago.key.alias('forma_pago'),
|
SATFormaPago.key.alias('forma_pago'),
|
||||||
SATUsoCfdi.key.alias('uso_cfdi'))
|
SATUsoCfdi.key.alias('uso_cfdi'),
|
||||||
|
Socios.codigo_postal)
|
||||||
.join(SATFormaPago, JOIN.LEFT_OUTER).switch(Socios)
|
.join(SATFormaPago, JOIN.LEFT_OUTER).switch(Socios)
|
||||||
.join(SATUsoCfdi, JOIN.LEFT_OUTER).switch(Socios)
|
.join(SATUsoCfdi, JOIN.LEFT_OUTER).switch(Socios)
|
||||||
.where((Socios.id==id) & (Socios.es_cliente==True))
|
.where((Socios.id==id) & (Socios.es_cliente==True))
|
||||||
.dicts()
|
.dicts()
|
||||||
)
|
)
|
||||||
if len(row):
|
if len(row):
|
||||||
return {'ok': True, 'row': row[0]}
|
client = row[0]
|
||||||
|
client['regimenes'] = SociosRegimenes.get_by_key(client['id'])
|
||||||
|
return {'ok': True, 'row': client}
|
||||||
return {'ok': False}
|
return {'ok': False}
|
||||||
|
|
||||||
name = values.get('name', '')
|
name = values.get('name', '')
|
||||||
|
@ -2849,7 +2869,8 @@ class Socios(BaseModel):
|
||||||
rows = (Socios
|
rows = (Socios
|
||||||
.select(Socios.id, Socios.nombre, Socios.rfc,
|
.select(Socios.id, Socios.nombre, Socios.rfc,
|
||||||
SATFormaPago.key.alias('forma_pago'),
|
SATFormaPago.key.alias('forma_pago'),
|
||||||
SATUsoCfdi.key.alias('uso_cfdi'))
|
SATUsoCfdi.key.alias('uso_cfdi'),
|
||||||
|
Socios.codigo_postal)
|
||||||
.join(SATFormaPago, JOIN.LEFT_OUTER).switch(Socios)
|
.join(SATFormaPago, JOIN.LEFT_OUTER).switch(Socios)
|
||||||
.join(SATUsoCfdi, JOIN.LEFT_OUTER).switch(Socios)
|
.join(SATUsoCfdi, JOIN.LEFT_OUTER).switch(Socios)
|
||||||
.where((Socios.es_cliente==True & Socios.es_activo==True) &
|
.where((Socios.es_cliente==True & Socios.es_activo==True) &
|
||||||
|
@ -2863,6 +2884,7 @@ class Socios(BaseModel):
|
||||||
def add(cls, values):
|
def add(cls, values):
|
||||||
accounts = util.loads(values.pop('accounts', '[]'))
|
accounts = util.loads(values.pop('accounts', '[]'))
|
||||||
fields = cls._clean(cls, values)
|
fields = cls._clean(cls, values)
|
||||||
|
regimenes = fields.pop('regimenes', ())
|
||||||
|
|
||||||
w = ((Socios.rfc==fields['rfc']) & (Socios.slug==fields['slug']))
|
w = ((Socios.rfc==fields['rfc']) & (Socios.slug==fields['slug']))
|
||||||
if Socios.select().where(w).exists():
|
if Socios.select().where(w).exists():
|
||||||
|
@ -2892,6 +2914,16 @@ class Socios(BaseModel):
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
for regimen in regimenes:
|
||||||
|
try:
|
||||||
|
fields = {
|
||||||
|
'socio': obj,
|
||||||
|
'regimen': regimen,
|
||||||
|
}
|
||||||
|
SociosRegimenes.create(**fields)
|
||||||
|
except IntegrityError:
|
||||||
|
pass
|
||||||
|
|
||||||
row = {
|
row = {
|
||||||
'id': obj.id,
|
'id': obj.id,
|
||||||
'rfc': obj.rfc,
|
'rfc': obj.rfc,
|
||||||
|
@ -2905,6 +2937,8 @@ class Socios(BaseModel):
|
||||||
def actualizar(cls, values, id):
|
def actualizar(cls, values, id):
|
||||||
fields = cls._clean(cls, values)
|
fields = cls._clean(cls, values)
|
||||||
fields.pop('accounts', '')
|
fields.pop('accounts', '')
|
||||||
|
regimenes = fields.pop('regimenes', ())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
q = Socios.update(**fields).where(Socios.id==id)
|
q = Socios.update(**fields).where(Socios.id==id)
|
||||||
q.execute()
|
q.execute()
|
||||||
|
@ -2913,6 +2947,19 @@ class Socios(BaseModel):
|
||||||
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
|
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
obj = Socios.get(Socios.id==id)
|
||||||
|
q = SociosRegimenes.delete().where(SociosRegimenes.socio==id)
|
||||||
|
q.execute()
|
||||||
|
for regimen in regimenes:
|
||||||
|
try:
|
||||||
|
fields = {
|
||||||
|
'socio': obj,
|
||||||
|
'regimen': regimen,
|
||||||
|
}
|
||||||
|
SociosRegimenes.create(**fields)
|
||||||
|
except IntegrityError:
|
||||||
|
pass
|
||||||
|
|
||||||
obj = Socios.get(Socios.id==id)
|
obj = Socios.get(Socios.id==id)
|
||||||
row = {
|
row = {
|
||||||
'id': id,
|
'id': id,
|
||||||
|
@ -2934,6 +2981,8 @@ class Socios(BaseModel):
|
||||||
|
|
||||||
q = SociosCuentasBanco.delete().where(SociosCuentasBanco.socio==id)
|
q = SociosCuentasBanco.delete().where(SociosCuentasBanco.socio==id)
|
||||||
q.execute()
|
q.execute()
|
||||||
|
q = SociosRegimenes.delete().where(SociosRegimenes.socio==id)
|
||||||
|
q.execute()
|
||||||
q = Socios.delete().where(Socios.id==id)
|
q = Socios.delete().where(Socios.id==id)
|
||||||
return bool(q.execute())
|
return bool(q.execute())
|
||||||
|
|
||||||
|
@ -3040,6 +3089,41 @@ class SociosCuentasBanco(BaseModel):
|
||||||
return account.socio == invoice.cliente
|
return account.socio == invoice.cliente
|
||||||
|
|
||||||
|
|
||||||
|
class SociosRegimenes(BaseModel):
|
||||||
|
socio = ForeignKeyField(Socios)
|
||||||
|
regimen = ForeignKeyField(SATRegimenes)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
indexes = (
|
||||||
|
(('socio', 'regimen'), True),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_key(self, socio):
|
||||||
|
fields = (SATRegimenes.key.alias('id'), SATRegimenes.name.alias('value'))
|
||||||
|
where = (SociosRegimenes.socio == socio)
|
||||||
|
regimenes = (SociosRegimenes
|
||||||
|
.select(*fields)
|
||||||
|
.where(where)
|
||||||
|
.join(SATRegimenes).switch(SociosRegimenes)
|
||||||
|
.dicts()
|
||||||
|
)
|
||||||
|
return tuple(regimenes)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_socio(self, socio):
|
||||||
|
fields = (SATRegimenes.id,)
|
||||||
|
where = (SociosRegimenes.socio == socio)
|
||||||
|
regimenes = (SociosRegimenes
|
||||||
|
.select(*fields)
|
||||||
|
.where(where)
|
||||||
|
.join(SATRegimenes).switch(SociosRegimenes)
|
||||||
|
.tuples()
|
||||||
|
)
|
||||||
|
regimenes = [r[0] for r in regimenes]
|
||||||
|
return regimenes
|
||||||
|
|
||||||
|
|
||||||
class Contactos(BaseModel):
|
class Contactos(BaseModel):
|
||||||
socio = ForeignKeyField(Socios)
|
socio = ForeignKeyField(Socios)
|
||||||
titulo = ForeignKeyField(TipoTitulo)
|
titulo = ForeignKeyField(TipoTitulo)
|
||||||
|
@ -3806,6 +3890,7 @@ class Productos(BaseModel):
|
||||||
cantidad_empaque = DecimalField(default=0.0, max_digits=14, decimal_places=4,
|
cantidad_empaque = DecimalField(default=0.0, max_digits=14, decimal_places=4,
|
||||||
auto_round=True)
|
auto_round=True)
|
||||||
is_discontinued = BooleanField(default=False)
|
is_discontinued = BooleanField(default=False)
|
||||||
|
objeto_impuesto = TextField(default='02')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
order_by = ('descripcion',)
|
order_by = ('descripcion',)
|
||||||
|
@ -4389,6 +4474,8 @@ class Facturas(BaseModel):
|
||||||
egreso_anticipo = BooleanField(default=False)
|
egreso_anticipo = BooleanField(default=False)
|
||||||
tipo_relacion = TextField(default='')
|
tipo_relacion = TextField(default='')
|
||||||
error = TextField(default='')
|
error = TextField(default='')
|
||||||
|
exportacion = TextField(default='01')
|
||||||
|
receptor_regimen = TextField(default='')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
order_by = ('fecha',)
|
order_by = ('fecha',)
|
||||||
|
@ -5590,6 +5677,8 @@ class Facturas(BaseModel):
|
||||||
'Rfc': invoice.cliente.rfc,
|
'Rfc': invoice.cliente.rfc,
|
||||||
'Nombre': invoice.cliente.nombre,
|
'Nombre': invoice.cliente.nombre,
|
||||||
'UsoCFDI': invoice.uso_cfdi,
|
'UsoCFDI': invoice.uso_cfdi,
|
||||||
|
'DomicilioFiscalReceptor': invoice.cliente.codigo_postal,
|
||||||
|
'RegimenFiscalReceptor': invoice.receptor_regimen
|
||||||
}
|
}
|
||||||
if invoice.cliente.tipo_persona == 4:
|
if invoice.cliente.tipo_persona == 4:
|
||||||
if invoice.cliente.pais:
|
if invoice.cliente.pais:
|
||||||
|
@ -5701,6 +5790,16 @@ class Facturas(BaseModel):
|
||||||
taxes['retenciones'] = retenciones
|
taxes['retenciones'] = retenciones
|
||||||
|
|
||||||
concepto['impuestos'] = taxes
|
concepto['impuestos'] = taxes
|
||||||
|
|
||||||
|
# cfdi4
|
||||||
|
if row.producto.objeto_impuesto:
|
||||||
|
concepto['ObjetoImp'] = row.producto.objeto_impuesto
|
||||||
|
else:
|
||||||
|
if taxes:
|
||||||
|
concepto['ObjetoImp'] = '02'
|
||||||
|
else:
|
||||||
|
concepto['ObjetoImp'] = '01'
|
||||||
|
|
||||||
conceptos.append(concepto)
|
conceptos.append(concepto)
|
||||||
|
|
||||||
impuestos = {}
|
impuestos = {}
|
||||||
|
@ -5749,11 +5848,14 @@ class Facturas(BaseModel):
|
||||||
|
|
||||||
if tax_decimals:
|
if tax_decimals:
|
||||||
xml_importe = FORMAT_TAX.format(tax.importe)
|
xml_importe = FORMAT_TAX.format(tax.importe)
|
||||||
|
xml_tax_base = FORMAT_TAX.format(tax.base)
|
||||||
else:
|
else:
|
||||||
xml_importe = FORMAT.format(tax.importe)
|
xml_importe = FORMAT.format(tax.importe)
|
||||||
|
xml_tax_base = FORMAT.format(tax.base)
|
||||||
|
|
||||||
if tax.impuesto.tipo == 'T':
|
if tax.impuesto.tipo == 'T':
|
||||||
traslado = {
|
traslado = {
|
||||||
|
"Base": xml_tax_base,
|
||||||
"Impuesto": tax.impuesto.key,
|
"Impuesto": tax.impuesto.key,
|
||||||
"TipoFactor": tipo_factor,
|
"TipoFactor": tipo_factor,
|
||||||
"TasaOCuota": str(tax.impuesto.tasa),
|
"TasaOCuota": str(tax.impuesto.tasa),
|
||||||
|
@ -10546,6 +10648,7 @@ def _crear_tablas(rfc):
|
||||||
PartnerInvoices,
|
PartnerInvoices,
|
||||||
WareHouseProduct,
|
WareHouseProduct,
|
||||||
SATUnidadesPeso,
|
SATUnidadesPeso,
|
||||||
|
SociosRegimenes,
|
||||||
]
|
]
|
||||||
log.info('Creando tablas...')
|
log.info('Creando tablas...')
|
||||||
database_proxy.create_tables(tablas, True)
|
database_proxy.create_tables(tablas, True)
|
||||||
|
@ -10604,6 +10707,7 @@ def _migrate_tables(rfc=''):
|
||||||
PartnerInvoices,
|
PartnerInvoices,
|
||||||
WareHouseProduct,
|
WareHouseProduct,
|
||||||
SATUnidadesPeso,
|
SATUnidadesPeso,
|
||||||
|
SociosRegimenes,
|
||||||
]
|
]
|
||||||
log.info('Creando tablas nuevas...')
|
log.info('Creando tablas nuevas...')
|
||||||
database_proxy.create_tables(tablas, True)
|
database_proxy.create_tables(tablas, True)
|
||||||
|
@ -10729,6 +10833,10 @@ def _migrate_tables(rfc=''):
|
||||||
is_discontinued = BooleanField(default=False)
|
is_discontinued = BooleanField(default=False)
|
||||||
migrations.append(migrator.add_column(
|
migrations.append(migrator.add_column(
|
||||||
table, 'is_discontinued', is_discontinued))
|
table, 'is_discontinued', is_discontinued))
|
||||||
|
if not 'objeto_impuesto' in columns:
|
||||||
|
objeto_impuesto = TextField(default='02')
|
||||||
|
migrations.append(migrator.add_column(table, 'objeto_impuesto', objeto_impuesto))
|
||||||
|
|
||||||
if 'almacen_id' in columns:
|
if 'almacen_id' in columns:
|
||||||
migrations.append(migrator.drop_column(table, 'almacen_id'))
|
migrations.append(migrator.drop_column(table, 'almacen_id'))
|
||||||
|
|
||||||
|
@ -10745,6 +10853,12 @@ def _migrate_tables(rfc=''):
|
||||||
if not 'divisas' in columns:
|
if not 'divisas' in columns:
|
||||||
divisas = TextField(default='')
|
divisas = TextField(default='')
|
||||||
migrations.append(migrator.add_column(table, 'divisas', divisas))
|
migrations.append(migrator.add_column(table, 'divisas', divisas))
|
||||||
|
if not 'exportacion' in columns:
|
||||||
|
new_field = TextField(default='01')
|
||||||
|
migrations.append(migrator.add_column(table, 'exportacion', new_field))
|
||||||
|
if not 'receptor_regimen' in columns:
|
||||||
|
receptor_regimen = TextField(default='')
|
||||||
|
migrations.append(migrator.add_column(table, 'receptor_regimen', receptor_regimen))
|
||||||
|
|
||||||
table = 'almacenes'
|
table = 'almacenes'
|
||||||
columns = [c.name for c in database_proxy.get_columns(table)]
|
columns = [c.name for c in database_proxy.get_columns(table)]
|
||||||
|
|
|
@ -42,7 +42,8 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
DEBUG = DEBUG
|
DEBUG = DEBUG
|
||||||
VERSION = '1.47.0'
|
VERSION = '2.0.0'
|
||||||
|
|
||||||
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
|
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
|
||||||
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
|
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
|
||||||
|
|
||||||
|
@ -133,6 +134,7 @@ PRE = {
|
||||||
'3.0': '{http://www.sat.gob.mx/cfd/3}',
|
'3.0': '{http://www.sat.gob.mx/cfd/3}',
|
||||||
'3.2': '{http://www.sat.gob.mx/cfd/3}',
|
'3.2': '{http://www.sat.gob.mx/cfd/3}',
|
||||||
'3.3': '{http://www.sat.gob.mx/cfd/3}',
|
'3.3': '{http://www.sat.gob.mx/cfd/3}',
|
||||||
|
'4.0': '{http://www.sat.gob.mx/cfd/4}',
|
||||||
'TIMBRE': '{http://www.sat.gob.mx/TimbreFiscalDigital}',
|
'TIMBRE': '{http://www.sat.gob.mx/TimbreFiscalDigital}',
|
||||||
'DONATARIA': '{http://www.sat.gob.mx/donat}',
|
'DONATARIA': '{http://www.sat.gob.mx/donat}',
|
||||||
'INE': '{http://www.sat.gob.mx/ine}',
|
'INE': '{http://www.sat.gob.mx/ine}',
|
||||||
|
@ -193,6 +195,7 @@ CURRENCY_MN = 'MXN'
|
||||||
|
|
||||||
# ~ v2
|
# ~ v2
|
||||||
CANCEL_VERSION = ('3.3', '4.0')
|
CANCEL_VERSION = ('3.3', '4.0')
|
||||||
|
CFDI_VERSIONS = CANCEL_VERSION
|
||||||
|
|
||||||
IS_MV = MV
|
IS_MV = MV
|
||||||
DB_COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
|
DB_COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
|
||||||
|
|
|
@ -685,6 +685,7 @@ function guardar_y_timbrar(values){
|
||||||
data['metodo_pago'] = $$('lst_metodo_pago').getValue()
|
data['metodo_pago'] = $$('lst_metodo_pago').getValue()
|
||||||
data['uso_cfdi'] = $$('lst_uso_cfdi').getValue()
|
data['uso_cfdi'] = $$('lst_uso_cfdi').getValue()
|
||||||
data['regimen_fiscal'] = $$('lst_regimen_fiscal').getValue()
|
data['regimen_fiscal'] = $$('lst_regimen_fiscal').getValue()
|
||||||
|
data['receptor_regimen'] = $$('lst_invoice_client_regimen').getValue()
|
||||||
data['relacionados'] = ids
|
data['relacionados'] = ids
|
||||||
data['tipo_relacion'] = tipo_relacion
|
data['tipo_relacion'] = tipo_relacion
|
||||||
data['anticipo'] = anticipo
|
data['anticipo'] = anticipo
|
||||||
|
@ -904,6 +905,12 @@ function search_client_by_id(id){
|
||||||
|
|
||||||
|
|
||||||
function set_client(row){
|
function set_client(row){
|
||||||
|
if(!row.codigo_postal){
|
||||||
|
msg = 'El cliente no tiene capturado su Código Postal, es obligatorio.'
|
||||||
|
msg_error(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var form = $$('form_invoice')
|
var form = $$('form_invoice')
|
||||||
var html = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
|
var html = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
|
||||||
form.setValues({
|
form.setValues({
|
||||||
|
@ -913,6 +920,12 @@ function set_client(row){
|
||||||
html += row.nombre + ' (' + row.rfc + ')</span>'
|
html += row.nombre + ' (' + row.rfc + ')</span>'
|
||||||
$$('lbl_client').setValue(html)
|
$$('lbl_client').setValue(html)
|
||||||
$$('cmd_cfdi_relacionados').enable()
|
$$('cmd_cfdi_relacionados').enable()
|
||||||
|
|
||||||
|
var lst = $$('lst_invoice_client_regimen')
|
||||||
|
lst.getList().clearAll()
|
||||||
|
lst.getList().parse(row.regimenes)
|
||||||
|
lst.setValue(lst.getPopup().getList().getFirstId())
|
||||||
|
|
||||||
form.focus('search_product_id')
|
form.focus('search_product_id')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ function cmd_new_partner_click(id, e, node){
|
||||||
$$('partner_balance').define('readonly', !cfg_partners['chk_config_change_balance_partner'])
|
$$('partner_balance').define('readonly', !cfg_partners['chk_config_change_balance_partner'])
|
||||||
get_partner_banks()
|
get_partner_banks()
|
||||||
get_partner_accounts_bank(0)
|
get_partner_accounts_bank(0)
|
||||||
|
get_sat_regimenes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,6 +109,8 @@ function cmd_edit_partner_click(){
|
||||||
var msg = ''
|
var msg = ''
|
||||||
var row = $$('grid_partners').getSelectedItem()
|
var row = $$('grid_partners').getSelectedItem()
|
||||||
|
|
||||||
|
get_sat_regimenes()
|
||||||
|
|
||||||
$$('form_partner_account_bank').clearValidation()
|
$$('form_partner_account_bank').clearValidation()
|
||||||
|
|
||||||
if (row == undefined){
|
if (row == undefined){
|
||||||
|
@ -145,6 +148,7 @@ function cmd_edit_partner_click(){
|
||||||
$$('cuenta_proveedor').enable()
|
$$('cuenta_proveedor').enable()
|
||||||
}
|
}
|
||||||
get_partner_accounts_bank(row['id'])
|
get_partner_accounts_bank(row['id'])
|
||||||
|
$$('lst_receptor_regimenes_fiscales').select(values.regimenes)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -239,7 +243,17 @@ function cmd_save_partner_click(id, e, node){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ids_regimenes = $$('lst_receptor_regimenes_fiscales').getSelectedId()
|
||||||
|
if(values.tipo_persona < 3){
|
||||||
|
if(!ids_regimenes){
|
||||||
|
msg = 'Selecciona al menos un Regimen Fiscal'
|
||||||
|
msg_error(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
values['accounts'] = $$('grid_partner_account_bank').data.getRange()
|
values['accounts'] = $$('grid_partner_account_bank').data.getRange()
|
||||||
|
values['regimenes'] = ids_regimenes
|
||||||
|
|
||||||
webix.ajax().post('/partners', values, {
|
webix.ajax().post('/partners', values, {
|
||||||
error:function(text, data, XmlHttpRequest){
|
error:function(text, data, XmlHttpRequest){
|
||||||
|
@ -343,9 +357,16 @@ function opt_tipo_change(new_value, old_value){
|
||||||
$$('id_fiscal').define('value', '')
|
$$('id_fiscal').define('value', '')
|
||||||
show('id_fiscal', new_value == 4)
|
show('id_fiscal', new_value == 4)
|
||||||
|
|
||||||
|
$$('lst_receptor_regimenes_fiscales').clearAll()
|
||||||
|
|
||||||
if (new_value == 1 || new_value == 2){
|
if (new_value == 1 || new_value == 2){
|
||||||
$$("rfc").define("value", "")
|
$$("rfc").define("value", "")
|
||||||
$$("rfc").define("readonly", false)
|
$$("rfc").define("readonly", false)
|
||||||
|
moral = false
|
||||||
|
if(new_value == 2){
|
||||||
|
moral = true
|
||||||
|
}
|
||||||
|
get_sat_regimenes(moral)
|
||||||
} else if (new_value == 3) {
|
} else if (new_value == 3) {
|
||||||
$$("rfc").define("value", RFC_PUBLICO)
|
$$("rfc").define("value", RFC_PUBLICO)
|
||||||
$$("nombre").define("value", PUBLICO)
|
$$("nombre").define("value", PUBLICO)
|
||||||
|
@ -376,6 +397,8 @@ function opt_tipo_change(new_value, old_value){
|
||||||
}
|
}
|
||||||
$$('lst_uso_cfdi_socio').getList().parse(query)
|
$$('lst_uso_cfdi_socio').getList().parse(query)
|
||||||
$$('lst_uso_cfdi_socio').refresh()
|
$$('lst_uso_cfdi_socio').refresh()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -619,3 +642,21 @@ function partner_delete_account_bank(row){
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_sat_regimenes(morales=false){
|
||||||
|
var data = {opt: 'actives', morales: morales}
|
||||||
|
webix.ajax().get('/satregimenes', data, {
|
||||||
|
error: function(text, data, xhr) {
|
||||||
|
msg = 'Error al consultar'
|
||||||
|
msg_error(msg)
|
||||||
|
},
|
||||||
|
success: function(text, data, xhr) {
|
||||||
|
var values = data.json()
|
||||||
|
$$('lst_receptor_regimenes_fiscales').clearAll()
|
||||||
|
$$('lst_receptor_regimenes_fiscales').parse(values)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -418,6 +418,7 @@ var suggest_partners = {
|
||||||
{id: 'rfc', adjust: 'data'},
|
{id: 'rfc', adjust: 'data'},
|
||||||
{id: 'forma_pago', hidden: true},
|
{id: 'forma_pago', hidden: true},
|
||||||
{id: 'uso_cfdi', hidden: true},
|
{id: 'uso_cfdi', hidden: true},
|
||||||
|
{id: 'codigo_postal', hidden: true},
|
||||||
],
|
],
|
||||||
dataFeed:function(text){
|
dataFeed:function(text){
|
||||||
if (text.length > 2){
|
if (text.length > 2){
|
||||||
|
@ -596,6 +597,10 @@ var controls_generate = [
|
||||||
autowidth:true},
|
autowidth:true},
|
||||||
{view: 'label', id: 'lbl_client', name: 'lbl_client',
|
{view: 'label', id: 'lbl_client', name: 'lbl_client',
|
||||||
label: 'Ninguno'},
|
label: 'Ninguno'},
|
||||||
|
]},
|
||||||
|
{cols: [{
|
||||||
|
view: 'richselect', id: 'lst_invoice_client_regimen',
|
||||||
|
label: 'Regimen Fiscal: ', labelWidth: 150, options: []}
|
||||||
]}
|
]}
|
||||||
]}},
|
]}},
|
||||||
{view: 'fieldset', label: 'Buscar Producto', body: {rows: [
|
{view: 'fieldset', label: 'Buscar Producto', body: {rows: [
|
||||||
|
|
|
@ -96,7 +96,7 @@ var controls_fiscales = [
|
||||||
{cols: [{view: 'text', id: 'no_interior', name: 'no_interior', width: 300,
|
{cols: [{view: 'text', id: 'no_interior', name: 'no_interior', width: 300,
|
||||||
label: 'No Interior: '},{}]},
|
label: 'No Interior: '},{}]},
|
||||||
{cols: [{view: 'search', id: 'codigo_postal', name: 'codigo_postal',
|
{cols: [{view: 'search', id: 'codigo_postal', name: 'codigo_postal',
|
||||||
width: 300, label: 'C.P.: ', attributes: {maxlength: 5}},{}]},
|
width: 300, label: 'C.P.: ', attributes: {maxlength: 5}, required: true},{}]},
|
||||||
{view: 'text', id: 'colonia', name: 'colonia', label: 'Colonia: '},
|
{view: 'text', id: 'colonia', name: 'colonia', label: 'Colonia: '},
|
||||||
{view: 'text', id: 'municipio', name: 'municipio', label: 'Municipio: '},
|
{view: 'text', id: 'municipio', name: 'municipio', label: 'Municipio: '},
|
||||||
{view: 'text', id: 'estado', name: 'estado', label: 'Estado: '},
|
{view: 'text', id: 'estado', name: 'estado', label: 'Estado: '},
|
||||||
|
@ -122,6 +122,12 @@ var controls_fiscales = [
|
||||||
{view: 'richselect', id: 'lst_uso_cfdi_socio', name: 'uso_cfdi_socio',
|
{view: 'richselect', id: 'lst_uso_cfdi_socio', name: 'uso_cfdi_socio',
|
||||||
label: 'Uso del CFDI', options: []},
|
label: 'Uso del CFDI', options: []},
|
||||||
{},
|
{},
|
||||||
|
]},
|
||||||
|
{template: 'Regimenes Fiscales', type: 'section'},
|
||||||
|
{cols: [
|
||||||
|
{view: 'list', id: 'lst_receptor_regimenes_fiscales', data: [],
|
||||||
|
select: 'multiselect', width: 600, height: 125, required: true},
|
||||||
|
{},
|
||||||
]}
|
]}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -159,7 +165,7 @@ var controls_others = [
|
||||||
label: 'Cuenta Proveedor: ', disabled: true}, {}]
|
label: 'Cuenta Proveedor: ', disabled: true}, {}]
|
||||||
},
|
},
|
||||||
{view: 'checkbox', name: 'es_ong', label: 'Es ONG: ', value: false},
|
{view: 'checkbox', name: 'es_ong', label: 'Es ONG: ', value: false},
|
||||||
{view: 'text', name: 'tags', label: 'Etiquetas',
|
{view: 'text', name: 'tags', label: 'Etiquetas', disabled: true,
|
||||||
tooltip: 'Utiles para filtrados rápidos. Separa por comas.'},
|
tooltip: 'Utiles para filtrados rápidos. Separa por comas.'},
|
||||||
{view: 'textarea' , height: 200, name: 'notas', label: 'Notas'},
|
{view: 'textarea' , height: 200, name: 'notas', label: 'Notas'},
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue