forked from elmau/empresa-libre
Generar archivo para productos
This commit is contained in:
parent
cd18ec5079
commit
4e6c5ffa2c
|
@ -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']])
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -102,3 +102,4 @@ IMPUESTOS = {
|
|||
'ICIC': '000',
|
||||
'CEDULAR': '000',
|
||||
}
|
||||
DEFAULT_SAT_PRODUCTO = '01010101'
|
||||
|
|
|
@ -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})
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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]}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue