This commit is contained in:
Mauricio Baeza 2018-10-10 22:25:10 -05:00
parent 4007923f84
commit 4eb4cc8fa5
10 changed files with 318 additions and 237 deletions

View File

@ -1,3 +1,11 @@
v 1.21.0 [11-oct-2018]
----------------------
- Error #287
- Mejora: Complemento de pago con datos de cuentas
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar.
v 1.20.0 [08-oct-2018]
----------------------
- Error #295

View File

@ -1 +1 @@
1.20.0
1.21.0

View File

@ -6,11 +6,18 @@ siempre actualizado.** Solo se da soporte sobre la ultima versión de **Empresa
Libre**.
### 1.21.0 [11-oct-2018]
- Error [#287](https://gitlab.com/mauriciobaeza/empresa-libre/issues/287)
- Mejora: Complemento de pago con datos de cuentas
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar.
### 1.20.0 [08-oct-2018]
- Error [#295](https://gitlab.com/mauriciobaeza/empresa-libre/issues/295)
- Mejora - Cuentas de banco para clientes
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal.
* IMPORTANTE: Es necesario realizar una migración, despues de actualizar.
### 1.19.1 [03-oct-2018]

View File

@ -552,12 +552,28 @@ class AppSATBancos(object):
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_satbancos(values)
req.context['result'] = self._db.get_sat_bancos(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.satbancos(values)
req.context['result'] = self._db.sat_bancos(values)
resp.status = falcon.HTTP_200
class AppSATFormaPago(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_sat_forma_pago(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.sat_forma_pago(values)
resp.status = falcon.HTTP_200

View File

@ -17,7 +17,8 @@ from controllers.main import (AppEmpresas,
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
AppMovimientosBanco, AppTickets, AppStudents, AppEmployees, AppNomina,
AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco
AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco,
AppSATFormaPago
)
@ -59,6 +60,7 @@ api.add_route('/nomina', AppNomina(db))
api.add_route('/invoicepay', AppInvoicePay(db))
api.add_route('/cfdipay', AppCfdiPay(db))
api.add_route('/satbancos', AppSATBancos(db))
api.add_route('/satformapago', AppSATFormaPago(db))
api.add_route('/socioscb', AppSociosCuentasBanco(db))

View File

@ -436,9 +436,12 @@ class StorageEngine(object):
def get_cfdipay(self, values):
return main.CfdiPagos.get_values(values)
def get_satbancos(self, values):
def get_sat_bancos(self, values):
return main.SATBancos.get_values(values)
def get_sat_forma_pago(self, values):
return main.SATFormaPago.get_values(values)
def get_partners_accounts_bank(self, values):
return main.SociosCuentasBanco.get_values(values)
@ -448,8 +451,11 @@ class StorageEngine(object):
def bankmovement(self, values):
return main.MovimientosBanco.post(values)
def satbancos(self, values):
def sat_bancos(self, values):
return main.SATBancos.post(values)
def sat_forma_pago(self, values):
return main.SATFormaPago.post(values)
def partners_accounts_bank(self, values):
return main.SociosCuentasBanco.post(values)

View File

@ -1244,6 +1244,31 @@ class SATFormaPago(BaseModel):
def __str__(self):
return 'Forma de pago: ({}) {}'.format(self.key, self.name)
@classmethod
def get_values(cls, values):
opt = values.pop('opt')
return getattr(cls, '_get_{}'.format(opt))(cls, values)
def _get_active_by_id(self, values):
rows = (SATFormaPago
.select(
SATFormaPago.id,
SATFormaPago.name.alias('value'))
.where(SATFormaPago.activo==True)
.dicts()
)
return tuple(rows)
def _get_active_by_key(self, values):
rows = (SATFormaPago
.select(
SATFormaPago.key.alias('id'),
SATFormaPago.name.alias('value'))
.where(SATFormaPago.activo==True)
.dicts()
)
return tuple(rows)
@classmethod
def get_(self):
rows = SATFormaPago.select().dicts()
@ -1878,218 +1903,6 @@ class CuentasBanco(BaseModel):
return data
class MovimientosBanco(BaseModel):
cuenta = ForeignKeyField(CuentasBanco)
fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S'])
descripcion = TextField(default='')
forma_pago = ForeignKeyField(SATFormaPago)
retiro = DecimalField(default=0.0, max_digits=20, decimal_places=6,
auto_round=True)
deposito = DecimalField(default=0.0, max_digits=20, decimal_places=6,
auto_round=True)
saldo = DecimalField(default=0.0, max_digits=20, decimal_places=6,
auto_round=True)
cancelado = BooleanField(default=False)
conciliado = BooleanField(default=False)
moneda = TextField(default='MXN') # Complemento de pagos
tipo_cambio = DecimalField(default=1.0, max_digits=15, decimal_places=6,
auto_round=True)
numero_operacion = TextField(default='')
origen_rfc = TextField(default='')
origen_nombre = TextField(default='')
origen_cuenta = TextField(default='')
destino_rfc = TextField(default='')
destino_cuenta = TextField(default='')
tipo_cadena_pago = TextField(default='')
certificado_pago = TextField(default='')
cadena_pago = TextField(default='')
sello_pago = TextField(default='')
class Meta:
order_by = ('fecha', 'id')
def _ultimo_saldo(self, cuenta, fecha):
query = (MovimientosBanco
.select()
.where(
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.fecha<=fecha) &
(MovimientosBanco.cancelado==False))[-1]
)
return round(float(query.saldo), DECIMALES)
def _movimiento_anterior(self, cuenta, fecha):
query = (MovimientosBanco
.select()
.where(
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.fecha<fecha) &
(MovimientosBanco.cancelado==False))[-1]
)
return query
def _actualizar_saldos(self, row):
query = (MovimientosBanco
.select()
.where(
(MovimientosBanco.cuenta==row.cuenta) &
(MovimientosBanco.fecha>row.fecha) &
(MovimientosBanco.cancelado==False))
)
saldo = round(Decimal(row.saldo), DECIMALES)
for mov in query:
mov.saldo = saldo + mov.deposito - mov.retiro
mov.save()
saldo = mov.saldo
CuentasBanco.actualizar_saldo(row.cuenta, saldo)
return saldo
@classmethod
def post(cls, values):
opt = values.pop('opt')
return getattr(cls, '_{}'.format(opt))(cls, values)
def _add(self, values):
ids = values.pop('ids', '')
if ids and not Facturas.validate_count_partners(util.loads(ids)):
msg = 'Facturas relacionadas a diferentes clientes'
data = {'ok': False, 'msg': msg}
return data
actualizar = False
if 'saldo' in values:
saldo = values['saldo']
else:
actualizar = True
hora = values.pop('hora')
account = CuentasBanco.get(CuentasBanco.id==int(values['cuenta']))
values['fecha'] = '{}T{}'.format(values['fecha'][:10], hora)
values['cuenta'] = account
values['moneda'] = account.moneda.key
values['retiro'] = util.get_float(values['retiro'])
values['deposito'] = util.get_float(values['deposito'])
values['tipo_cambio'] = util.get_float(
values.get('tipo_cambio', 1.00), True)
values['forma_pago'] = int(values['forma_pago'])
ultimo_saldo = self._ultimo_saldo(
self, values['cuenta'], values['fecha'])
values['saldo'] = \
ultimo_saldo - values['retiro'] + values['deposito']
with database_proxy.transaction():
try:
obj = MovimientosBanco.create(**values)
except IntegrityError:
msg = 'Este movimiento ya existe'
return {'ok': False, 'msg': msg}
if actualizar:
saldo = self._actualizar_saldos(self, obj)
if ids:
FacturasPagos.add(obj, util.loads(ids))
return {'ok': True, 'saldo': saldo}
def _cancel(self, values):
id = int(values['id'])
try:
obj = MovimientosBanco.get(MovimientosBanco.id==id)
except MovimientosBanco.DoesNotExist:
msg = 'No se encontró el movimiento'
return {'ok': False, 'msg': msg}
if obj.cancelado:
msg = 'El movimiento ya esta cancelado'
return {'ok': False, 'msg': msg}
if obj.conciliado:
msg = 'El movimiento esta conciliado, no se puede cancelar'
return {'ok': False, 'msg': msg}
# ~ filters = (CfdiPagos.movimiento==obj)
# ~ cp = CfdiPagos.select().where(filters).count()
# ~ if cp > 0:
# ~ msg = 'El movimiento tiene Factura de Pago, no se puede cancelar'
# ~ return {'ok': False, 'msg': msg}
with database_proxy.transaction():
obj.cancelado = True
obj.save()
FacturasPagos.cancelar(obj)
obj = self._movimiento_anterior(self, obj.cuenta, obj.fecha)
self._actualizar_saldos(self, obj)
balance = CuentasBanco.get_saldo(obj.cuenta.id)
msg = 'Movimiento cancelado correctamente'
return {'ok': True, 'msg': msg, 'balance': balance}
@classmethod
def con(cls, id):
cant = (MovimientosBanco
.select(MovimientosBanco.id)
.where(MovimientosBanco.cuenta==id)
.count()
)
if cant > 2:
return {'ok': True}
return {'ok': False}
@classmethod
def get_(cls, values):
cuenta = int(values['cuenta'])
if 'fechas' in values:
rango = util.loads(values['fechas'])
fd = (MovimientosBanco.fecha.between(
util.get_date(rango['start']),
util.get_date(rango['end'], True)))
filtros = (fd &
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.cancelado==False)
)
else:
year = int(values['year'])
mes = int(values['mes'])
if year == -1:
fy = (MovimientosBanco.fecha.year > 0)
else:
fy = (MovimientosBanco.fecha.year == year)
if mes == -1:
fm = (MovimientosBanco.fecha.month > 0)
else:
fm = (MovimientosBanco.fecha.month == mes)
filtros = (fy & fm &
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.cancelado==False)
)
rows = tuple(MovimientosBanco.select(
MovimientosBanco.id,
MovimientosBanco.fecha,
MovimientosBanco.numero_operacion,
SATFormaPago.name.alias('way_payment'),
MovimientosBanco.descripcion,
MovimientosBanco.retiro,
MovimientosBanco.deposito,
MovimientosBanco.saldo,
fn.COUNT(CfdiPagos.id).alias('invoice')
)
.join(SATFormaPago).switch(MovimientosBanco)
.join(CfdiPagos, JOIN.LEFT_OUTER).switch(MovimientosBanco)
.where(filtros)
.group_by(MovimientosBanco.id, SATFormaPago.name)
.dicts()
)
return {'ok': True, 'rows': rows}
class SATUsoCfdi(BaseModel):
key = TextField(index=True, unique=True)
name = TextField(default='', index=True)
@ -2853,6 +2666,215 @@ class ContactoCorreos(BaseModel):
)
class MovimientosBanco(BaseModel):
cuenta = ForeignKeyField(CuentasBanco)
fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S'])
descripcion = TextField(default='')
forma_pago = ForeignKeyField(SATFormaPago)
retiro = DecimalField(default=0.0, max_digits=20, decimal_places=6,
auto_round=True)
deposito = DecimalField(default=0.0, max_digits=20, decimal_places=6,
auto_round=True)
saldo = DecimalField(default=0.0, max_digits=20, decimal_places=6,
auto_round=True)
cancelado = BooleanField(default=False)
conciliado = BooleanField(default=False)
moneda = TextField(default='MXN') # Complemento de pagos
tipo_cambio = DecimalField(default=1.0, max_digits=15, decimal_places=6,
auto_round=True)
numero_operacion = TextField(default='')
cuenta_socio = ForeignKeyField(SociosCuentasBanco, null=True)
tipo_cadena_pago = TextField(default='')
certificado_pago = TextField(default='')
cadena_pago = TextField(default='')
sello_pago = TextField(default='')
class Meta:
order_by = ('fecha', 'id')
def _ultimo_saldo(self, cuenta, fecha):
query = (MovimientosBanco
.select()
.where(
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.fecha<=fecha) &
(MovimientosBanco.cancelado==False))[-1]
)
return round(float(query.saldo), DECIMALES)
def _movimiento_anterior(self, cuenta, fecha):
query = (MovimientosBanco
.select()
.where(
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.fecha<fecha) &
(MovimientosBanco.cancelado==False))[-1]
)
return query
def _actualizar_saldos(self, row):
query = (MovimientosBanco
.select()
.where(
(MovimientosBanco.cuenta==row.cuenta) &
(MovimientosBanco.fecha>row.fecha) &
(MovimientosBanco.cancelado==False))
)
saldo = round(Decimal(row.saldo), DECIMALES)
for mov in query:
mov.saldo = saldo + mov.deposito - mov.retiro
mov.save()
saldo = mov.saldo
CuentasBanco.actualizar_saldo(row.cuenta, saldo)
return saldo
@classmethod
def post(cls, values):
opt = values.pop('opt')
return getattr(cls, '_{}'.format(opt))(cls, values)
def _add(self, values):
ids = values.pop('ids', '')
if ids and not Facturas.validate_count_partners(util.loads(ids)):
msg = 'Facturas relacionadas a diferentes clientes'
data = {'ok': False, 'msg': msg}
return data
actualizar = False
if 'saldo' in values:
saldo = values['saldo']
else:
actualizar = True
hora = values.pop('hora')
account = CuentasBanco.get(CuentasBanco.id==int(values['cuenta']))
values['fecha'] = '{}T{}'.format(values['fecha'][:10], hora)
values['cuenta'] = account
values['moneda'] = account.moneda.key
values['retiro'] = util.get_float(values['retiro'])
values['deposito'] = util.get_float(values['deposito'])
values['tipo_cambio'] = util.get_float(
values.get('tipo_cambio', 1.00), True)
values['forma_pago'] = int(values['forma_pago'])
ultimo_saldo = self._ultimo_saldo(
self, values['cuenta'], values['fecha'])
values['saldo'] = \
ultimo_saldo - values['retiro'] + values['deposito']
with database_proxy.transaction():
try:
obj = MovimientosBanco.create(**values)
except IntegrityError:
msg = 'Error al agregar el movimiento'
return {'ok': False, 'msg': msg}
if actualizar:
saldo = self._actualizar_saldos(self, obj)
if ids:
FacturasPagos.add(obj, util.loads(ids))
return {'ok': True, 'saldo': saldo}
def _cancel(self, values):
id = int(values['id'])
try:
obj = MovimientosBanco.get(MovimientosBanco.id==id)
except MovimientosBanco.DoesNotExist:
msg = 'No se encontró el movimiento'
return {'ok': False, 'msg': msg}
if obj.cancelado:
msg = 'El movimiento ya esta cancelado'
return {'ok': False, 'msg': msg}
if obj.conciliado:
msg = 'El movimiento esta conciliado, no se puede cancelar'
return {'ok': False, 'msg': msg}
# ~ filters = (CfdiPagos.movimiento==obj)
# ~ cp = CfdiPagos.select().where(filters).count()
# ~ if cp > 0:
# ~ msg = 'El movimiento tiene Factura de Pago, no se puede cancelar'
# ~ return {'ok': False, 'msg': msg}
with database_proxy.transaction():
obj.cancelado = True
obj.save()
FacturasPagos.cancelar(obj)
obj = self._movimiento_anterior(self, obj.cuenta, obj.fecha)
self._actualizar_saldos(self, obj)
balance = CuentasBanco.get_saldo(obj.cuenta.id)
msg = 'Movimiento cancelado correctamente'
return {'ok': True, 'msg': msg, 'balance': balance}
@classmethod
def con(cls, id):
cant = (MovimientosBanco
.select(MovimientosBanco.id)
.where(MovimientosBanco.cuenta==id)
.count()
)
if cant > 2:
return {'ok': True}
return {'ok': False}
@classmethod
def get_(cls, values):
cuenta = int(values['cuenta'])
if 'fechas' in values:
rango = util.loads(values['fechas'])
fd = (MovimientosBanco.fecha.between(
util.get_date(rango['start']),
util.get_date(rango['end'], True)))
filtros = (fd &
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.cancelado==False)
)
else:
year = int(values['year'])
mes = int(values['mes'])
if year == -1:
fy = (MovimientosBanco.fecha.year > 0)
else:
fy = (MovimientosBanco.fecha.year == year)
if mes == -1:
fm = (MovimientosBanco.fecha.month > 0)
else:
fm = (MovimientosBanco.fecha.month == mes)
filtros = (fy & fm &
(MovimientosBanco.cuenta==cuenta) &
(MovimientosBanco.cancelado==False)
)
rows = tuple(MovimientosBanco.select(
MovimientosBanco.id,
MovimientosBanco.fecha,
MovimientosBanco.numero_operacion,
SATFormaPago.name.alias('way_payment'),
MovimientosBanco.descripcion,
MovimientosBanco.retiro,
MovimientosBanco.deposito,
MovimientosBanco.saldo,
fn.COUNT(CfdiPagos.id).alias('invoice')
)
.join(SATFormaPago).switch(MovimientosBanco)
.join(CfdiPagos, JOIN.LEFT_OUTER).switch(MovimientosBanco)
.where(filtros)
.group_by(MovimientosBanco.id, SATFormaPago.name)
.dicts()
)
return {'ok': True, 'rows': rows}
class Alumnos(BaseModel):
rfc = TextField(null=True)
curp = TextField(index=True, unique=True)
@ -8392,6 +8414,17 @@ def _migrate_tables(rfc=''):
fecha_cancelacion = DateTimeField(null=True)
migrations.append(migrator.add_column('cfdipagos', 'fecha_cancelacion', fecha_cancelacion))
table = 'movimientosbanco'
columns = [c.name for c in database_proxy.get_columns(table)]
if not 'cuenta_socio' in columns:
cuenta_socio = ForeignKeyField(SociosCuentasBanco, null=True, to_field=SociosCuentasBanco.id)
migrations.append(migrator.add_column(table, 'socioscuentasbanco_id', cuenta_socio))
migrations.append(migrator.drop_column(table, 'origen_rfc'))
migrations.append(migrator.drop_column(table, 'origen_nombre'))
migrations.append(migrator.drop_column(table, 'origen_cuenta'))
migrations.append(migrator.drop_column(table, 'destino_rfc'))
migrations.append(migrator.drop_column(table, 'destino_cuenta'))
if migrations:
with database_proxy.atomic() as txn:
migrate(*migrations)

