Stamp fiscal leyends

This commit is contained in:
Mauricio Baeza 2020-03-01 23:18:26 -06:00
parent cca836bb6a
commit 2877a68b92
9 changed files with 303 additions and 20 deletions

View File

@ -90,6 +90,12 @@ SAT = {
'xmlns': 'http://www.sat.gob.mx/divisas',
'schema': ' http://www.sat.gob.mx/divisas http://www.sat.gob.mx/sitio_internet/cfd/divisas/divisas.xsd',
},
'leyendas': {
'version': '1.0',
'prefix': 'leyendasFisc',
'xmlns': 'http://www.sat.gob.mx/leyendasFiscales',
'schema': ' http://www.sat.gob.mx/leyendasFiscales http://www.sat.gob.mx/sitio_internet/cfd/leyendasFiscales/leyendasFisc.xsd',
},
}
@ -107,6 +113,7 @@ class CFDI(object):
self._edu = False
self._pagos = False
self._is_nomina = False
self._leyendas = False
self._divisas = ''
self.error = ''
@ -129,8 +136,6 @@ class CFDI(object):
if 'nomina' in datos:
self._nomina(datos['nomina'])
#~ if 'complementos' in datos:
#~ self._complementos(datos['complementos'])
return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8'))
@ -158,6 +163,7 @@ class CFDI(object):
if 'ine' in datos['complementos']:
self._ine = True
self._pagos = bool(datos['complementos'].get('pagos', False))
self._leyendas = bool(datos['complementos'].get('leyendas', False))
self._divisas = datos['comprobante'].pop('divisas', '')
@ -217,9 +223,15 @@ class CFDI(object):
attributes[name] = SAT['pagos']['xmlns']
schema_pagos = SAT['pagos']['schema']
schema_leyendas = ''
if self._leyendas:
name = 'xmlns:{}'.format(SAT['leyendas']['prefix'])
attributes[name] = SAT['leyendas']['xmlns']
schema_leyendas = SAT['leyendas']['schema']
attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \
schema_locales + schema_donativo + schema_ine + schema_edu + \
schema_divisas + schema_nomina + schema_pagos
schema_divisas + schema_nomina + schema_pagos + schema_leyendas
attributes.update(datos)
if not 'Version' in attributes:
@ -464,6 +476,14 @@ class CFDI(object):
for row in relacionados:
ET.SubElement(node_pago, '{}:DoctoRelacionado'.format(pre), row)
if 'leyendas' in datos:
pre = SAT['leyendas']['prefix']
attributes = {'version': SAT['leyendas']['version']}
node_leyend = ET.SubElement(
self._complemento, '{}:LeyendasFiscales'.format(pre), attributes)
for leyend in datos['leyendas']:
ET.SubElement(node_leyend, '{}:Leyenda'.format(pre), leyend)
if 'ce' in datos:
pre = 'cce11'
datos = datos.pop('ce')

View File

