Merge branch 'develop'

Permitir repetir productos al facturar
This commit is contained in:
Mauricio Baeza 2018-02-09 20:38:46 -06:00
commit bb3f6d3aed
8 changed files with 76 additions and 66 deletions

View File

@ -313,7 +313,10 @@ class SendMail(object):
message.attach(MIMEText(options['mensaje'], 'html')) message.attach(MIMEText(options['mensaje'], 'html'))
for f in options['files']: for f in options['files']:
part = MIMEBase('application', 'octet-stream') part = MIMEBase('application', 'octet-stream')
part.set_payload(f[0]) if isinstance(f[0], str):
part.set_payload(f[0].encode('utf-8'))
else:
part.set_payload(f[0])
encoders.encode_base64(part) encoders.encode_base64(part)
part.add_header( part.add_header(
'Content-Disposition', 'Content-Disposition',

View File

@ -1061,24 +1061,23 @@ class LIBO(object):
self._template.setPrinter(self._set_properties({'PaperFormat': LETTER})) self._template.setPrinter(self._set_properties({'PaperFormat': LETTER}))
self._render(data) self._render(data)
# ~ path = '{}.ods'.format(tempfile.mkstemp()[1]) path_ods = get_path_temp('.ods')
path = get_path_temp('.ods') self._template.storeToURL(self._path_url(path_ods), ())
self._template.storeToURL(self._path_url(path), ())
if ods: if ods:
data = self._read(path) data = self._read(path_ods)
_kill(path) _kill(path_ods)
return data return data
doc = self._doc_open(path, {'Hidden': True}) doc = self._doc_open(path_ods, {'Hidden': True})
options = {'FilterName': 'calc_pdf_Export'} options = {'FilterName': 'calc_pdf_Export'}
# ~ path = tempfile.mkstemp()[1] path_pdf = get_path_temp('.pdf')
path = get_path_temp('.pdf') doc.storeToURL(self._path_url(path_pdf), self._set_properties(options))
doc.storeToURL(self._path_url(path), self._set_properties(options))
doc.close(True) doc.close(True)
self._template.close(True) self._template.close(True)
data = self._read(path) data = self._read(path_pdf)
_kill(path) _kill(path_ods)
_kill(path_pdf)
return data return data
def _get_data(self, doc, name=0): def _get_data(self, doc, name=0):
@ -1674,7 +1673,8 @@ def _nomina(doc, data, values, version_cfdi):
info = CaseInsensitiveDict(node_nomina.attrib.copy()) info = CaseInsensitiveDict(node_nomina.attrib.copy())
node = node_nomina.find('{}Emisor'.format(PRE['NOMINA'][version])) node = node_nomina.find('{}Emisor'.format(PRE['NOMINA'][version]))
data['emisor'].update(CaseInsensitiveDict(node.attrib.copy())) if not node is None:
data['emisor'].update(CaseInsensitiveDict(node.attrib.copy()))
node = node_nomina.find('{}Receptor'.format(PRE['NOMINA'][version])) node = node_nomina.find('{}Receptor'.format(PRE['NOMINA'][version]))
data['receptor'].update(CaseInsensitiveDict(node.attrib.copy())) data['receptor'].update(CaseInsensitiveDict(node.attrib.copy()))

View File

@ -136,44 +136,31 @@ def import_invoice():
if not rows: if not rows:
return {'ok': False, 'msg': msg} return {'ok': False, 'msg': msg}
# ~ clave, descripcion, precio, cantidad products = []
products = {}
for row in rows: for row in rows:
try: try:
obj = Productos.get(Productos.clave==row[0]) obj = Productos.get(Productos.clave==row[0])
if obj.id in products: vu = round(row[2], 2)
vu = round(row[2], 2) descuento = round(row[3], 2)
descuento = round(row[3], 2) cant = round(row[4], 2)
cant = round(row[4], 2) pf = vu - descuento
pf = products[obj.id]['valor_unitario'] - descuento p = {
products[obj.id]['cantidad'] += cant 'id_product': obj.id,
products[obj.id]['importe'] = round( 'delete': '-',
pf * products[obj.id]['cantidad'], DECIMALES) 'clave': obj.clave,
if vu != products[obj.id]['valor_unitario']: 'descripcion': obj.descripcion,
msg = 'Precio diferente en producto: {}'.format(row[0]) 'unidad': obj.unidad.id,
return {'ok': False, 'msg': msg} 'cantidad': cant,
else: 'valor_unitario': vu,
vu = round(row[2], 2) 'descuento': descuento,
descuento = round(row[3], 2) 'importe': round(pf * cant, DECIMALES),
cant = round(row[4], 2) 'taxes': _get_taxes_product(obj.id),
pf = vu - descuento }
p = { products.append(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: except Productos.DoesNotExist:
pass pass
log.info('Factura importada...') log.info('Factura importada...')
return {'ok': True, 'rows': tuple(products.values())} return {'ok': True, 'rows': tuple(products)}
def get_doc(type_doc, id, rfc): def get_doc(type_doc, id, rfc):
@ -246,6 +233,7 @@ def config_timbrar():
'cfdi_tax_locales': Configuracion.get_bool('chk_config_tax_locales'), 'cfdi_tax_locales': Configuracion.get_bool('chk_config_tax_locales'),
'cfdi_tax_decimals': Configuracion.get_bool('chk_config_tax_decimals'), 'cfdi_tax_decimals': Configuracion.get_bool('chk_config_tax_decimals'),
'cfdi_with_taxes': Configuracion.get_bool('chk_config_price_with_taxes_in_invoice'), 'cfdi_with_taxes': Configuracion.get_bool('chk_config_price_with_taxes_in_invoice'),
'cfdi_add_same_product': Configuracion.get_bool('chk_config_add_same_product'),
} }
return conf return conf
@ -333,6 +321,7 @@ class Configuracion(BaseModel):
'chk_config_tax_locales', 'chk_config_tax_locales',
'chk_config_tax_decimals', 'chk_config_tax_decimals',
'chk_config_price_with_taxes_in_invoice', 'chk_config_price_with_taxes_in_invoice',
'chk_config_add_same_product',
'chk_config_anticipo', 'chk_config_anticipo',
'chk_config_cuenta_predial', 'chk_config_cuenta_predial',
'chk_config_codigo_barras', 'chk_config_codigo_barras',
@ -2748,11 +2737,11 @@ class Productos(BaseModel):
clave = values.get('key', '') clave = values.get('key', '')
row = (Productos row = (Productos
.select( .select(
Productos.id, Productos.id.alias('id_product'),
Productos.clave, Productos.clave,
Productos.clave_sat, Productos.clave_sat,
Productos.descripcion, Productos.descripcion,
SATUnidades.name.alias('unidad'), SATUnidades.id.alias('unidad'),
Productos.valor_unitario, Productos.valor_unitario,
Productos.descuento) Productos.descuento)
.join(SATUnidades).switch(Productos) .join(SATUnidades).switch(Productos)
@ -2761,7 +2750,7 @@ class Productos(BaseModel):
.dicts() .dicts()
) )
if len(row): if len(row):
id = row[0]['id'] id = row[0]['id_product']
model_pt = Productos.impuestos.get_through_model() model_pt = Productos.impuestos.get_through_model()
taxes = tuple(model_pt taxes = tuple(model_pt
.select( .select(
@ -3360,7 +3349,7 @@ class Facturas(BaseModel):
} }
options = { options = {
'para': obj.cliente.correo_facturas, 'para': obj.cliente.correo_facturas,
'copia': values['correo_copia'], 'copia': values.get('correo_copia', ''),
'confirmar': util.get_bool(values.get('correo_confirmacion', '0')), 'confirmar': util.get_bool(values.get('correo_confirmacion', '0')),
'asunto': util.make_info_mail(values['correo_asunto'], fields), 'asunto': util.make_info_mail(values['correo_asunto'], fields),
'mensaje': util.make_info_mail(values['correo_mensaje'], fields), 'mensaje': util.make_info_mail(values['correo_mensaje'], fields),
@ -3567,12 +3556,11 @@ class Facturas(BaseModel):
locales_retenciones = 0 locales_retenciones = 0
for product in products: for product in products:
# ~ print ('\n', product['descripcion']) id_product = product.pop('id_product')
id_product = product.pop('id')
id_student = product.pop('id_student', 0) id_student = product.pop('id_student', 0)
p = Productos.get(Productos.id==id_product) p = Productos.get(Productos.id==id_product)
product['unidad'] = p.unidad.key product['unidad'] = SATUnidades.get(SATUnidades.id==product['unidad']).key
product['clave'] = p.clave product['clave'] = p.clave
product['clave_sat'] = p.clave_sat product['clave_sat'] = p.clave_sat
product['cuenta_predial'] = p.cuenta_predial product['cuenta_predial'] = p.cuenta_predial
@ -3793,8 +3781,8 @@ class Facturas(BaseModel):
'ClaveProdServ': row.producto.clave_sat, 'ClaveProdServ': row.producto.clave_sat,
'NoIdentificacion': row.producto.clave, 'NoIdentificacion': row.producto.clave,
'Cantidad': FORMAT.format(row.cantidad), 'Cantidad': FORMAT.format(row.cantidad),
'ClaveUnidad': row.producto.unidad.key, 'ClaveUnidad': row.unidad,
'Unidad': row.producto.unidad.name[:20], 'Unidad': SATUnidades.get(SATUnidades.key==row.unidad).name[:20],
'Descripcion': row.descripcion, 'Descripcion': row.descripcion,
'ValorUnitario': FORMAT.format(row.valor_unitario), 'ValorUnitario': FORMAT.format(row.valor_unitario),
'Importe': FORMAT.format(row.importe), 'Importe': FORMAT.format(row.importe),
@ -4467,10 +4455,10 @@ class PreFacturas(BaseModel):
locales_retenciones = 0 locales_retenciones = 0
for product in products: for product in products:
id_product = product.pop('id') id_product = product.pop('id_product')
p = Productos.get(Productos.id==id_product) p = Productos.get(Productos.id==id_product)
product['unidad'] = p.unidad.key product['unidad'] = SATUnidades.get(SATUnidades.id==product['unidad']).key
product['clave'] = p.clave product['clave'] = p.clave
product['clave_sat'] = p.clave_sat product['clave_sat'] = p.clave_sat
product['cuenta_predial'] = p.cuenta_predial product['cuenta_predial'] = p.cuenta_predial
@ -4717,16 +4705,16 @@ class PreFacturasDetalle(BaseModel):
PreFacturasDetalle.factura==id) PreFacturasDetalle.factura==id)
for p in reversed(productos): for p in reversed(productos):
row = {'id': p.producto.id} row = {'id_product': p.producto.id}
row['clave'] = p.producto.clave row['clave'] = p.producto.clave
row['descripcion'] = p.descripcion row['descripcion'] = p.descripcion
row['unidad'] = p.producto.unidad.name row['unidad'] = p.producto.unidad.id
row['cantidad'] = p.cantidad row['cantidad'] = p.cantidad
row['valor_unitario'] = p.valor_unitario row['valor_unitario'] = p.valor_unitario
row['descuento'] = p.descuento row['descuento'] = p.descuento
pf = p.valor_unitario - p.descuento pf = p.valor_unitario - p.descuento
row['importe'] = round(pf * p.cantidad, DECIMALES) row['importe'] = round(pf * p.cantidad, DECIMALES)
impuestos = cls._get_impuestos(cls, row['id']) impuestos = cls._get_impuestos(cls, row['id_product'])
data.append({'row': row, 'taxes': impuestos}) data.append({'row': row, 'taxes': impuestos})
return {'rows': data, 'receptor': receptor} return {'rows': data, 'receptor': receptor}

View File

@ -63,6 +63,7 @@ var controllers = {
$$('chk_config_tax_locales').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_tax_locales').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_tax_decimals').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_tax_decimals').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_price_with_taxes_in_invoice').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_price_with_taxes_in_invoice').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_add_same_product').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_edu').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_edu').attachEvent('onItemClick', chk_config_item_click)

View File

@ -167,6 +167,7 @@ function default_config(){
cfg_invoice['tax_locales'] = values.cfdi_tax_locales cfg_invoice['tax_locales'] = values.cfdi_tax_locales
cfg_invoice['tax_decimals'] = values.cfdi_tax_decimals cfg_invoice['tax_decimals'] = values.cfdi_tax_decimals
cfg_invoice['with_taxes'] = values.cfdi_with_taxes cfg_invoice['with_taxes'] = values.cfdi_with_taxes
cfg_invoice['add_same_product'] = values.cfdi_add_same_product
if(values.cfdi_show_pedimento){ if(values.cfdi_show_pedimento){
$$('grid_details').showColumn('pedimento') $$('grid_details').showColumn('pedimento')
} }
@ -558,10 +559,11 @@ function guardar_y_timbrar(values){
var rows = grid.data.getRange() var rows = grid.data.getRange()
for (i = 0; i < rows.length; i++) { for (i = 0; i < rows.length; i++) {
delete rows[i]['id']
delete rows[i]['delete'] delete rows[i]['delete']
delete rows[i]['clave'] delete rows[i]['clave']
delete rows[i]['clave_sat'] delete rows[i]['clave_sat']
delete rows[i]['unidad'] //~ delete rows[i]['unidad']
delete rows[i]['importe'] delete rows[i]['importe']
delete rows[i]['student'] delete rows[i]['student']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario']) rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
@ -808,7 +810,7 @@ function calcular_impuestos(){
var import2 = (valor_unitario * cantidad).round(DECIMALES) var import2 = (valor_unitario * cantidad).round(DECIMALES)
var importe = parseFloat(product.importe) var importe = parseFloat(product.importe)
subtotal += importe subtotal += importe
query = table_pt.chain().find({'product': product.id}).data() query = table_pt.chain().find({'product': product.id_product}).data()
for(var tax of query){ for(var tax of query){
impuesto = table_taxes.findOne({'id': tax.tax}) impuesto = table_taxes.findOne({'id': tax.tax})
if(impuesto.tipo == 'E'){ if(impuesto.tipo == 'E'){
@ -867,7 +869,16 @@ function set_product(values){
var taxes = values.taxes var taxes = values.taxes
var values = values.row var values = values.row
var form = $$('form_invoice') var form = $$('form_invoice')
var row = grid.getItem(values.id) var row = undefined
if(!cfg_invoice['add_same_product']){
for(var id in grid.data.pull){
if(grid.getItem(id).id_product == values.id_product){
row = grid.getItem(id)
break
}
}
}
values['delete'] = '-' values['delete'] = '-'
if (row == undefined){ if (row == undefined){
@ -946,7 +957,7 @@ function search_product_id_key_press(code, e){
function grid_details_before_edit_start(id){ function grid_details_before_edit_start(id){
var columns = ['', 'descripcion', 'pedimento','cantidad', 'valor_unitario', 'descuento'] var columns = ['', 'unidad', 'descripcion', 'pedimento','cantidad', 'valor_unitario', 'descuento']
if(!columns.indexOf(id.column)){ if(!columns.indexOf(id.column)){
return !this.getItem(id.row)[id.column] return !this.getItem(id.row)[id.column]
} }
@ -956,6 +967,10 @@ function grid_details_before_edit_start(id){
function grid_details_before_edit_stop(state, editor){ function grid_details_before_edit_stop(state, editor){
var row = grid.getItem(editor.row) var row = grid.getItem(editor.row)
if(editor.column == 'unidad'){
return true
}
if(editor.column == 'descripcion'){ if(editor.column == 'descripcion'){
if(!state.value.trim()){ if(!state.value.trim()){
msg = 'La descripción no puede estar vacía' msg = 'La descripción no puede estar vacía'
@ -1358,9 +1373,9 @@ function cmd_prefactura_click(){
var rows = grid.data.getRange() var rows = grid.data.getRange()
for (i = 0; i < rows.length; i++) { for (i = 0; i < rows.length; i++) {
delete rows[i]['id']
delete rows[i]['delete'] delete rows[i]['delete']
delete rows[i]['clave'] delete rows[i]['clave']
delete rows[i]['unidad']
delete rows[i]['importe'] delete rows[i]['importe']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario']) rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
rows[i]['descuento'] = parseFloat(rows[i]['descuento']) rows[i]['descuento'] = parseFloat(rows[i]['descuento'])

View File

@ -599,6 +599,8 @@ var options_admin_otros = [
labelRight: 'Calcular impuestos con 4 decimales'}, labelRight: 'Calcular impuestos con 4 decimales'},
{view: 'checkbox', id: 'chk_config_price_with_taxes_in_invoice', labelWidth: 0, {view: 'checkbox', id: 'chk_config_price_with_taxes_in_invoice', labelWidth: 0,
labelRight: 'Precio incluye impuestos'}, labelRight: 'Precio incluye impuestos'},
{view: 'checkbox', id: 'chk_config_add_same_product', labelWidth: 0,
labelRight: 'Permitir agregar el mismo producto'},
]}, ]},
{maxHeight: 20}, {maxHeight: 20},
{template: 'Ayudas varias', type: 'section'}, {template: 'Ayudas varias', type: 'section'},

View File

@ -277,7 +277,7 @@ var grid_invoices = {
var grid_details_cols = [ var grid_details_cols = [
{id: "id", header:"ID", hidden: true}, {id: 'id_product', header: 'id_product', hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'}, {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'}, adjust: 'data'},
@ -287,7 +287,8 @@ var grid_details_cols = [
{id: "pedimento", header: 'Pedimento', editor: 'text', hidden: true}, {id: "pedimento", header: 'Pedimento', editor: 'text', hidden: true},
{id: "id_student", header: 'ID_Alumno', hidden: true}, {id: "id_student", header: 'ID_Alumno', hidden: true},
{id: 'student', header: 'Alumno', hidden: true, width: 150}, {id: 'student', header: 'Alumno', hidden: true, width: 150},
{id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100}, {id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100,
editor: 'select', options: 'values/unidades'},
{id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100, {id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100,
format: webix.i18n.numberFormat, css: 'right', editor: 'text'}, format: webix.i18n.numberFormat, css: 'right', editor: 'text'},
{id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'}, {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'},