View File

@ -47,7 +47,7 @@ except ImportError:
DEBUG = DEBUG
VERSION = '1.20.0'
VERSION = '1.21.0'
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)

View File

@ -206,18 +206,18 @@ function lst_cuentas_banco_change(nv, ov){
}
function get_bancos_forma_pago(retiro){
var values = table_waypayment.chain().find({'id': { '$ne' : '99' }}).data()
if(retiro){
lst = $$('lst_retiro_forma_pago')
}else{
lst = $$('lst_deposito_forma_pago')
}
lst.getList().parse(values)
if(current_way_payment){
lst.setValue(current_way_payment)
}
}
//~ function get_bancos_forma_pago(retiro){
//~ var values = table_waypayment.chain().find({'id': { '$ne' : '99' }}).data()
//~ if(retiro){
//~ lst = $$('lst_retiro_forma_pago')
//~ }else{
//~ lst = $$('lst_deposito_forma_pago')
//~ }
//~ lst.getList().parse(values)
//~ if(current_way_payment){
//~ lst.setValue(current_way_payment)
//~ }
//~ }
function get_facturas_por_pagar(){
@ -246,7 +246,8 @@ function get_facturas_por_pagar(){
function cmd_agregar_retiro_click(){
get_bancos_forma_pago(true)
//~ get_bancos_forma_pago(true)
set_way_payment('lst_retiro_forma_pago', true, current_way_payment)
var title = 'Agregar retiro de banco a la cuenta ' + $$('lst_cuentas_banco').getText() + ' en ' + $$('txt_cuenta_moneda').getValue()
$$('title_bank_retiro').setValue(title)
$$('multi_bancos').setValue('banco_retiro')
@ -255,8 +256,9 @@ function cmd_agregar_retiro_click(){
function cmd_agregar_deposito_click(){
msg_importe = ''
get_bancos_forma_pago(false)
//~ get_bancos_forma_pago(false)
get_facturas_por_pagar()
set_way_payment('lst_deposito_forma_pago', true, current_way_payment)
var g = $$('grid_cfdi_este_deposito')
g.config.columns[g.getColumnIndex('importe')].header = 'Este Pago ' + current_currency

View File

@ -502,7 +502,7 @@ function get_forma_pago(control){
function get_way_payment(){
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
webix.ajax().get('/satformapago', {opt: 'active_by_id'}, function(text, data){
var values = data.json()
table_waypayment.clear()
table_waypayment.insert(values)
@ -510,9 +510,16 @@ function get_way_payment(){
}
function set_way_payment(control){
var values = table_waypayment.chain().data()
function set_way_payment(control, filter99=false, current_way_payment=''){
if(filter99){
var values = table_waypayment.chain().find({'value': { '$ne' : 'Por definir' }}).data()
}else{
var values = table_waypayment.chain().data()
}
$$(control).getList().parse(values)
if(current_way_payment){
$$(control).setValue(current_way_payment)
}
}