XML con CFDI relacionados

This commit is contained in:
Mauricio Baeza 2017-11-08 23:47:15 -06:00
parent 7bfa2552eb
commit d61e0e8d8e
7 changed files with 470 additions and 43 deletions

View File

@ -59,6 +59,7 @@ class CFDI(object):
return ''
self._comprobante(datos['comprobante'])
self._relacionados(datos['relacionados'])
self._emisor(datos['emisor'])
self._receptor(datos['receptor'])
self._conceptos(datos['conceptos'])
@ -107,10 +108,6 @@ class CFDI(object):
attributes['xsi:schemaLocation'] = self._sat_cfdi['schema']
attributes.update(datos)
#~ if DEBUG:
#~ attributes['Fecha'] = self._now()
#~ attributes['NoCertificado'] = CERT_NUM
if not 'Version' in attributes:
attributes['Version'] = self._sat_cfdi['version']
if not 'Fecha' in attributes:
@ -119,10 +116,20 @@ class CFDI(object):
self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes)
return
def _emisor(self, datos):
#~ if DEBUG:
#~ datos['Rfc'] = RFC_TEST
def _relacionados(self, datos):
if not datos['tipo'] or not datos['cfdis']:
return
node_name = '{}:CfdiRelacionados'.format(self._pre)
value = {'TipoRelacion': datos['tipo']}
node = ET.SubElement(self._cfdi, node_name, value)
for uuid in datos['cfdis']:
node_name = '{}:CfdiRelacionado'.format(self._pre)
value = {'UUID': uuid}
ET.SubElement(node, node_name, value)
return
def _emisor(self, datos):
node_name = '{}:Emisor'.format(self._pre)
emisor = ET.SubElement(self._cfdi, node_name, datos)
return

View File

@ -64,6 +64,9 @@ class StorageEngine(object):
def _get_formapago(self, values):
return main.SATFormaPago.get_activos(values)
def _get_tiporelacion(self, values):
return main.SATTipoRelacion.get_activos(values)
def _get_condicionespago(self, values):
return main.CondicionesPago.get_()

View File

