diff --git a/source/app/models/main.py b/source/app/models/main.py index 60ef522..869875c 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -250,6 +250,7 @@ def config_main(): data['empresa'] = titulo.format(data['empresa'], obj.nombre) data['escuela'] = obj.es_escuela data['timbres'] = Emisor.get_timbres() + data['multi_currency'] = SATMonedas.get_multi_currency() return data @@ -1312,6 +1313,14 @@ class SATMonedas(BaseModel): def __str__(self): return 'Moneda: ({}) {}'.format(self.key, self.name) + @classmethod + def get_multi_currency(cls): + count_currencies = len(cls.get_activos()) + multi_currency = False + if count_currencies > 1: + multi_currency = True + return multi_currency + @classmethod def add(self, values): try: @@ -1733,7 +1742,8 @@ class CuentasBanco(BaseModel): data = { 'ok': True, 'rows': tuple(rows), - 'moneda': first.moneda.name, + 'moneda': '{} ({})'.format(first.moneda.name, first.moneda.key), + 'key_currency': first.moneda.key, 'saldo': first.saldo, } return data @@ -1743,7 +1753,12 @@ class CuentasBanco(BaseModel): def _get_currency(self, values): id = int(values['id']) account = CuentasBanco.get(CuentasBanco.id==id) - return {'ok': True, 'currency': account.moneda.name} + data = { + 'ok': True, + 'currency': '{} ({})'.format(account.moneda.name, account.moneda.key), + 'key_currency': account.moneda.key, + } + return data @classmethod def emisor(cls): @@ -3645,6 +3660,8 @@ class Facturas(BaseModel): Facturas.estatus, Socios.nombre.alias('cliente'), Facturas.total, + Facturas.moneda.alias('currency'), + Facturas.total_mn, Facturas.saldo, ) .where(filtros) @@ -3749,14 +3766,22 @@ class Facturas(BaseModel): fm = (Facturas.fecha.month == int(values['month'])) filters = (fy & fm) - rows = tuple(Facturas - .select(Facturas.id, Facturas.serie, Facturas.folio, Facturas.uuid, - Facturas.fecha, Facturas.tipo_comprobante, Facturas.estatus, + rows = tuple(Facturas.select( + Facturas.id, + Facturas.serie, + Facturas.folio, + Facturas.uuid, + Facturas.fecha, + Facturas.tipo_comprobante, + Facturas.estatus, case(Facturas.pagada, ( (True, 'Si'), (False, 'No'), )).alias('paid'), - Facturas.total_mn, Socios.nombre.alias('cliente')) + Facturas.total, + Facturas.moneda.alias('currency'), + Facturas.total_mn, + Socios.nombre.alias('cliente')) .where(filters) .join(Socios) .switch(Facturas).dicts() @@ -5231,6 +5256,8 @@ class FacturasPagos(BaseModel): auto_round=True) saldo = DecimalField(default=0.0, max_digits=18, decimal_places=6, auto_round=True) + tipo_cambio = DecimalField(default=1.0, max_digits=15, decimal_places=6, + auto_round=True) class Meta: order_by = ('factura',) @@ -5294,8 +5321,11 @@ class FacturasPagos(BaseModel): @classmethod def add(cls, mov, ids): - for i, importe in ids.items(): + for i, values in ids.items(): fac = Facturas.get(Facturas.id==int(i)) + this_pay = values['this_pay'] + importe = values['importe'] + type_change = values['type_change'] mov_ant, numero = cls._movimiento_anterior(cls, mov, fac) nuevo = { 'movimiento': mov, @@ -5307,12 +5337,20 @@ class FacturasPagos(BaseModel): nuevo['saldo_anterior'] = float(fac.saldo) else: nuevo['saldo_anterior'] = float(mov_ant.saldo) - nuevo['saldo'] = nuevo['saldo_anterior'] - importe + + if(fac.moneda=='MXN'): + nuevo['saldo'] = nuevo['saldo_anterior'] - importe + else: + nuevo['importe'] = this_pay + nuevo['tipo_cambio'] = type_change + nuevo['saldo'] = nuevo['saldo_anterior'] - this_pay FacturasPagos.create(**nuevo) + fac.saldo = nuevo['saldo'] if nuevo['saldo'] == 0: fac.pagada = True fac.save() + cls._actualizar_saldo_cliente(cls, fac.cliente, importe * -1) return @@ -5549,7 +5587,7 @@ class CfdiPagos(BaseModel): Facturas.serie.alias('Serie'), Facturas.folio.alias('Folio'), Facturas.moneda.alias('MonedaDR'), - # ~ Facturas.tipo_cambio.alias('TipoCambioDR'), + Facturas.tipo_cambio.alias('TipoCambioDR'), # ~ Facturas.metodo_pago.alias('MetodoDePagoDR'), FacturasPagos.numero.alias('NumParcialidad'), FacturasPagos.saldo_anterior.alias('ImpSaldoAnt'), @@ -5564,7 +5602,7 @@ class CfdiPagos(BaseModel): r['IdDocumento'] = str(r['IdDocumento']) r['Folio'] = str(r['Folio']) r['NumParcialidad'] = str(r['NumParcialidad']) - # ~ r['TipoCambioDR'] = FORMAT.format(r['TipoCambioDR']) + r['TipoCambioDR'] = FORMAT.format(r['TipoCambioDR']) r['MetodoDePagoDR'] = DEFAULT_CFDIPAY['WAYPAY'] r['ImpSaldoAnt'] = FORMAT.format(r['ImpSaldoAnt']) r['ImpPagado'] = FORMAT.format(r['ImpPagado']) @@ -5572,6 +5610,10 @@ class CfdiPagos(BaseModel): r['ImpSaldoInsoluto'] = '0.00' else: r['ImpSaldoInsoluto'] = FORMAT.format(r['ImpSaldoInsoluto']) + if currency == r['MonedaDR']: + del r['TipoCambioDR'] + if not r['Serie']: + del r['Serie'] return related @@ -8109,6 +8151,10 @@ def _migrate_tables(rfc=''): columns = [c.name for c in database_proxy.get_columns('facturaspagos')] if 'cancelado' in columns: migrations.append(migrator.drop_column('facturaspagos', 'cancelado')) + if not 'tipo_cambio' in columns: + tipo_cambio = DecimalField(default=1.0, max_digits=15, decimal_places=6, + auto_round=True) + migrations.append(migrator.add_column('facturaspagos', 'tipo_cambio', tipo_cambio)) columns = [c.name for c in database_proxy.get_columns('certificado')] if not 'es_fiel' in columns: diff --git a/source/static/js/controller/bancos.js b/source/static/js/controller/bancos.js index 5d0aeaa..2ca67da 100644 --- a/source/static/js/controller/bancos.js +++ b/source/static/js/controller/bancos.js @@ -17,6 +17,27 @@ var msg = '' var msg_importe = '' +//~ var is_mn = true +var current_currency = '' +var current_way_payment = '' + + +function init_config_bank(){ + var multi_currency = get_config('multi_currency') + var g1 = $$('grid_cfdi_por_pagar') + var g2 = $$('grid_cfdi_este_deposito') + + if(multi_currency){ + g1.showColumn('total') + g1.showColumn('currency') + g2.showColumn('total') + g2.showColumn('currency') + g2.showColumn('this_pay') + g2.showColumn('type_change') + } + show('cmd_complemento_pago', get_config('used_cfdi_pays')) + set_year_month() +} var bancos_controllers = { @@ -46,8 +67,7 @@ var bancos_controllers = { $$('grid_cfdi_por_pagar').attachEvent('onItemDblClick', grid_cfdi_por_pagar_double_click) $$('grid_cfdi_este_deposito').attachEvent('onItemDblClick', grid_cfdi_este_deposito_double_click) - set_year_month() - show('cmd_complemento_pago', get_config('used_cfdi_pays')) + init_config_bank() } } @@ -94,6 +114,8 @@ function get_cuentas_banco(){ $$('txt_cuenta_moneda').setValue(values.moneda) $$('txt_cuenta_saldo').setValue(values.saldo) get_estado_cuenta() + current_currency = values.key_currency + //~ set_is_mn() }else{ enable('cmd_agregar_retiro', false) enable('cmd_agregar_deposito', false) @@ -160,6 +182,7 @@ function get_account_currency(){ var values = data.json() if(values.ok){ $$('txt_cuenta_moneda').setValue(values.currency) + current_currency = values.key_currency } } }) @@ -174,14 +197,16 @@ function lst_cuentas_banco_change(nv, ov){ function get_bancos_forma_pago(retiro){ - webix.ajax().get('/values/formapago', {}, function(text, data){ - var values = data.json() - if(retiro){ - $$('lst_retiro_forma_pago').getList().parse(values) - }else{ - $$('lst_deposito_forma_pago').getList().parse(values) - } - }) + 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) + } } @@ -220,6 +245,9 @@ function cmd_agregar_deposito_click(){ msg_importe = '' get_bancos_forma_pago(false) get_facturas_por_pagar() + var g = $$('grid_cfdi_este_deposito') + g.config.columns[g.getColumnIndex('importe')].header = 'Este Pago ' + current_currency + g.refreshColumns() $$('multi_bancos').setValue('banco_deposito') } @@ -358,19 +386,36 @@ function txt_deposito_importe_change(new_value, old_value){ } } +function get_type_change(){ + +} + function actualizar_deposito(grid){ grid.sort("#fecha#", "desc", "date") var suma = 0 var descripcion = '' + grid.data.each(function(obj){ descripcion += 'Pago de la factura: ' + obj.serie + obj.folio + ' del ' descripcion += 'cliente: ' + obj.cliente + '\n' if(obj.importe == undefined){ + //~ if(is_mn){ + //~ obj.importe = obj.total_mn + //~ }else{ obj.importe = obj.saldo + obj.this_pay = obj.saldo } suma += obj.importe.to_float() + if(obj.type_change == undefined){ + if(obj.currency==CURRENCY_MN){ + obj.type_change = 1.00 + } + //~ else{ + //~ obj.type_change = get_type_change(obj.currency) + //~ } + } }) $$('txt_deposito_importe').setValue(suma.round(DECIMALES)) @@ -407,6 +452,41 @@ function grid_cfdi_este_deposito_before_edit_stop(state, editor){ var row = grid.getItem(editor.row) if(editor.column == 'importe'){ + var importe = parseFloat(state.value) + if(isNaN(importe)){ + msg = 'El importe a pagar debe ser un número' + msg_error(msg) + grid.blockEvent() + state.value = state.old + grid.editCancel() + grid.unblockEvent() + return true + } + if(importe <= 0){ + msg = 'El importe a pagar debe ser mayor a cero' + msg_error(msg) + grid.blockEvent() + state.value = state.old + grid.editCancel() + grid.unblockEvent() + return true + } + var saldo = row['saldo'].to_float() + if(row['currency']==CURRENCY_MN && importe > saldo){ + msg = 'El importe a pagar no puede ser mayor al saldo de la factura' + msg_error(msg) + grid.blockEvent() + state.value = state.old + grid.editCancel() + grid.unblockEvent() + return true + } + + var this_pay = row['this_pay'].to_float() + row['type_change'] = (importe / this_pay).round(DECIMALES_TAX) + } + + if(editor.column == 'this_pay'){ var importe = parseFloat(state.value) if(isNaN(importe)){ msg = 'El importe a pagar debe ser un número' @@ -437,12 +517,16 @@ function grid_cfdi_este_deposito_before_edit_stop(state, editor){ return true } } + + + } function validate_deposito(values){ var grid = $$('grid_cfdi_este_deposito') var importe = values.deposito_importe.to_float() + var msg_tc = '' if(!importe){ msg = 'El importe es requerido' @@ -503,14 +587,31 @@ function validate_deposito(values){ return false } suma += tmp + + if(obj.currency!='MXN'){ + if(obj.type_change==undefined){ + msg_tc = 'Captura el Tipo de Cambio' + }else{ + var tc = obj.type_change + if(tc <= 1.0){ + msg_tc = 'El Tipo de Cambio debe ser mayor a 1.00' + } + } + } }) suma = suma.round(DECIMALES) + if(msg_tc){ + msg_error(msg_tc) + return false + } + if(suma > importe){ msg = 'La suma del pago de facturas, no puede ser mayor al deposito' msg_error(msg) return false } + if(suma < importe){ msg_importe = 'El importe del depósito en mayor a la suma de facturas. ' msg_importe += 'Asegurate de que esto sea correcto' @@ -536,12 +637,20 @@ function guardar_deposito(values){ data['retiro'] = 0.0 data['descripcion'] = values.deposito_descripcion + current_way_payment = data['forma_pago'] + if(grid.count()){ var ids = new Object() grid.data.each(function(obj){ - ids[obj.id] = obj.importe.to_float() + //~ ids[obj.id] = obj.importe.to_float() + ids[obj.id] = { + 'this_pay': obj.this_pay.to_float(), + 'importe': obj.importe.to_float(), + 'type_change': obj.type_change, + } }) data['ids'] = ids + //~ showvar(data['ids']) } webix.ajax().post('/movbanco', data, { @@ -1054,3 +1163,11 @@ function grid_cfdi_este_deposito_double_click(id, e, node){ this.move(id.row, -1, $$('grid_cfdi_por_pagar')) actualizar_deposito(grid) } + + +//~ function set_is_mn(){ + //~ var c = $$('txt_cuenta_moneda').getValue() + //~ if(c!=CURRENCY_MN){ + //~ is_mn = false + //~ } +//~ } diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 413f6a6..fac40e6 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -1,3 +1,19 @@ +//~ Empresa Libre +//~ Copyright (C) 2016-2018 Mauricio Baeza Servin (web@correolibre.net) +//~ +//~ This program is free software: you can redistribute it and/or modify +//~ it under the terms of the GNU General Public License as published by +//~ the Free Software Foundation, either version 3 of the License, or +//~ (at your option) any later version. +//~ +//~ This program is distributed in the hope that it will be useful, +//~ but WITHOUT ANY WARRANTY; without even the implied warranty of +//~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//~ GNU General Public License for more details. +//~ +//~ You should have received a copy of the GNU General Public License +//~ along with this program. If not, see . + var query = [] var grid = null var msg = '' @@ -8,6 +24,17 @@ var donativo = false var cfg_invoice = new Object() +function init_config_invoices(){ + var multi_currency = get_config('multi_currency') + var g = $$('grid_invoices') + + if(multi_currency){ + g.showColumn('total') + g.showColumn('currency') + } +} + + var invoices_controllers = { init: function(){ //~ Invoices @@ -53,6 +80,9 @@ var invoices_controllers = { $$('cmd_import_invoice').attachEvent('onItemClick', cmd_import_invoice_click) webix.extend($$('grid_invoices'), webix.ProgressBar) + + init_config_invoices() + focus('search_client_name') } } diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index 72a72ac..06632bd 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -38,6 +38,8 @@ function configuracion_inicial(){ $$('cmd_update_timbres').refresh() add_config({'key': 'decimales_precios', 'value': values.decimales_precios}) add_config({'key': 'used_cfdi_pays', 'value': values.pagos}) + add_config({'key': 'multi_currency', 'value': values.multi_currency}) + }) get_way_payment() diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js index bcbe808..c1f66a2 100644 --- a/source/static/js/controller/util.js +++ b/source/static/js/controller/util.js @@ -23,6 +23,7 @@ var DECIMALES = 2; //~ var DECIMALES_PU = 4; var DECIMALES_TAX = 4; var CLAVE_ANTICIPOS = '84111506'; +var CURRENCY_MN = 'MXN'; var db = new loki('data.db'); @@ -149,11 +150,17 @@ String.prototype.is_number = function(){ String.prototype.to_float = function(){ return get_float(this) } +String.prototype.to_float4 = function(){ + return get_float4(this) +} function get_float(value){ return parseFloat(value.replace('$', '').replace(',', '').trim()).round(DECIMALES) } +function get_float4(value){ + return parseFloat(value.replace('$', '').replace(',', '').trim()).round(DECIMALES_TAX) +} var format_decimal_2 = webix.Number.numToStr({ @@ -182,6 +189,11 @@ function format_currency(value){ } +function format_currency4(value){ + return '$ ' + format_decimal_4(value) +} + + webix.protoUI({ $cssName: "text", name: "currency", diff --git a/source/static/js/ui/bancos.js b/source/static/js/ui/bancos.js index 94c0f60..9620d4d 100644 --- a/source/static/js/ui/bancos.js +++ b/source/static/js/ui/bancos.js @@ -90,6 +90,10 @@ var grid_cfdi_por_pagar_cols = [ {id: 'cliente', header: ['Razón Social', {content: 'selectFilter'}], fillspace:true, sort: 'string'}, {id: 'total', header: ['Total'], width: 125, sort: 'int', + format: webix.i18n.priceFormat, css: 'right', hidden: true}, + {id: 'currency', header: ['Moneda', {content: 'selectFilter'}], + adjust: 'data', sort: 'string', hidden: true}, + {id: 'total_mn', header: ['Total M.N'], width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right'}, {id: 'saldo', header: ['Saldo'], width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right'}, @@ -107,11 +111,19 @@ var grid_cfdi_este_deposito_cols = [ {id: 'estatus', header: 'Estatus', adjust: 'header'}, {id: 'cliente', header: ['Razón Social'], fillspace: true}, {id: 'total', header: ['Total'], width: 125, sort: 'int', + format: webix.i18n.priceFormat, css: 'right', hidden: true}, + {id: 'currency', header: ['Moneda', {content: 'selectFilter'}], + adjust: 'data', sort: 'string', hidden: true}, + {id: 'total_mn', header: ['Total M.N'], width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right'}, {id: 'saldo', header: ['Saldo'], width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right'}, + {id: 'this_pay', header: ['Este pago'], width: 125, sort: 'int', hidden: true, + format: webix.i18n.priceFormat, css: 'right', editor: 'text'}, {id: 'importe', header: ['Este pago'], width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right', editor: 'text'}, + {id: 'type_change', header: ['T.C.'], width: 75, hidden: true, + format: format_currency4, css: 'right', editor: 'text'}, ] diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 516bdc2..49fe2f4 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -255,6 +255,11 @@ var grid_invoices_cols = [ adjust: "data", sort:"string"}, {id: "paid", header: ["Pagada", {content: "selectFilter"}], adjust: "data", sort:"string"}, + {id: 'total', header: ['Total', {content: 'numberFilter'}], + width: 125, sort: 'int', format: webix.i18n.priceFormat, css: 'right', + hidden: true}, + {id: 'currency', header: ['Moneda', {content: 'selectFilter'}], + adjust: 'data', sort: 'string', hidden: true}, {id: 'total_mn', header: ['Total M.N.', {content: 'numberFilter'}], width: 150, sort: 'int', format: webix.i18n.priceFormat, css: 'right', footer: {content: 'summTimbrada', css: 'right'}},