Generar archivo para productos

This commit is contained in:
Mauricio Baeza 2017-11-15 19:29:51 -06:00
parent cd18ec5079
commit 4e6c5ffa2c
8 changed files with 281 additions and 13 deletions

View File

@ -36,7 +36,7 @@ from dateutil import parser
from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \
PATH_XMLSEC, TEMPLATE_CANCEL, IMPUESTOS
PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO
#~ def _get_hash(password):
@ -1277,10 +1277,10 @@ class ImportFacturaLibre(object):
self._rfc = rfc
self._con = None
self._cursor = None
self._error = ''
self._is_connect = self._connect(path)
self._clientes = []
self._clientes_rfc = []
self._error = ''
@property
def error(self):
@ -1299,7 +1299,7 @@ class ImportFacturaLibre(object):
return False
if obj['rfc'] != self._rfc:
self._error = 'Los datos no corresponden al emisor: {}'.format(self._rfc)
self._error = 'Los datos no corresponden al RFC: {}'.format(self._rfc)
return False
return True
@ -1328,6 +1328,7 @@ class ImportFacturaLibre(object):
tables = (
('receptores', 'Socios'),
('cfdfacturas', 'Facturas'),
('categorias', 'Categorias'),
)
for source, target in tables:
data[target] = self._get_table(source)
@ -1339,6 +1340,63 @@ class ImportFacturaLibre(object):
def _get_table(self, table):
return getattr(self, '_{}'.format(table))()
def import_productos(self):
sql = "SELECT * FROM productos"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('id_categoria', 'categoria'),
('noIdentificacion', 'clave'),
('descripcion', 'descripcion'),
('unidad', 'unidad'),
('valorUnitario', 'valor_unitario'),
('existencia', 'existencia'),
('inventario', 'inventario'),
('codigobarras', 'codigo_barras'),
('CuentaPredial', 'cuenta_predial'),
('precio_compra', 'ultimo_precio'),
('minimo', 'minimo'),
)
data = []
sql = """
SELECT nombre, tasa, tipo
FROM impuestos, productos, productosimpuestos
WHERE productos.id=productosimpuestos.id_producto
AND productosimpuestos.id_impuesto=impuestos.id
AND productos.id = ?
"""
for row in rows:
new = {t: row[s] for s, t in fields}
new['descripcion'] = ' '.join(new['descripcion'].split())
new['clave_sat'] = DEFAULT_SAT_PRODUCTO
self._cursor.execute(sql, (row['id'],))
impuestos = self._cursor.fetchall()
new['impuestos'] = tuple(impuestos)
data.append(new)
return data
def _categorias(self):
sql = "SELECT * FROM categorias"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('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=?"
self._cursor.execute(sql, [invoice['id_cliente']])

View File

@ -15,7 +15,7 @@ if __name__ == '__main__':
from controllers import util
from settings import log, VERSION, PATH_CP, COMPANIES, PRE, CURRENT_CFDI, \
INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS
INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS, DEFAULT_SAT_PRODUCTO
FORMAT = '{0:.2f}'
@ -529,6 +529,10 @@ class Categorias(BaseModel):
(('categoria', 'padre'), True),
)
@classmethod
def exists(cls, filters):
return Categorias.select().where(filters).exists()
@classmethod
def get_all(cls):
rows = (Categorias.select(
@ -536,6 +540,9 @@ class Categorias(BaseModel):
Categorias.categoria.alias('value'),
Categorias.padre.alias('parent_id'))
).dicts()
for row in rows:
if row['parent_id'] is None:
row['parent_id'] = 0
return tuple(rows)
@ -3073,6 +3080,112 @@ def _importar_facturas(rows):
return
def _importar_categorias(rows):
log.info('\tImportando Categorías...')
for row in rows:
if row['padre'] is None:
filters = (
(Categorias.categoria==row['categoria']) &
(Categorias.padre.is_null(True))
)
else:
filters = (
(Categorias.categoria==row['categoria']) &
(Categorias.padre==row['padre'])
)
if Categorias.exists(filters):
continue
try:
Categorias.create(**row)
except IntegrityError:
msg = '\tCategoria: ({}) {}'.format(row['padre'], row['categoria'])
log.error(msg)
log.info('\tCategorías importadas...')
return
def _get_id_unidad(unidad):
obj = SATUnidades.select(SATUnidades.id).where(SATUnidades.name==unidad)
if obj is None:
msg = 'No se encontró: {}'.format(unidad)
log.error('\t', msg)
return unidad
return str(obj[0].id)
def _get_impuestos(impuestos):
lines = '|'
for impuesto in impuestos:
info = (
IMPUESTOS.get(impuesto['nombre']),
impuesto['nombre'],
impuesto['tipo'][0],
str(round(float(impuesto['tasa']) / 100.0, 6)),
)
lines += '|'.join(info)
return lines
def _generar_archivo_productos(archivo):
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.ImportFacturaLibre(archivo, rfc)
if not app.is_connect:
log.error('\t{}'.format(app._error))
return
rows = app.import_productos()
p, _, _, _ = util.get_path_info(archivo)
path_txt = util._join(p, 'productos.txt')
log.info('\tGenerando archivo: {}'.format(path_txt))
fields = (
'clave',
'clave_sat',
'unidad',
'categoria',
'descripcion',
'valor_unitario',
'existencia',
'inventario',
'codigo_barras',
'cuenta_predial',
'ultimo_precio',
'minimo',
)
data = ['|'.join(fields)]
for row in rows:
impuestos = row.pop('impuestos', ())
line = [str(row[r]) for r in fields]
line[2] = _get_id_unidad(line[2])
line = '|'.join(line) + _get_impuestos(impuestos)
data.append(line)
with open(path_txt, 'w') as fh:
fh.write('\n'.join(data))
log.info('\tArchivo generado: {}'.format(path_txt))
return
def _importar_factura_libre(archivo):
rfc = input('Introduce el RFC: ').strip().upper()
if not rfc:
@ -3089,18 +3202,39 @@ def _importar_factura_libre(archivo):
log.info('Importando datos...')
app = util.ImportFacturaLibre(archivo, rfc)
if not app.is_connect:
log.error('\t{}'.format(app.error))
log.error('\t{}'.format(app._error))
return
data = app.import_data()
_importar_socios(data['Socios'])
_importar_facturas(data['Facturas'])
_importar_categorias(data['Categorias'])
#~ _generar_archivo_productos(data['Productos'], archivo)
log.info('Importación terminada...')
return
def _test():
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)
rows = Categorias.select().where(
Categorias.categoria=='Productos', Categorias.padre.is_null(True)).exists()
print (rows)
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'
@ -3125,10 +3259,19 @@ help_lr = 'Listar RFCs'
@click.option('-i', '--importar-valores', is_flag=True, default=False)
@click.option('-a', '--archivo')
@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)
def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, rfc,
borrar_rfc, listar_rfc, importar_valores, archivo, factura_libre):
borrar_rfc, listar_rfc, importar_valores, archivo, factura_libre, test,
generar_archivo_productos, importar_productos):
opt = locals()
if opt['test']:
_test()
sys.exit(0)
if opt['iniciar_bd']:
_iniciar_bd()
sys.exit(0)
@ -3183,6 +3326,36 @@ def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, rfc,
_importar_factura_libre(opt['archivo'])
sys.exit(0)
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)
_generar_archivo_productos(opt['archivo'])
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)
_importar_productos(opt['archivo'])
sys.exit(0)
return