@ -4,7 +4,7 @@ import sqlite3
import click
from peewee import *
from playhouse.fields import PasswordField, ManyToManyField
from playhouse.shortcuts import case, SQL
from playhouse.shortcuts import case, SQL, cast
if __name__ == '__main__':
@ -704,6 +704,19 @@ class SATTipoRelacion(BaseModel):
def __str__(self):
return 'Tipo de relación: ({}) {}'.format(self.key, self.name)
@classmethod
def get_activos(cls, values):
field = SATTipoRelacion.id
if values:
field = SATTipoRelacion.key.alias('id')
rows = (SATTipoRelacion
.select(field, SATTipoRelacion.name.alias('value'))
.where(SATTipoRelacion.activo==True)
.dicts()
)
return ({'id': '-', 'value': ''},) + tuple(rows)
class SATUsoCfdi(BaseModel):
key = TextField(index=True, unique=True)
@ -1336,8 +1349,72 @@ class Facturas(BaseModel):
msg = 'Factura enviada correctamente'
return {'ok': True, 'msg': msg}
def _get_filter_folios(self, values):
if not 'folio' in values:
return ''
folios = values['folio'].split('-')
if len(folios) == 1:
try:
folio1 = int(folios[0])
except ValueError:
return ''
folio2 = folio1
else:
try:
folio1 = int(folios[0])
folio2 = int(folios[1])
except ValueError:
return ''
return (Facturas.folio.between(folio1, folio2))
def _get_opt(self, values):
cfdis = util.loads(values['cfdis'])
if values['year'] == '-1':
fy = (Facturas.fecha.year > 0)
else:
fy = (Facturas.fecha.year == int(values['year']))
if values['month'] == '-1':
fm = (Facturas.fecha.month > 0)
else:
fm = (Facturas.fecha.month == int(values['month']))
if values['opt'] == 'relacionados':
folios = self._get_filter_folios(self, values)
uuid = values.get('uuid', '')
if uuid:
f_uuid = (cast(Facturas.uuid, 'text').contains(uuid))
cliente = (Facturas.cliente == int(values['id_cliente']))
if cfdis:
f_ids = (Facturas.id.not_in(cfdis))
else:
f_ids = (Facturas.id > 0)
if folios:
filters = (fy & fm & folios & cliente & f_ids)
elif uuid:
filters = (fy & fm & f_uuid & cliente & f_ids)
else:
filters = (fy & fm & cliente & f_ids)
rows = tuple(Facturas
.select(Facturas.id, Facturas.serie, Facturas.folio,
Facturas.uuid, Facturas.fecha, Facturas.tipo_comprobante,
Facturas.estatus, Facturas.total_mn)
.where(filters).dicts()
)
return {'ok': True, 'rows': rows}
@classmethod
def get_(cls, values):
opt = values.get('opt', '')
if opt:
return cls._get_opt(cls, values)
if 'start' in values:
filters = Facturas.fecha.between(
util.get_date(values['start']),
@ -1469,10 +1546,19 @@ class Facturas(BaseModel):
}
return data
def _guardar_relacionados(self, invoice, relacionados):
for cfdi in relacionados:
data = {
'factura': invoice,
'factura_origen': cfdi,
}
FacturasRelacionadas.create(**data)
return
@classmethod
def add(cls, values):
#~ print ('VALUES', values)
productos = util.loads(values.pop('productos'))
relacionados = util.loads(values.pop('relacionados'))
emisor = Emisor.select()[0]
values['folio'] = cls._get_folio(cls, values['serie'])
@ -1482,6 +1568,7 @@ class Facturas(BaseModel):
with database_proxy.atomic() as txn:
obj = Facturas.create(**values)
totals = cls._calculate_totals(cls, obj, productos)
cls._guardar_relacionados(cls, obj, relacionados)
obj.subtotal = totals['subtotal']
obj.total_trasladados = totals['total_trasladados']
obj.total_retenciones = totals['total_retenciones']
@ -1508,6 +1595,7 @@ class Facturas(BaseModel):
emisor = Emisor.select()[0]
certificado = Certificado.select()[0]
comprobante = {}
relacionados = {}
if invoice.serie:
comprobante['Serie'] = invoice.serie
if invoice.condiciones_pago:
@ -1529,6 +1617,11 @@ class Facturas(BaseModel):
comprobante['TipoDeComprobante'] = invoice.tipo_comprobante
comprobante['MetodoPago'] = invoice.metodo_pago
comprobante['LugarExpedicion'] = invoice.lugar_expedicion
if invoice.tipo_relacion:
relacionados = {
'tipo': invoice.tipo_relacion,
'cfdis': FacturasRelacionadas.get_(invoice),
}
emisor = {
'Rfc': emisor.rfc,
@ -1623,6 +1716,7 @@ class Facturas(BaseModel):
data = {
'comprobante': comprobante,
'relacionados': relacionados,
'emisor': emisor,
'receptor': receptor,
'conceptos': conceptos,
@ -2012,6 +2106,15 @@ class FacturasRelacionadas(BaseModel):
class Meta:
order_by = ('factura',)
@classmethod
def get_(cls, invoice):
query = (FacturasRelacionadas
.select()
.where(FacturasRelacionadas.factura==invoice)
)
return [str(r.factura_origen.uuid) for r in query]
class PreFacturasRelacionadas(BaseModel):
factura = ForeignKeyField(PreFacturas, related_name='original')
factura_origen = ForeignKeyField(PreFacturas, related_name='relacion')

View File

@ -36,7 +36,7 @@
{"key": "04", "name": "Sustitución de los CFDI previos", "activo": true, "default": true},
{"key": "05", "name": "Traslados de mercancias facturados previamente", "activo": true},
{"key": "06", "name": "Factura generada por los traslados previos", "activo": true},
{"key": "07", "name": "Actividad", "CFDI por aplicación de anticipo": true}
{"key": "07", "name": "CFDI por aplicación de anticipo", "activo": true}
]
},
{

View File

@ -1,6 +1,8 @@
var query = []
var grid = null
var msg = ''
var result = false
var tipo_relacion = ''
function get_condicion_pago(){
@ -108,6 +110,7 @@ function cmd_new_invoice_click(id, e, node){
grid.clearAll()
grid_totals.clearAll()
grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0})
$$('cmd_cfdi_relacionados').disable()
$$('multi_invoices').setValue('invoices_new')
form.focus('search_client_name')
}
@ -148,7 +151,7 @@ function cmd_delete_invoice_click(id, e, node){
return
}
var msg = '¿Estás seguro de eliminar la siguiente Factura?<BR><BR>'
msg = '¿Estás seguro de eliminar la siguiente Factura?<BR><BR>'
msg += '(' + row['folio'] + ') ' + row['cliente']
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
@ -228,7 +231,6 @@ function validate_invoice(values){
return false
}
return true
}
@ -325,17 +327,13 @@ function save_preinvoice(data){
}
function cmd_timbrar_click(id, e, node){
var form = this.getFormView();
if(!form.validate()) {
webix.message({type:'error', text:'Valores inválidos'})
return
}
var values = form.getValues();
if(!validate_invoice(values)){
return
function guardar_y_timbrar(values){
query = table_relaciones.chain().data()
var ids = []
if(query.length > 0){
for (i = 0; i < query.length; i++) {
ids.push(query[i]['id'])
}
}
var rows = grid.data.getRange()
@ -360,13 +358,53 @@ function cmd_timbrar_click(id, e, node){
data['metodo_pago'] = $$('lst_metodo_pago').getValue()
data['uso_cfdi'] = $$('lst_uso_cfdi').getValue()
data['regimen_fiscal'] = $$('lst_regimen_fiscal').getValue()
data['relacionados'] = ids
data['tipo_relacion'] = tipo_relacion
if(!save_invoice(data)){
return
}
form.setValues({id_partner: 0, lbl_partner: 'Ninguno'})
table_relaciones.clear()
tipo_relacion = ''
$$('form_invoice').setValues({id_partner: 0, lbl_partner: 'Ninguno'})
$$('multi_invoices').setValue('invoices_home')
}
function cmd_timbrar_click(id, e, node){
var form = this.getFormView();
if(!form.validate()) {
webix.message({type:'error', text:'Valores inválidos'})
return
}
var values = form.getValues()
if(!validate_invoice(values)){
return
}
query = table_relaciones.chain().data()
msg = '¿Todos los datos son correctos?<BR><BR>'
if(query.length > 0){
msg += 'La factura tiene CFDI relacionados<BR><BR>'
}
msg += '¿Estás seguro de timbrar esta factura?'
webix.confirm({
title: 'Timbrar Factura',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
guardar_y_timbrar(values)
}
}
})
}
@ -403,6 +441,7 @@ function set_client(row){
forma_pago: row.forma_pago, uso_cfdi: row.uso_cfdi}, true)
html += row.nombre + ' (' + row.rfc + ')</span>'
$$('lbl_client').setValue(html)
$$('cmd_cfdi_relacionados').enable()
form.focus('search_product_id')
}
@ -874,6 +913,7 @@ function reset_invoice(){
table_pt.clear()
table_totals.clear()
$$('cmd_cfdi_relacionados').disable()
form.focus('search_client_name')
}
@ -1130,6 +1170,143 @@ function grid_preinvoices_click(id, e, node){
}
function cmd_cfdi_relacionados_click(){
show('CFDI Relacionados, en desarrollo')
function get_facturas_por_cliente(){
var values = $$('form_invoice').getValues()
var id = values.id_partner
var y = $$('filter_cfdi_year').getValue()
var m = $$('filter_cfdi_month').getValue()
var ids = []
var rows = $$('grid_relacionados').data.getRange()
for (i = 0; i < rows.length; i++) {
ids.push(rows[i]['id'])
}
filters = {
'year': y,
'month': m,
'id_cliente': id,
'cfdis': ids,
'folio': $$('filter_cfdi_folio').getValue(),
'uuid': $$('filter_cfdi_uuid').getValue(),
'opt': 'relacionados'
}
var grid = $$('grid_cfdi_cliente')
webix.ajax().get('/invoices', filters, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
};
}
})
}
function get_info_cfdi_relacionados(){
webix.ajax().get('/values/tiporelacion', {key: true}, function(text, data){
var values = data.json()
$$('lst_tipo_relacion').getList().parse(values)
$$('lst_tipo_relacion').setValue(tipo_relacion)
})
query = table_relaciones.chain().data()
$$('grid_relacionados').parse(query)
get_facturas_por_cliente()
}
function cmd_cfdi_relacionados_click(){
var d = new Date()
ui_invoice.init()
var fy = $$('filter_cfdi_year')
var fm = $$('filter_cfdi_month')
fy.blockEvent()
fm.blockEvent()
$$('lbl_cfdi_cliente').setValue($$('lbl_client').getValue())
data = $$('filter_year').getList().data
fy.getList().data.sync(data)
fy.setValue(d.getFullYear())
fm.setValue(d.getMonth() + 1)
fy.unblockEvent()
fm.unblockEvent()
get_info_cfdi_relacionados()
$$('win_cfdi_relacionados').show()
}
function cmd_limpiar_relacionados_click(){
msg = '¿Estás seguro de quitar todas las relaciones<BR><BR>'
msg += 'ESTA ACCION NO SE PUEDE DESHACER'
webix.confirm({
title: 'Limpiar relaciones',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
$$('lst_tipo_relacion').setValue('')
$$('grid_relacionados').clearAll()
table_relaciones.clear()
tipo_relacion = ''
msg_sucess('Las relaciones han sido eliminadas')
}
}
})
}
function cmd_guardar_relacionados_click(){
var grid = $$('grid_relacionados')
var value = $$('lst_tipo_relacion').getValue()
if(value == '' || value == '-'){
msg_error('Selecciona el tipo de relación')
return
}
if(grid.count() == 0){
msg_error('Agrega al menos un CFDI a relacionar')
return
}
var data = grid.data.getRange()
table_relaciones.clear()
table_relaciones.insert(data)
tipo_relacion = value
msg_sucess('Relaciones guardadas correctamente')
}
function cmd_filter_relacionados_click(){
get_facturas_por_cliente()
}
function filter_cfdi_year_change(nv, ov){
cmd_filter_relacionados_click()
}
function filter_cfdi_month_change(nv, ov){
cmd_filter_relacionados_click()
}

