Mostrar cantidad de empaques en PDF
This commit is contained in:
parent
5cdbb0aaa7
commit
f761ab7a5a
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -1,8 +1,28 @@
|
||||||
v 1.28.0 [03-feb-2019]
|
v 1.28.0 [04-feb-2019]
|
||||||
----------------------
|
----------------------
|
||||||
- Mejora: Manejo de empaques para mensajeria
|
- Mejora: Manejo de empaques para mensajeria
|
||||||
|
|
||||||
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar.
|
* IMPORTANTE:
|
||||||
|
Es necesario realizar una migración, despues de actualizar.
|
||||||
|
|
||||||
|
```
|
||||||
|
git pull origin master
|
||||||
|
|
||||||
|
cd source/app/models
|
||||||
|
|
||||||
|
python main.py -bk
|
||||||
|
|
||||||
|
python main.py -m
|
||||||
|
```
|
||||||
|
|
||||||
|
Es necesario agregar un nuevo requerimiento.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo pip install cryptography
|
||||||
|
```
|
||||||
|
|
||||||
|
Si envias tus facturas por correo directamente, es necesario volver a capturar
|
||||||
|
la contraseña de tu servidor de correo y guardar los datos nuevamente.
|
||||||
|
|
||||||
|
|
||||||
v 1.27.1 [23-ene-2019]
|
v 1.27.1 [23-ene-2019]
|
||||||
|
|
|
@ -13,3 +13,4 @@ pyqrcode
|
||||||
pypng
|
pypng
|
||||||
reportlab
|
reportlab
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
|
cryptography
|
||||||
|
|
|
@ -872,7 +872,7 @@ class LIBO(object):
|
||||||
currency = self.CELL_STYLE.get(self._currency, 'peso')
|
currency = self.CELL_STYLE.get(self._currency, 'peso')
|
||||||
return '{}{}'.format(currency, match.groups()[1])
|
return '{}{}'.format(currency, match.groups()[1])
|
||||||
|
|
||||||
def _conceptos(self, data):
|
def _conceptos(self, data, pakings):
|
||||||
first = True
|
first = True
|
||||||
col1 = []
|
col1 = []
|
||||||
col2 = []
|
col2 = []
|
||||||
|
@ -881,8 +881,9 @@ class LIBO(object):
|
||||||
col5 = []
|
col5 = []
|
||||||
col6 = []
|
col6 = []
|
||||||
col7 = []
|
col7 = []
|
||||||
|
col8 = []
|
||||||
count = len(data) - 1
|
count = len(data) - 1
|
||||||
for concepto in data:
|
for i, concepto in enumerate(data):
|
||||||
key = concepto.get('noidentificacion', '')
|
key = concepto.get('noidentificacion', '')
|
||||||
description = concepto['descripcion']
|
description = concepto['descripcion']
|
||||||
unidad = concepto['unidad']
|
unidad = concepto['unidad']
|
||||||
|
@ -899,6 +900,8 @@ class LIBO(object):
|
||||||
cell_5 = self._set_cell('{valorunitario}', valor_unitario, value=True)
|
cell_5 = self._set_cell('{valorunitario}', valor_unitario, value=True)
|
||||||
cell_6 = self._set_cell('{importe}', importe, value=True)
|
cell_6 = self._set_cell('{importe}', importe, value=True)
|
||||||
cell_7 = self._set_cell('{descuento}', descuento, value=True)
|
cell_7 = self._set_cell('{descuento}', descuento, value=True)
|
||||||
|
if pakings:
|
||||||
|
cell_8 = self._set_cell('{empaque}', pakings[i], value=True)
|
||||||
if len(data) > 1:
|
if len(data) > 1:
|
||||||
row = cell_1.getCellAddress().Row + 1
|
row = cell_1.getCellAddress().Row + 1
|
||||||
self._sheet.getRows().insertByIndex(row, count)
|
self._sheet.getRows().insertByIndex(row, count)
|
||||||
|
@ -912,6 +915,8 @@ class LIBO(object):
|
||||||
col5.append((float(valor_unitario),))
|
col5.append((float(valor_unitario),))
|
||||||
col6.append((float(importe),))
|
col6.append((float(importe),))
|
||||||
col7.append((float(descuento),))
|
col7.append((float(descuento),))
|
||||||
|
if pakings:
|
||||||
|
col8.append((pakings[i],))
|
||||||
self._total_cantidades += float(cantidad)
|
self._total_cantidades += float(cantidad)
|
||||||
if not count:
|
if not count:
|
||||||
return
|
return
|
||||||
|
@ -919,6 +924,9 @@ class LIBO(object):
|
||||||
style_5 = self._get_style(cell_5)
|
style_5 = self._get_style(cell_5)
|
||||||
style_6 = self._get_style(cell_6)
|
style_6 = self._get_style(cell_6)
|
||||||
style_7 = self._get_style(cell_7)
|
style_7 = self._get_style(cell_7)
|
||||||
|
style_8 = ''
|
||||||
|
if pakings:
|
||||||
|
style_8 = self._get_style(cell_8)
|
||||||
|
|
||||||
col = cell_1.getCellAddress().Column
|
col = cell_1.getCellAddress().Column
|
||||||
target1 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
target1 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
||||||
|
@ -933,9 +941,13 @@ class LIBO(object):
|
||||||
col = cell_6.getCellAddress().Column
|
col = cell_6.getCellAddress().Column
|
||||||
target6 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
target6 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
||||||
target7 = None
|
target7 = None
|
||||||
|
target8 = None
|
||||||
if not cell_7 is None:
|
if not cell_7 is None:
|
||||||
col = cell_7.getCellAddress().Column
|
col = cell_7.getCellAddress().Column
|
||||||
target7 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
target7 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
||||||
|
if pakings:
|
||||||
|
col = cell_8.getCellAddress().Column
|
||||||
|
target8 = self._sheet.getCellRangeByPosition(col, row+1, col, row+count)
|
||||||
|
|
||||||
target1.setFormulaArray(tuple(col1))
|
target1.setFormulaArray(tuple(col1))
|
||||||
target2.setDataArray(tuple(col2))
|
target2.setDataArray(tuple(col2))
|
||||||
|
@ -945,6 +957,8 @@ class LIBO(object):
|
||||||
target6.setDataArray(tuple(col6))
|
target6.setDataArray(tuple(col6))
|
||||||
if not target7 is None:
|
if not target7 is None:
|
||||||
target7.setDataArray(tuple(col7))
|
target7.setDataArray(tuple(col7))
|
||||||
|
if not target8 is None:
|
||||||
|
target8.setDataArray(tuple(col8))
|
||||||
|
|
||||||
if style_5:
|
if style_5:
|
||||||
cell_5.CellStyle = style_5
|
cell_5.CellStyle = style_5
|
||||||
|
@ -955,6 +969,9 @@ class LIBO(object):
|
||||||
if style_7:
|
if style_7:
|
||||||
cell_7.CellStyle = style_7
|
cell_7.CellStyle = style_7
|
||||||
target7.CellStyle = style_7
|
target7.CellStyle = style_7
|
||||||
|
if style_8:
|
||||||
|
cell_8.CellStyle = style_8
|
||||||
|
target8.CellStyle = style_8
|
||||||
return
|
return
|
||||||
|
|
||||||
def _add_totales(self, data):
|
def _add_totales(self, data):
|
||||||
|
@ -1275,10 +1292,12 @@ class LIBO(object):
|
||||||
self._currency = data['totales']['moneda']
|
self._currency = data['totales']['moneda']
|
||||||
self._pagos = data.pop('pagos', False)
|
self._pagos = data.pop('pagos', False)
|
||||||
|
|
||||||
|
pakings = data.pop('pakings', [])
|
||||||
|
|
||||||
self._comprobante(data['comprobante'])
|
self._comprobante(data['comprobante'])
|
||||||
self._emisor(data['emisor'])
|
self._emisor(data['emisor'])
|
||||||
self._receptor(data['receptor'])
|
self._receptor(data['receptor'])
|
||||||
self._conceptos(data['conceptos'])
|
self._conceptos(data['conceptos'], pakings)
|
||||||
|
|
||||||
if self._pagos:
|
if self._pagos:
|
||||||
self._cfdipays(data['pays'])
|
self._cfdipays(data['pays'])
|
||||||
|
@ -2105,7 +2124,7 @@ def get_data_from_xml(invoice, values):
|
||||||
data['pagos'] = values.get('pagos', False)
|
data['pagos'] = values.get('pagos', False)
|
||||||
if data['pagos']:
|
if data['pagos']:
|
||||||
data['pays'] = _cfdipays(doc, data, version)
|
data['pays'] = _cfdipays(doc, data, version)
|
||||||
|
data['pakings'] = values.get('pakings', [])
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,10 @@
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import math
|
import math
|
||||||
import os
|
|
||||||
|
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
||||||
|
|
||||||
|
|
||||||
def round_up(value):
|
def round_up(value):
|
||||||
|
@ -31,19 +29,18 @@ def round_up(value):
|
||||||
|
|
||||||
|
|
||||||
def _get_key(password):
|
def _get_key(password):
|
||||||
salt = os.urandom(16)
|
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
|
||||||
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt,
|
digest.update(password.encode())
|
||||||
iterations=100000, backend=default_backend())
|
key = base64.urlsafe_b64encode(digest.finalize())
|
||||||
key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
|
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
||||||
def encrypt(data, password)
|
def encrypt(data, password):
|
||||||
f = Fernet(_get_key(password))
|
f = Fernet(_get_key(password))
|
||||||
return f.encrypt(data.encode()).decode()
|
return f.encrypt(data.encode()).decode()
|
||||||
|
|
||||||
|
|
||||||
def decrypt(data, password)
|
def decrypt(data, password):
|
||||||
f = Fernet(_get_key(password))
|
f = Fernet(_get_key(password))
|
||||||
return f.decrypt(data.encode()).decode()
|
return f.decrypt(data.encode()).decode()
|
||||||
|
|
||||||
|
|
|
@ -351,6 +351,17 @@ class Configuracion(BaseModel):
|
||||||
msg = 'No se pudo guardar la configuración'
|
msg = 'No se pudo guardar la configuración'
|
||||||
return {'ok': result, 'msg': msg}
|
return {'ok': result, 'msg': msg}
|
||||||
|
|
||||||
|
def _save_mail(self, values):
|
||||||
|
rfc = Emisor.select()[0].rfc
|
||||||
|
values['correo_contra'] = utils.encrypt(values['correo_contra'], rfc)
|
||||||
|
|
||||||
|
for k, v in values.items():
|
||||||
|
obj, created = Configuracion.get_or_create(clave=k)
|
||||||
|
obj.valor = v
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
return {'ok': True}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_bool(cls, key):
|
def get_bool(cls, key):
|
||||||
data = (Configuracion
|
data = (Configuracion
|
||||||
|
@ -438,6 +449,17 @@ class Configuracion(BaseModel):
|
||||||
values = {r.clave: util.get_bool(r.valor) for r in data}
|
values = {r.clave: util.get_bool(r.valor) for r in data}
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
def _get_correo(self):
|
||||||
|
fields = ('correo_servidor', 'correo_puerto', 'correo_ssl',
|
||||||
|
'correo_usuario', 'correo_copia', 'correo_asunto',
|
||||||
|
'correo_mensaje', 'correo_directo', 'correo_confirmacion')
|
||||||
|
data = (Configuracion
|
||||||
|
.select()
|
||||||
|
.where(Configuracion.clave.in_(fields))
|
||||||
|
)
|
||||||
|
values = {r.clave: r.valor for r in data}
|
||||||
|
return values
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_(cls, keys):
|
def get_(cls, keys):
|
||||||
if isinstance(keys, str):
|
if isinstance(keys, str):
|
||||||
|
@ -450,9 +472,12 @@ class Configuracion(BaseModel):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
options = ('partners',
|
options = ('partners',
|
||||||
'admin_products', 'main_products',
|
'admin_products',
|
||||||
|
'main_products',
|
||||||
'complements',
|
'complements',
|
||||||
'folios')
|
'folios',
|
||||||
|
'correo',
|
||||||
|
)
|
||||||
opt = keys['fields']
|
opt = keys['fields']
|
||||||
if opt in options:
|
if opt in options:
|
||||||
return getattr(cls, '_get_{}'.format(opt))(cls)
|
return getattr(cls, '_get_{}'.format(opt))(cls)
|
||||||
|
@ -513,16 +538,7 @@ class Configuracion(BaseModel):
|
||||||
values[f] = Configuracion.get_(f)
|
values[f] = Configuracion.get_(f)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
if keys['fields'] == 'correo':
|
if keys['fields'] == 'path_cer':
|
||||||
fields = ('correo_servidor', 'correo_puerto', 'correo_ssl',
|
|
||||||
'correo_usuario', 'correo_contra', 'correo_copia',
|
|
||||||
'correo_asunto', 'correo_mensaje', 'correo_directo',
|
|
||||||
'correo_confirmacion')
|
|
||||||
data = (Configuracion
|
|
||||||
.select()
|
|
||||||
.where(Configuracion.clave.in_(fields))
|
|
||||||
)
|
|
||||||
elif keys['fields'] == 'path_cer':
|
|
||||||
fields = ('path_key', 'path_cer')
|
fields = ('path_key', 'path_cer')
|
||||||
data = (Configuracion
|
data = (Configuracion
|
||||||
.select()
|
.select()
|
||||||
|
@ -3720,6 +3736,8 @@ class Facturas(BaseModel):
|
||||||
return {'ok': True, 'msg': 'Notas guardadas correctamente'}
|
return {'ok': True, 'msg': 'Notas guardadas correctamente'}
|
||||||
|
|
||||||
def _get_not_in_xml(self, invoice, emisor):
|
def _get_not_in_xml(self, invoice, emisor):
|
||||||
|
pdf_from = Configuracion.get_('make_pdf_from') or '1'
|
||||||
|
|
||||||
values = {}
|
values = {}
|
||||||
|
|
||||||
values['notas'] = invoice.notas
|
values['notas'] = invoice.notas
|
||||||
|
@ -3757,6 +3775,16 @@ class Facturas(BaseModel):
|
||||||
for k, v in receptor.items():
|
for k, v in receptor.items():
|
||||||
values['receptor'][k] = v
|
values['receptor'][k] = v
|
||||||
|
|
||||||
|
use_packing = Configuracion.get_bool('chk_use_packing')
|
||||||
|
if use_packing:
|
||||||
|
w = FacturasDetalle.factura == invoice
|
||||||
|
q = (FacturasDetalle
|
||||||
|
.select(FacturasDetalle.empaques)
|
||||||
|
.where(w)
|
||||||
|
.order_by(FacturasDetalle.id.asc())
|
||||||
|
.tuples())
|
||||||
|
values['pakings'] = [str(int(r[0])) for r in q]
|
||||||
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -4005,6 +4033,7 @@ class Facturas(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def send(cls, id, rfc):
|
def send(cls, id, rfc):
|
||||||
values = Configuracion.get_({'fields': 'correo'})
|
values = Configuracion.get_({'fields': 'correo'})
|
||||||
|
contra = Configuracion.get_('correo_contra')
|
||||||
in_zip = Configuracion.get_bool('chk_config_send_zip')
|
in_zip = Configuracion.get_bool('chk_config_send_zip')
|
||||||
|
|
||||||
if not values:
|
if not values:
|
||||||
|
@ -4031,7 +4060,7 @@ class Facturas(BaseModel):
|
||||||
'puerto': values['correo_puerto'],
|
'puerto': values['correo_puerto'],
|
||||||
'ssl': bool(int(values['correo_ssl'])),
|
'ssl': bool(int(values['correo_ssl'])),
|
||||||
'usuario': values['correo_usuario'],
|
'usuario': values['correo_usuario'],
|
||||||
'contra': values['correo_contra'],
|
'contra': utils.decrypt(contra, rfc),
|
||||||
}
|
}
|
||||||
options = {
|
options = {
|
||||||
'para': obj.cliente.correo_facturas,
|
'para': obj.cliente.correo_facturas,
|
||||||
|
@ -8888,6 +8917,8 @@ def _migrate_tables(rfc=''):
|
||||||
with database_proxy.atomic() as txn:
|
with database_proxy.atomic() as txn:
|
||||||
migrate(*migrations)
|
migrate(*migrations)
|
||||||
|
|
||||||
|
Configuracion.add({'version': VERSION})
|
||||||
|
|
||||||
log.info('Tablas migradas correctamente...')
|
log.info('Tablas migradas correctamente...')
|
||||||
_importar_valores('', rfc)
|
_importar_valores('', rfc)
|
||||||
|
|
||||||
|
|
|
@ -817,6 +817,7 @@ function cmd_probar_correo_click(){
|
||||||
|
|
||||||
function save_config_mail(values){
|
function save_config_mail(values){
|
||||||
|
|
||||||
|
values['opt'] = 'save_mail'
|
||||||
webix.ajax().sync().post('/config', values, {
|
webix.ajax().sync().post('/config', values, {
|
||||||
error: function(text, data, xhr) {
|
error: function(text, data, xhr) {
|
||||||
msg = 'Error al guardar la configuración'
|
msg = 'Error al guardar la configuración'
|
||||||
|
|
Loading…
Reference in New Issue