View File

@ -102,3 +102,4 @@ IMPUESTOS = {
'ICIC': '000',
'CEDULAR': '000',
}
DEFAULT_SAT_PRODUCTO = '01010101'

View File

@ -1,13 +1,21 @@
function get_categorias(){
webix.ajax().sync().get('/values/categorias', function(text, data){
var values = data.json()
$$('categoria').getList().parse(values, 'plainjs')
})
}
function cmd_new_product_click(id, e, node){
$$('form_product').setValues({
id: 0, es_activo_producto: true})
id: 0, es_activo_producto: true})
add_config({'key': 'id_product', 'value': ''})
get_new_key()
get_taxes()
get_categorias()
$$('grid_products').clearSelection()
$$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
$$("multi_products").setValue("product_new")
}

View File

@ -153,3 +153,29 @@ function get_config(value){
return key.value
}
}
webix.DataDriver.plainjs = webix.extend({
arr2hash:function(data){
var hash = {};
for (var i=0; i<data.length; i++){
var pid = data[i].parent_id;
if (!hash[pid]) hash[pid]=[];
hash[pid].push(data[i]);
}
return hash;
},
hash2tree:function(hash, level){
var top = hash[level];
for (var i=0; i<top.length; i++){
var branch = top[i].id;
if (hash[branch])
top[i].data = this.hash2tree(hash, branch);
}
return top;
},
getRecords:function(data, id){
var hash = this.arr2hash(data);
return this.hash2tree(hash, 0);
}
}, webix.DataDriver.json)

View File

@ -5,7 +5,8 @@ var menu_data = [
{id: 'app_folios', icon: 'sort-numeric-asc', value: 'Folios'},
{id: 'app_correo', icon: 'envelope-o', value: 'Correo'},
{id: 'app_sat', icon: 'table', value: 'Catalogos SAT'},
{id: 'app_options', icon: 'cog', value: 'Opciones'},
{id: 'app_options', icon: 'check-circle-o', value: 'Opciones'},
{id: 'app_utilidades', icon: 'cog', value: 'Utilidades'},
]

View File

@ -286,12 +286,12 @@ var form_partner = {
view: 'form',
id: 'form_partner',
complexData: true,
scroll: true,
elements: controls_partner,
elementsConfig: {
labelWidth: 150,
labelAlign: 'right'
},
autoheight: true,
rules: {
nombre: function(value){ return value.trim() != '';},
rfc: validate_rfc,
@ -308,7 +308,7 @@ var multi_partners = {
{view: 'toolbar', elements: toolbar_partners},
grid_partners,
]},
{id: 'partners_new', rows:[form_partner, {}]}
{id: 'partners_new', rows:[form_partner]}
]
}

View File

@ -34,6 +34,7 @@ var grid_products = {
columns: grid_products_cols,
}
var suggest_categories = {
view: 'datasuggest',
type: 'tree',
@ -85,7 +86,7 @@ var suggest_sat_producto = {
this.hide()
}
}
}
},
}
@ -95,7 +96,7 @@ var controls_generals = [
bottomLabel: 'Se recomienda solo desactivar y no eliminar'},
{cols: [
{view: 'combo', id: 'categoria', name: 'categoria', label: 'Categoría',
labelPosition: 'top', options: suggest_categories},
labelPosition: 'top', suggest: suggest_categories},
{view: 'text', id: 'clave', name: 'clave', label: 'Clave',
labelPosition: 'top', readonly: true, required: true},
{view: 'checkbox', id: 'chk_automatica', label: 'Automática',