@ -594,6 +594,29 @@ class AppSATFormaPago(object):
resp.status = falcon.HTTP_200
class AppSATLeyendaFiscales(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_leyendas_fiscales_get(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_leyendas_fiscales_post(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.sat_leyendas_fiscales_delete(values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppSociosCuentasBanco(object):
def __init__(self, db):

View File

@ -17,7 +17,7 @@ from controllers.main import (AppEmpresas,
AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
AppMovimientosBanco, AppTickets, AppStudents, AppEmployees, AppNomina,
AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco,
AppSATFormaPago
AppSATFormaPago, AppSATLeyendaFiscales
)
@ -61,6 +61,7 @@ api.add_route('/cfdipay', AppCfdiPay(db))
api.add_route('/satbancos', AppSATBancos(db))
api.add_route('/satformapago', AppSATFormaPago(db))
api.add_route('/socioscb', AppSociosCuentasBanco(db))
api.add_route('/leyendasfiscales', AppSATLeyendaFiscales(db))
session_options = {

View File

@ -462,6 +462,15 @@ class StorageEngine(object):
def nomina(self, values, user):
return main.CfdiNomina.post(values, user)
def sat_leyendas_fiscales_get(self, values):
return main.SATLeyendasFiscales.get_values(values)
def sat_leyendas_fiscales_post(self, values):
return main.SATLeyendasFiscales.post(values)
def sat_leyendas_fiscales_delete(self, values):
return main.SATLeyendasFiscales.remove(values)
# Companies only in MV
def _get_empresas(self, values):
return main.companies_get()

View File

@ -308,6 +308,7 @@ def config_timbrar():
'cfdi_add_same_product': Configuracion.get_bool('chk_config_add_same_product'),
'cfdi_tax_locales_truncate': Configuracion.get_bool('chk_config_tax_locales_truncate'),
'cfdi_folio_custom': Configuracion.get_bool('chk_folio_custom'),
'cfdi_leyendasfiscales': Configuracion.get_bool('chk_config_leyendas_fiscales'),
}
return conf
@ -2479,6 +2480,83 @@ class SATRiesgoPuesto(BaseModel):
return
class SATLeyendasFiscales(BaseModel):
texto_leyenda = TextField(index=True)
norma = TextField(default='')
disposicion_fiscal = TextField(default='')
active = BooleanField(default=True)
default = BooleanField(default=False)
class Meta:
order_by = ('texto_leyenda',)
indexes = (
(('texto_leyenda', 'norma', 'disposicion_fiscal'), True),
)
def _get_all(self, values):
rows = (SATLeyendasFiscales.select(
SATLeyendasFiscales.id,
SQL(" '-' AS delete"),
SATLeyendasFiscales.texto_leyenda,
SATLeyendasFiscales.norma,
SATLeyendasFiscales.disposicion_fiscal)
.dicts()
)
return tuple(rows)
def _get_active(self, values):
rows = (SATLeyendasFiscales.select(
SATLeyendasFiscales.id,
SATLeyendasFiscales.texto_leyenda,
SATLeyendasFiscales.norma,
SATLeyendasFiscales.disposicion_fiscal)
.where(SATLeyendasFiscales.active==True)
.dicts()
)
return tuple(rows)
@classmethod
def get_by_id(cls, ids):
rows = (SATLeyendasFiscales.select(
SATLeyendasFiscales.texto_leyenda.alias('textoLeyenda'),
SATLeyendasFiscales.norma,
SATLeyendasFiscales.disposicion_fiscal.alias('disposicionFiscal'))
.where(SATLeyendasFiscales.id.in_(ids))
.dicts()
)
for row in rows:
if not row['norma']:
del row['norma']
if not row['disposicionFiscal']:
del row['disposicionFiscal']
return tuple(rows)
@classmethod
def get_values(cls, values):
opt = values.pop('opt')
return getattr(cls, '_get_{}'.format(opt))(cls, values)
def _new(self, values):
try:
obj = SATLeyendasFiscales.create(**values)
values['id'] = obj.id
values['delete'] = '-'
result = {'ok': True, 'row': values}
except Exception as e:
result = {'ok': False, 'msg': 'La Leyenda Fiscal ya existe'}
return result
@classmethod
def post(cls, values):
opt = values.pop('opt')
return getattr(cls, '_{}'.format(opt))(cls, values)
@classmethod
def remove(cls, id):
q = SATLeyendasFiscales.delete().where(SATLeyendasFiscales.id==id)
return bool(q.execute())
class TipoCambio(BaseModel):
dia = DateField(default=util.now)
moneda = ForeignKeyField(SATMonedas)
@ -4642,6 +4720,8 @@ class Facturas(BaseModel):
divisas = ''
values['divisas'] = divisas
leyendas_fiscales = utils.loads(values.pop('leyendas_fiscales', '[]'))
emisor = Emisor.select()[0]
values['serie'] = cls._get_serie(cls, user, values['serie'])
if Configuracion.get_bool('chk_folio_custom') and folio_custom:
@ -4666,6 +4746,7 @@ class Facturas(BaseModel):
totals = cls._calculate_totals(cls, obj, productos, tipo_comprobante)
cls._guardar_relacionados(cls, obj, relacionados)
cls._guardar_ine(cls, obj, ine)
cls._save_leyendas_fiscales(cls, obj, leyendas_fiscales)
obj.subtotal = totals['subtotal']
obj.descuento = totals['descuento']
obj.total_trasladados = totals['total_trasladados']
@ -4696,6 +4777,18 @@ class Facturas(BaseModel):
data = {'ok': True, 'row': row, 'new': True, 'error': False, 'msg': msg}
return data
def _save_leyendas_fiscales(self, invoice, valores):
if not valores:
return
data = {
'factura': invoice,
'nombre': 'leyendas',
'valores': utils.dumps(valores),
}
FacturasComplementos.create(**data)
return
def _make_xml(self, invoice, auth):
tax_decimals = Configuracion.get_bool('chk_config_tax_decimals')
decimales_precios = Configuracion.get_bool('chk_config_decimales_precios')
@ -4710,11 +4803,15 @@ class Facturas(BaseModel):
comprobante = {}
relacionados = {}
donativo = {}
complementos = FacturasComplementos.get_(invoice)
complementos = FacturasComplementos.get_by_invoice(invoice)
comprobante['divisas'] = invoice.divisas
if invoice.divisas:
complementos['divisas'] = True
if 'leyendas' in complementos:
ids = complementos['leyendas']
complementos['leyendas'] = SATLeyendasFiscales.get_by_id(ids)
if invoice.donativo:
donativo['noAutorizacion'] = emisor.autorizacion
donativo['fechaAutorizacion'] = str(emisor.fecha_autorizacion)
@ -5855,6 +5952,14 @@ class FacturasComplementos(BaseModel):
)
return {r.nombre: utils.loads(r.valores) for r in query}
@classmethod
def get_by_invoice(cls, invoice):
query = (FacturasComplementos
.select()
.where(FacturasComplementos.factura==invoice)
)
return {r.nombre: utils.loads(r.valores) for r in query}
class PreFacturasRelacionadas(BaseModel):
factura = ForeignKeyField(PreFacturas, related_name='original')
@ -9082,7 +9187,7 @@ def _crear_tablas(rfc):
SATNivelesEducativos, SATEstados, SATRiesgoPuesto, SATPeriodicidadPago,
SATOrigenRecurso, SATTipoContrato, SATTipoDeduccion, SATTipoHoras,
SATTipoIncapacidad, SATTipoJornada, SATTipoNomina, SATTipoOtroPago,
SATTipoPercepcion, SATTipoRegimen,
SATTipoPercepcion, SATTipoRegimen, SATLeyendasFiscales,
Socios, SociosCuentasBanco, Contactos, ContactoCorreos,
ContactoDirecciones, Empleados, ContactoTelefonos, Departamentos,
Puestos,
@ -9152,7 +9257,7 @@ def _migrate_tables(rfc=''):
CfdiNominaOtroPago, CfdiNominaOtros, CfdiNominaPercepciones,
CfdiNominaRelacionados, CfdiNominaSeparacion, CfdiNominaSubcontratos,
CfdiNominaTotales, SATNivelesEducativos, Roles, Permisos,
SociosCuentasBanco
SociosCuentasBanco, SATLeyendasFiscales
]
log.info('Creando tablas nuevas...')
database_proxy.create_tables(tablas, True)
@ -10376,6 +10481,5 @@ def main(args):
if __name__ == '__main__':
args = _process_command_line_arguments()
main(args)
# ~ main2()

View File

@ -2605,8 +2605,53 @@ function cmd_admin_leyendas_fiscales_click(){
}
function get_leyendas_fiscales(){
webix.ajax().sync().get('/leyendasfiscales', {'opt': 'all'}, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
var grid = $$('grid_admin_leyendas_fiscales')
grid.clearAll()
grid.parse(values)
grid.refresh()
}
})
}
function add_admin_leyenda_fiscal_click(){
msg_ok('Add')
var form = $$('form_admin_leyendas_fiscales')
var grid = $$('grid_admin_leyendas_fiscales')
if (!form.validate()){
msg = 'Valores inválidos'
msg_error(msg)
return
}
var values = form.getValues()
values['opt'] = 'new'
var empty = {texto_leyenda: '', norma: '', disposicion_fiscal: ''}
webix.ajax().post('/leyendasfiscales', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
msg_ok('Leyenda guardada correctamente')
form.setValues(empty)
grid.add(values.row)
}else{
msg_error(values.msg)
}
}
})
}
@ -2630,3 +2675,18 @@ function grid_admin_leyendas_fiscales_click(id){
})
}
function delete_leyenda_fiscal(id){
var grid = $$('grid_admin_leyendas_fiscales')
webix.ajax().del('/leyendasfiscales', {id: id}, function(text, xml, xhr){
msg = 'Leyenda Fiscal eliminada correctamente'
if(xhr.status == 200){
grid.remove(id)
msg_ok(msg)
}else{
msg = 'No se pudo eliminar'
msg_error(msg)
}
})
}

View File

@ -205,6 +205,13 @@ function default_config(){
}else{
$$('tv_invoice').getTabbar().showOption('INE')
}
if(!values.cfdi_leyendasfiscales){
$$('tv_invoice').getTabbar().hideOption('Leyendas Fiscales')
}else{
get_leyendas_fiscales()
$$('tv_invoice').getTabbar().showOption('Leyendas Fiscales')
}
cfg_invoice['leyendasfiscales'] = values.cfdi_leyendasfiscales
cfg_invoice['edu'] = values.cfdi_edu
cfg_invoice['open_pdf'] = values.cfdi_open_pdf
cfg_invoice['tax_locales'] = values.cfdi_tax_locales
@ -642,6 +649,8 @@ function guardar_y_timbrar(values){
data['notas'] = values.notas
data['folio_custom'] = $$('txt_folio_custom').getValue()
data['divisas'] = $$('opt_divisas').getValue()
data['leyendas_fiscales'] = $$('grid_leyendas_fiscales').getSelectedId(true, true)
$$('grid_leyendas_fiscales').unselectAll()
var usar_ine = $$('chk_cfdi_usar_ine').getValue()
if(usar_ine){
@ -709,6 +718,17 @@ function cmd_timbrar_click(id, e, node){
msg += 'El Tipo de Comprobante es Traslado, el total será puesto a 0 (Cero), asegurate de que sea el tipo de comprobante correcto<BR><BR>'
}
if(cfg_invoice['leyendasfiscales']){
var rows = $$('grid_leyendas_fiscales').getSelectedId(true, true)
if(rows.length == 0){
msg += 'No se agregará ninguna leyenda fiscal<BR><BR>'
}else if(rows.length == 1){
msg += 'Se agregará una leyenda fiscal<BR><BR>'
}else{
msg += 'Se agregarán ' + rows.length + ' leyendas fiscales<BR><BR>'
}
}
msg += '¿Estás seguro de timbrar esta factura?<BR><BR>'
webix.confirm({
@ -2352,3 +2372,20 @@ function search_by_click(){
search_by(value)
}
}
function get_leyendas_fiscales(){
webix.ajax().get('/leyendasfiscales', {'opt': 'active'}, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
var grid = $$('grid_leyendas_fiscales')
grid.clearAll()
grid.parse(values)
grid.refresh()
}
})
}

View File

@ -1428,9 +1428,9 @@ var body_win_emisor_logo = [
var grid_cols_admin_leyendas_fiscales = [
{id: 'id', header: 'ID', hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: 'leyenda', header: 'Leyenda', fillspace: 1},
{id: 'texto_leyenda', header: 'Leyenda', fillspace: 1},
{id: 'norma', header: 'Norma', fillspace: 1},
{id: 'disposicion', header: 'Disposición Fiscal', fillspace: 1},
{id: 'disposicion_fiscal', header: 'Disposición Fiscal', fillspace: 1},
//~ {id: 'active', header: 'Activa', template: '{common.checkbox()}',
//~ editor: 'checkbox'},
]
@ -1439,7 +1439,7 @@ var grid_cols_admin_leyendas_fiscales = [
var grid_admin_leyendas_fiscales = {
view: 'datatable',
id: 'grid_admin_leyendas_fiscales',
select: 'cell',
select: 'row',
adjust: true,
autoheight: true,
headermenu: true,
@ -1456,15 +1456,15 @@ var grid_admin_leyendas_fiscales = {
var form_controls_admin_leyendas_fiscales = [
{cols: [
{view: 'text', id: 'txt_leyenda_fiscal', label: 'Leyenda', width: 200,
labelPosition: 'top', name: 'txt_leyenda_fiscal', required: true},
{view: 'text', id: 'txt_texto_leyenda', label: 'Leyenda', width: 200,
labelPosition: 'top', name: 'texto_leyenda', required: true},
{view: "text", id: 'txt_norma_fiscal', label: 'Norma', width: 200,
name: 'txt_leyenda_fiscal', labelPosition: 'top'},
name: 'norma', labelPosition: 'top'},
{view: "text", id: 'txt_disposicion_fiscal', label: 'Disposición',
name: 'txt_disposicion_fiscal', labelPosition: 'top', width: 200}
name: 'disposicion_fiscal', labelPosition: 'top', width: 200}
]},
{cols: [{},
{view: 'button', type: 'icon', icon: 'plus', label: 'Agregar', autowidth: true,
{view: 'button', type: 'form', icon: 'plus', label: 'Agregar', autowidth: true,
click: function(){add_admin_leyenda_fiscal_click()}
}, {}]},
grid_admin_leyendas_fiscales,

View File

@ -701,7 +701,7 @@ var opt_tipo_comite = [
]
var controles_ine = [
var controls_ine = [
{maxHeight: 15},
{cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_cfdi_usar_ine', labelWidth: 0,
@ -727,16 +727,45 @@ var controles_ine = [
]
var grid_cols_leyendas_fiscales = [
{id: 'id', header: 'ID', hidden: true},
{id: 'texto_leyenda', header: 'Leyenda', fillspace: 2},
{id: 'norma', header: 'Norma', fillspace: 1},
{id: 'disposicion_fiscal', header: 'Disposición Fiscal', fillspace: 1},
]
var grid_leyendas_fiscales = {
view: 'datatable',
id: 'grid_leyendas_fiscales',
select: 'row',
multiselect: true,
adjust: true,
autoheight: true,
headermenu: true,
columns: grid_cols_leyendas_fiscales,
}
var controls_leyendas_fiscales = [
{maxHeight: 15},
{cols: [{maxWidth: 15},
{view: 'label', id: 'lbl_title', label: 'Selecciona las Leyendas Fiscales a integrar en esta factura'},
{}]},
{maxHeight: 15},
grid_leyendas_fiscales,
]
var controls_invoices = [
{
view: 'tabview',
id: 'tv_invoice',
//~ tabbar: {options: ['Generar', 'PreFacturas', 'INE']},
animate: true,
cells: [
{id: 'Generar', rows: controls_generate},
{id: 'PreFacturas', rows: controls_prefactura},
{id: 'INE', rows: controles_ine},
{id: 'INE', rows: controls_ine},
{id: 'Leyendas Fiscales', rows: controls_leyendas_fiscales},
]
},
]