diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index 1463e5f..ffb2b7c 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -928,7 +928,7 @@ class LIBO(object):
options = {'AsTemplate': True, 'Hidden': True}
doc = self._doc_open(path, options)
if doc is None:
- return ()
+ return (), 'No se pudo abrir la plantilla'
data, msg = self._get_data(doc)
doc.close(True)
@@ -952,6 +952,22 @@ class LIBO(object):
rows = [dict(zip(fields, r)) for r in data[1:]]
return rows, ''
+ def invoice(self, path):
+ options = {'AsTemplate': True, 'Hidden': True}
+ doc = self._doc_open(path, options)
+ if doc is None:
+ return (), 'No se pudo abrir la plantilla'
+
+ data, msg = self._get_data(doc)
+ doc.close(True)
+
+ if len(data) == 1:
+ msg = 'Sin datos para importar'
+ return (), msg
+
+ rows = tuple(data[1:])
+ return rows, ''
+
def to_pdf(data, emisor_rfc, ods=False):
rfc = data['emisor']['rfc']
@@ -1390,6 +1406,15 @@ def upload_file(rfc, opt, file_obj):
name = '{}_products.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
+ elif opt == 'invoiceods':
+ tmp = file_obj.filename.split('.')
+ ext = tmp[-1].lower()
+ if ext != 'ods':
+ msg = 'Extensión de archivo incorrecta, selecciona un archivo ODS'
+ return {'status': 'server', 'name': msg, 'ok': False}
+
+ name = '{}_invoice.ods'.format(rfc.lower())
+ path = _join(PATH_MEDIA, 'tmp', name)
if save_file(path, file_obj.file.read()):
return {'status': 'server', 'name': file_obj.filename, 'ok': True}
@@ -2601,11 +2626,26 @@ def import_products(rfc):
name = '{}_products.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
if not is_file(path):
- return ()
+ return (), 'No se encontró la plantilla'
if APP_LIBO:
app = LIBO()
if app.is_running:
return app.products(path)
- return ()
\ No newline at end of file
+ return (), 'No se encontro LibreOffice'
+
+
+def import_invoice(rfc):
+ name = '{}_invoice.ods'.format(rfc.lower())
+ path = _join(PATH_MEDIA, 'tmp', name)
+ if not is_file(path):
+ return (), 'No se encontró la plantilla'
+
+ if APP_LIBO:
+ app = LIBO()
+ if app.is_running:
+ return app.invoice(path)
+
+ return (), 'No se encontro LibreOffice'
+
diff --git a/source/app/models/db.py b/source/app/models/db.py
index a3d0923..cf12c63 100644
--- a/source/app/models/db.py
+++ b/source/app/models/db.py
@@ -25,6 +25,9 @@ class StorageEngine(object):
return getattr(self, '_get_{}'.format(table))(values, session)
return getattr(self, '_get_{}'.format(table))(values)
+ def _get_importinvoice(self, values):
+ return main.import_invoice()
+
def _get_main(self, values):
return main.config_main()
diff --git a/source/app/models/main.py b/source/app/models/main.py
index 3bddc11..cc064b1 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -104,6 +104,62 @@ def validar_timbrar():
return {'ok': True, 'msg': msg}
+
+def _get_taxes_product(id):
+ model_pt = Productos.impuestos.get_through_model()
+ impuestos = tuple(model_pt
+ .select(
+ model_pt.productos_id.alias('product'),
+ model_pt.satimpuestos_id.alias('tax'))
+ .where(model_pt.productos_id==id).dicts())
+ return impuestos
+
+
+def import_invoice():
+ log.info('Importando factura...')
+ emisor = Emisor.select()[0]
+ rows, msg = util.import_invoice(emisor.rfc)
+ if not rows:
+ return {'ok': False, 'msg': msg}
+
+ # ~ clave, descripcion, precio, cantidad
+ products = {}
+ for row in rows:
+ try:
+ obj = Productos.get(Productos.clave==row[0])
+ if obj.id in products:
+ vu = round(row[2], 2)
+ cant = round(row[3], 2)
+ pf = products[obj.id]['valor_unitario'] - float(obj.descuento)
+ products[obj.id]['cantidad'] += cant
+ products[obj.id]['importe'] = round(
+ pf * products[obj.id]['cantidad'], DECIMALES)
+ if vu != products[obj.id]['valor_unitario']:
+ msg = 'Precio diferente en producto: {}'.format(row[0])
+ return {'ok': False, 'msg': msg}
+ else:
+ vu = round(row[2], 2)
+ cant = round(row[3], 2)
+ pf = vu - float(obj.descuento)
+ p = {
+ 'id': obj.id,
+ 'delete': '-',
+ 'clave': obj.clave,
+ 'descripcion': obj.descripcion,
+ 'unidad': obj.unidad.name,
+ 'cantidad': cant,
+ 'valor_unitario': vu,
+ 'descuento': obj.descuento,
+ 'importe': round(pf * cant, DECIMALES),
+ 'taxes': _get_taxes_product(obj.id),
+ }
+ products[obj.id] = p
+ except Productos.DoesNotExist:
+ pass
+ log.info('Factura importada...')
+ return {'ok': True, 'rows': tuple(products.values())}
+
+
def get_doc(type_doc, id, rfc):
types = {
'xml': 'application/xml',
@@ -2231,21 +2287,21 @@ class Productos(BaseModel):
q.execute()
obj = Productos.get(w)
obj.impuestos = taxes
- log.info('Producto actualizado...')
+ log.info('\tProducto actualizado: {}'.format(data['clave']))
ap += 1
else:
obj = Productos.create(**data)
obj.impuestos = taxes
- log.info('Producto agregado...')
+ log.info('\tProducto agregado: {}'.format(data['clave']))
np += 1
except Exception as e:
msg = 'Error al importar producto: {}'.format(data['clave'])
log.error(msg)
log.error(e)
- msg = 'Productos encontrados: {}\n'.format(len(rows))
- msg += 'Productos agregados: {}\n'.format(np)
- msg += 'Productos actualizados: {}'.format(ap)
+ msg = 'Productos encontrados: {}
'.format(len(rows))
+ msg += 'Productos agregados: {}
'.format(np)
+ msg += 'Productos actualizados: {}
'.format(ap)
msg += 'Productos con problemas: {}'.format(len(rows) - np - ap)
return {'ok': True, 'msg': msg}
diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js
index f7b03b8..4bc2f6f 100644
--- a/source/static/js/controller/invoices.js
+++ b/source/static/js/controller/invoices.js
@@ -43,6 +43,7 @@ var invoices_controllers = {
$$('cmd_cfdi_notes').attachEvent('onItemClick', cmd_cfdi_notes_click)
$$('cmd_admin_invoice_notes').attachEvent('onItemClick', cmd_admin_invoice_notes_click)
+ $$('cmd_import_invoice').attachEvent('onItemClick', cmd_import_invoice_click)
webix.extend($$('grid_invoices'), webix.ProgressBar)
}
@@ -858,7 +859,6 @@ function set_product(values){
table_pt.insert(v)
}
}
- //~ calculate_taxes()
calcular_impuestos()
}
@@ -868,25 +868,6 @@ function grid_products_found_click(obj){
}
-//~ function search_product_by_id(id){
- //~ webix.ajax().get('/values/product', {'id': id}, {
- //~ error: function(text, data, xhr) {
- //~ msg_error('Error al consultar')
- //~ },
- //~ success: function(text, data, xhr){
- //~ var values = data.json()
- //~ if (values.ok){
- //~ set_product(values)
- //~ } else {
- //~ msg = 'No se encontró un producto con la clave: ' + id
- //~ msg_error(msg)
- //~ }
- //~ }
- //~ })
-
-//~ }
-
-
function search_product_by_key(key){
webix.ajax().get('/values/productokey', {'key': key}, {
error: function(text, data, xhr) {
@@ -1864,4 +1845,112 @@ function cmd_admin_invoice_notes_click(){
to_end('invoice_notes')
})
cfg_invoice['notes'] = true
-}
\ No newline at end of file
+}
+
+
+function cmd_import_invoice_click(){
+ win_import_invoice.init()
+ $$('win_import_invoice').show()
+}
+
+
+function cmd_upload_invoice_click(){
+ var form = $$('form_upload_invoice')
+
+ var values = form.getValues()
+
+ if(!$$('lst_upload_invoice').count()){
+ $$('win_import_invoice').close()
+ return
+ }
+
+ if($$('lst_upload_invoice').count() > 1){
+ msg = 'Selecciona solo un archivo'
+ msg_error(msg)
+ return
+ }
+
+ var template = $$('up_invoice').files.getItem($$('up_invoice').files.getFirstId())
+
+ if(template.type.toLowerCase() != 'ods'){
+ msg = 'Archivo inválido.\n\nSe requiere un archivo ODS'
+ msg_error(msg)
+ return
+ }
+
+ msg = '¿Estás seguro de importar este archivo?'
+ webix.confirm({
+ title: 'Importar Factura',
+ ok: 'Si',
+ cancel: 'No',
+ type: 'confirm-error',
+ text: msg,
+ callback:function(result){
+ if(result){
+ $$('up_invoice').send()
+ }
+ }
+ })
+}
+
+
+function add_import_product_taxes(taxes){
+ //~ var taxes = values.taxes
+ //~ var row = grid.getItem(values.id)
+
+ //~ values['delete'] = '-'
+ //~ if (row == undefined){
+ //~ grid.add(values)
+ //~ } else {
+ //~ values['cantidad'] = parseFloat(row.cantidad) + parseFloat(values['cantidad'])
+ //~ values['valor_unitario'] = parseFloat(row.valor_unitario)
+ //~ values['descuento'] = parseFloat(row.descuento)
+ //~ var precio_final = values['valor_unitario'] - values['descuento']
+ //~ values['importe'] = (precio_final * values['cantidad']).round(DECIMALES)
+ //~ grid.updateItem(row.id, values)
+ //~ }
+
+ for(var v of taxes){
+ var pt = table_pt.findOne(v)
+ if(pt === null){
+ table_pt.insert(v)
+ }
+ }
+}
+
+
+function up_invoice_upload_complete(response){
+ if(response.status != 'server'){
+ msg = 'Ocurrio un error al subir el archivo'
+ msg_error(msg)
+ return
+ }
+ msg = 'Archivo subido correctamente.\n\nComenzando importación.'
+ msg_ok(msg)
+ $$('win_import_invoice').close()
+
+ webix.ajax().get('/values/importinvoice', {
+ error: function(text, data, xhr) {
+ msg_error('Error al consultar')
+ },
+ success: function(text, data, xhr){
+ var values = data.json()
+ if (values.ok){
+ for(var p of values.rows){
+ add_import_product_taxes(p.taxes)
+ }
+ grid.clearAll()
+ grid.parse(values.rows, 'json')
+ grid.refresh()
+ calcular_impuestos()
+ }else{
+ webix.alert({
+ title: 'Error al importar',
+ text: values.msg,
+ type: 'alert-error',
+ })
+ }
+ }
+ })
+}
+
diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js
index 0e68323..d2bbf9e 100644
--- a/source/static/js/controller/main.js
+++ b/source/static/js/controller/main.js
@@ -75,23 +75,6 @@ function get_partners(){
}
-function get_products(){
- var grid = $$('grid_products')
- webix.ajax().get('/products', {}, {
- error: function(text, data, xhr) {
- msg_error('Error al consultar')
- },
- success: function(text, data, xhr) {
- var values = data.json();
- grid.clearAll();
- if (values.ok){
- grid.parse(values.rows, 'json');
- };
- }
- });
-}
-
-
function menu_user_click(id, e, node){
if (id == 1){
window.location = '/logout';
diff --git a/source/static/js/controller/products.js b/source/static/js/controller/products.js
index 1c081f4..d409a87 100644
--- a/source/static/js/controller/products.js
+++ b/source/static/js/controller/products.js
@@ -50,6 +50,24 @@ function get_categorias(){
}
+function get_products(){
+ var grid = $$('grid_products')
+ webix.ajax().get('/products', {}, {
+ error: function(text, data, xhr) {
+ msg_error('Error al consultar')
+ },
+ success: function(text, data, xhr) {
+ var values = data.json();
+ grid.clearAll();
+ if (values.ok){
+ grid.parse(values.rows, 'json');
+ grid.refresh()
+ };
+ }
+ });
+}
+
+
function cmd_new_product_click(id, e, node){
get_taxes()
configurar_productos(true)
@@ -378,8 +396,11 @@ function up_products_upload_complete(response){
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
- msg_ok(values.msg)
get_products()
+ webix.alert({
+ title: 'Importación terminada',
+ text: values.msg,
+ })
}else{
msg_error(values.msg)
}
diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js
index eb39161..754bf82 100644
--- a/source/static/js/ui/invoices.js
+++ b/source/static/js/ui/invoices.js
@@ -201,6 +201,8 @@ var toolbar_invoices_generate = {view: 'toolbar', elements: [{},
type: 'iconButton', autowidth: true, icon: 'commenting-o'},
{view: 'button', id: 'cmd_cfdi_relacionados', label: 'CFDI Relacionados',
type: 'iconButton', autowidth: true, icon: 'file-o'},
+ {view: 'button', id: 'cmd_import_invoice', label: 'Importar',
+ type: 'iconButton', autowidth: true, icon: 'upload'},
{view: 'checkbox', id: 'chk_cfdi_anticipo', labelRight: 'Es Anticipo',
labelWidth: 0, width: 100, hidden: true},
{view: 'checkbox', id: 'chk_cfdi_donativo', labelRight: 'Es Donativo',
@@ -277,7 +279,8 @@ var grid_invoices = {
var grid_details_cols = [
{id: "id", header:"ID", hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
- {id: "clave", header:{text: 'Clave', css: 'center'}, width: 100},
+ {id: "clave", header:{text: 'Clave', css: 'center'}, width: 100,
+ adjust: 'data'},
{id: "clave_sat", hidden: true},
{id: "descripcion", header:{text: 'Descripción', css: 'center'},
fillspace: true, editor: 'popup'},
@@ -661,3 +664,35 @@ var app_invoices = {
multi_invoices
],
}
+
+
+var body_upload_invoice = {rows: [
+ {view: 'form', id: 'form_upload_invoice', rows: [
+ {cols: [{},
+ {view: 'uploader', id: 'up_invoice', autosend: false,
+ link: 'lst_upload_invoice', value: 'Seleccionar Archivo',
+ upload: '/files/invoiceods'}, {}]},
+ {cols: [
+ {view: 'list', id: 'lst_upload_invoice', name: 'lst_upload_invoice',
+ type: 'uploader', autoheight: true, borderless: true}]},
+ {cols: [{}, {view: 'button', id: 'cmd_upload_invoice',
+ label: 'Importar Factura'}, {}]},
+ ]},
+]}
+
+
+var win_import_invoice = {
+ init: function(){
+ webix.ui({
+ view: 'window',
+ id: 'win_import_invoice',
+ width: 400,
+ modal: true,
+ position: 'center',
+ head: 'Importar Factura de Plantilla',
+ body: body_upload_invoice,
+ })
+ $$('cmd_upload_invoice').attachEvent('onItemClick', cmd_upload_invoice_click)
+ $$('up_invoice').attachEvent('onUploadComplete', up_invoice_upload_complete)
+ }
+}
\ No newline at end of file