View File

@ -12,6 +12,7 @@ var table_pt = db.addCollection('productstaxes')
var table_totals = db.addCollection('totals', {unique: ['tax']})
var table_series = db.addCollection('series')
var table_usocfdi = db.addCollection('usocfdi')
var table_relaciones = db.addCollection('relaciones')
function show(values){

View File

@ -1,4 +1,154 @@
var months = [
{id: -1, value: 'Todos'},
{id: 1, value: 'Enero'},
{id: 2, value: 'Febrero'},
{id: 3, value: 'Marzo'},
{id: 4, value: 'Abril'},
{id: 5, value: 'Mayo'},
{id: 6, value: 'Junio'},
{id: 7, value: 'Julio'},
{id: 8, value: 'Agosto'},
{id: 9, value: 'Septiembre'},
{id: 10, value: 'Octubre'},
{id: 11, value: 'Noviembre'},
{id: 12, value: 'Diciembre'},
]
var grid_cfdi_cliente_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
footer: {content: 'rowCount', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "header",
sort:"string"},
{id: 'folio', header: ['Folio'], adjust: 'data', sort: 'int',
css: 'right'},
{id: 'uuid', header: ['UUID', {content: 'textFilter'}], width: 250,
sort: 'string'},
{id: "fecha", header: ["Fecha y Hora"], width: 150, sort: 'date'},
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
adjust: 'header', sort: 'string'},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "header", sort:"string"},
{id: 'total_mn', header: ['Total M.N.'], width: 150,
sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
]
var grid_relacionados_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right'},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: "Serie", adjust: "header", sort:"string"},
{id: 'folio', header: 'Folio', adjust: 'data', sort: 'int', css: 'right'},
{id: 'uuid', header: 'UUID', width: 250, sort: 'string'},
{id: "fecha", header: "Fecha y Hora", width: 150, sort: 'date'},
{id: "tipo_comprobante", header: "Tipo", adjust: 'header', sort: 'string'},
{id: "estatus", header: "Estatus", adjust: "header", sort:"string"},
{id: 'total_mn', header: ['Total M.N.'], width: 150,
sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
]
var grid_cfdi_cliente = {
view: 'datatable',
id: 'grid_cfdi_cliente',
select: 'row',
autoConfig: false,
adjust: true,
height: 300,
resizeColumn: true,
headermenu: true,
drag: true,
columns: grid_cfdi_cliente_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
}
}
var grid_relacionados = {
view: 'datatable',
id: 'grid_relacionados',
select: 'row',
autoConfig: false,
adjust: true,
height: 200,
resizeColumn: true,
headermenu: true,
drag: true,
columns: grid_relacionados_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i + 1
})
}
}
}
var body_cfdi_relacionados = {rows: [
{cols: [
{view: 'label', id: 'lbl_cfdi_title', label: 'Cliente: ',
autowidth: true},
{view: 'label', id: 'lbl_cfdi_cliente', label: '', align: 'left'}]},
{view: 'richselect', id: 'lst_tipo_relacion', label: 'Tipo de Relación',
labelWidth: 150, required: true, options: []},
{minHeight: 10, maxHeight: 10},
{cols: [
{view: 'richselect', id: 'filter_cfdi_year', label: 'Año', width: 100,
labelAlign: 'center', labelPosition: 'top', options: []},
{view: 'richselect', id: 'filter_cfdi_month', label: 'Mes', width: 125,
labelAlign: 'center', labelPosition: 'top', options: months},
{view: 'text', id: 'filter_cfdi_folio', label: 'Folio', width: 125,
labelAlign: 'center', labelPosition: 'top'},
{view: 'text', id: 'filter_cfdi_uuid', label: 'UUID',
labelAlign: 'center', labelPosition: 'top'},
{view: 'icon', id: 'cmd_filter_relacionados', icon: 'filter'},
]},
grid_cfdi_cliente,
{minHeight: 10, maxHeight: 10},
{view: 'label', label: 'CFDI Relacionados'},
grid_relacionados,
{minHeight: 10, maxHeight: 10},
{cols: [{},
{view: 'button', id: 'cmd_guardar_relacionados', label: 'Relacionar'},
{view: 'button', id: 'cmd_limpiar_relacionados', label: 'Limpiar'},
{}]},
{minHeight: 15, maxHeight: 15},
]}
var ui_invoice = {
init: function(){
webix.ui({
view: 'window',
id: 'win_cfdi_relacionados',
autoheight: true,
width: 850,
modal: true,
position: 'center',
head: {view: 'toolbar',
elements: [
{view: 'label', label: 'CFDI Relacionados'},
{view: 'icon', icon: 'times-circle',
click: '$$("win_cfdi_relacionados").close()'},
]
},
body: body_cfdi_relacionados,
})
$$('cmd_guardar_relacionados').attachEvent('onItemClick', cmd_guardar_relacionados_click)
$$('cmd_limpiar_relacionados').attachEvent('onItemClick', cmd_limpiar_relacionados_click)
$$('cmd_filter_relacionados').attachEvent('onItemClick', cmd_filter_relacionados_click)
$$('filter_cfdi_year').attachEvent('onChange', filter_cfdi_year_change)
$$('filter_cfdi_month').attachEvent('onChange', filter_cfdi_month_change)
}}
var toolbar_invoices = [
{view: "button", id: "cmd_new_invoice", label: "Nueva", type: "iconButton",
@ -28,23 +178,6 @@ var toolbar_invoices_generate = {view: 'toolbar', elements: [{},
]}
var months = [
{id: -1, value: 'Todos'},
{id: 1, value: 'Enero'},
{id: 2, value: 'Febrero'},
{id: 3, value: 'Marzo'},
{id: 4, value: 'Abril'},
{id: 5, value: 'Mayo'},
{id: 6, value: 'Junio'},
{id: 7, value: 'Julio'},
{id: 8, value: 'Agosto'},
{id: 9, value: 'Septiembre'},
{id: 10, value: 'Octubre'},
{id: 11, value: 'Noviembre'},
{id: 12, value: 'Diciembre'},
]
var toolbar_invoices_filter = [
{view: 'richselect', id: 'filter_year', label: 'Año', labelAlign: 'right',
labelWidth: 50, width: 150, options: []},
@ -443,3 +576,6 @@ var app_invoices = {
}