diff --git a/source/app/models/db.py b/source/app/models/db.py index ea6fe5c..d3fab7e 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -131,7 +131,7 @@ class StorageEngine(object): return main.Emisor.get_regimenes() def _get_usocfdi(self, values): - return main.SATUsoCfdi.get_activos(values) + return main.SATUsoCfdi.get_activos() def delete(self, table, id): if table == 'partner': @@ -191,6 +191,9 @@ class StorageEngine(object): def _get_timbrar(self, values): return main.Facturas.timbrar(int(values['id'])) + def _get_anticipoegreso(self, values): + return main.Facturas.anticipo_egreso(int(values['id'])) + def get_emisor(self, rfc): return main.Emisor.get_(rfc) diff --git a/source/app/models/main.py b/source/app/models/main.py index de7f418..90f8429 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -910,7 +910,7 @@ class CuentasBanco(BaseModel): .join(SATMonedas).switch(CuentasBanco) .where(CuentasBanco.id==obj.id).dicts() ) - data = {'ok': True, 'row': row[0]} + data = {'ok': True, 'row': rows[0]} return data @@ -932,13 +932,16 @@ class SATUsoCfdi(BaseModel): return 'Uso del CFDI: {} ({})'.format(self.name, self.key) @classmethod - def get_activos(cls, values): - field = SATUsoCfdi.id - if values: - field = SATUsoCfdi.key.alias('id') + def get_id(self, key): + if key is None: + return + return SATUsoCfdi.get(SATUsoCfdi.key==key).id + + @classmethod + def get_activos(cls): rows = (SATUsoCfdi .select( - field, + SATUsoCfdi.key.alias('id'), SATUsoCfdi.name.alias('value'), SATUsoCfdi.fisica, SATUsoCfdi.moral, @@ -1009,7 +1012,8 @@ class Socios(BaseModel): fields['rfc'] = fields['rfc'].upper() fields['nombre'] = util.spaces(fields['nombre']) fields['slug'] = util.to_slug(fields['nombre']) - fields['uso_cfdi'] = fields.pop('uso_cfdi_socio', None) + uso_cfdi = fields.pop('uso_cfdi_socio', None) + fields['uso_cfdi'] = SATUsoCfdi.get_id(uso_cfdi) fields['condicion_pago'] = \ CondicionesPago.get_or(fields.get('condicion_pago', None)) @@ -1030,7 +1034,14 @@ class Socios(BaseModel): str(CondicionesPago.get(id=row['condicion_pago'])) return row - rows = Socios.select(Socios.id, Socios.rfc, Socios.nombre).dicts() + rows = (Socios + .select( + Socios.id, + Socios.rfc, + Socios.nombre, + Socios.saldo_cliente) + .dicts() + ) return {'ok': True, 'rows': tuple(rows)} @classmethod @@ -1047,7 +1058,6 @@ class Socios(BaseModel): .where((Socios.id==id) & (Socios.es_cliente==True)) .dicts() ) - #~ print (id, row) if len(row): return {'ok': True, 'row': row[0]} return {'ok': False} @@ -1077,12 +1087,13 @@ class Socios(BaseModel): data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} return data - #~ ToDo Agregar Condicion de pago y tags + #~ ToDo Agregar tags row = { 'id': obj.id, 'rfc': obj.rfc, 'nombre': obj.nombre, + 'saldo_cliente': obj.saldo_cliente, } data = {'ok': True, 'row': row, 'new': True} return data @@ -1091,7 +1102,6 @@ class Socios(BaseModel): def actualizar(cls, values, id): fields = cls._clean(cls, values) try: - #~ print (fields) q = Socios.update(**fields).where(Socios.id==id) q.execute() except IntegrityError: @@ -1346,6 +1356,7 @@ class Facturas(BaseModel): acuse = TextField(default='') donativo = BooleanField(default=False) anticipo = BooleanField(default=False) + egreso_anticipo = BooleanField(default=False) tipo_relacion = TextField(default='') error = TextField(default='') @@ -1472,8 +1483,6 @@ class Facturas(BaseModel): obj = SATTipoRelacion.get(SATTipoRelacion.key==invoice.tipo_relacion) values['tiporelacion'] = str(obj) - print ('\nTR', invoice.tipo_relacion) - return values @classmethod @@ -1511,6 +1520,21 @@ class Facturas(BaseModel): def _send(self, id, rfc): return Facturas.send(id, rfc) + @util.run_in_thread + def _actualizar_saldo_cliente(self, invoice): + if invoice.tipo_comprobante == 'T': + return + + importe = invoice.total_mn + if invoice.tipo_comprobante == 'E': + importe *= -1 + + q = (Socios + .update(saldo_cliente=Socios.saldo_cliente + importe) + .where(Socios.id==invoice.cliente.id) + ) + return bool(q.execute()) + @classmethod def send(cls, id, rfc): values = Configuracion.get_({'fields': 'correo'}) @@ -1940,6 +1964,42 @@ class Facturas(BaseModel): obj.save() return obj.estatus_sat + @classmethod + def anticipo_egreso(cls, id): + origen = Facturas.get(Facturas.id == id) + relacionadas = (FacturasRelacionadas + .select(FacturasRelacionadas.factura_origen) + .where(FacturasRelacionadas.factura==origen)) + conceptos = (FacturasDetalle + .select() + .where(FacturasDetalle.factura==origen)) + impuestos = (FacturasImpuestos + .select() + .where(FacturasImpuestos.factura==origen)) + + #~ egreso_anticipo = BooleanField(default=False) + + serie = Folios.get_egreso(origen.serie) + nueva = { + 'cliente': origen.cliente, + 'tipo_comprobante': 'E', + 'forma_pago': '30', + 'serie': serie, + 'folio': cls._get_folio(cls, serie), + 'tipo_relacion': '07', + 'pagada': True, + 'lugar_expedicion': origen.lugar_expedicion, + 'uso_cfdi': origen.uso_cfdi, + 'moneda': origen.moneda, + 'tipo_cambio': origen.tipo_cambio, + 'regimen_fiscal': origen.regimen_fiscal, + 'subtotal': origen.subtotal, + 'total': origen.total, + 'total_trasladados': origen.total_trasladados, + 'total_retenciones': origen.total_retenciones, + } + return + @classmethod def timbrar(cls, id): obj = Facturas.get(Facturas.id == id) @@ -1950,6 +2010,7 @@ class Facturas(BaseModel): enviar_correo = util.get_bool(Configuracion.get_('correo_directo')) auth = Emisor.get_auth() + anticipo = False msg = 'Factura timbrada correctamente' result = util.timbra_xml(obj.xml, auth) if result['ok']: @@ -1962,6 +2023,9 @@ class Facturas(BaseModel): row = {'uuid': obj.uuid, 'estatus': 'Timbrada'} if enviar_correo: cls._send(cls, id, auth['RFC']) + if obj.tipo_comprobante == 'I' and obj.tipo_relacion == '07': + anticipo = True + cls._actualizar_saldo_cliente(cls, obj) else: msg = result['error'] obj.estatus = 'Error' @@ -1969,7 +2033,14 @@ class Facturas(BaseModel): obj.save() row = {'estatus': 'Error'} - return {'ok': result['ok'], 'msg': msg, 'row': row} + result = { + 'ok': result['ok'], + 'msg': msg, + 'row': row, + 'anticipo': anticipo + } + + return result class PreFacturas(BaseModel): diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 127006e..5d1ab53 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -280,12 +280,54 @@ function update_grid_invoices(values){ } } + +function send_anticipo_egreso(id){ + webix.ajax().get('/values/anticipoegreso', {id: id}, function(text, data){ + var values = data.json() + if(values.ok){ + msg_sucess(values.msg) + gi.add(values.row) + }else{ + webix.alert({ + title: 'Error al Timbrar', + text: values.msg, + type: 'alert-error' + }) + } + }) + +} + + +function generar_anticipo_egreso(id){ + msg = 'La factura tiene un CFDI de anticipo relacionado

' + msg += '¿Deseas generar la factura de egreso correspondiente?' + + webix.confirm({ + title: 'Generar Egreso', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + send_anticipo_egreso(id) + } + } + }) +} + + function send_timbrar(id){ webix.ajax().get('/values/timbrar', {id: id}, function(text, data){ var values = data.json() if(values.ok){ msg_sucess(values.msg) gi.updateItem(id, values.row) + if(values.anticipo){ + //~ generar_anticipo_egreso(id) + //~ show('Generar egreso de anticipo') + } }else{ webix.alert({ title: 'Error al Timbrar', @@ -406,6 +448,8 @@ function guardar_y_timbrar(values){ table_relaciones.clear() tipo_relacion = '' anticipo = false + $$('chk_cfdi_anticipo').setValue(0) + $$('form_invoice').setValues({id_partner: 0, lbl_partner: 'Ninguno'}) $$('multi_invoices').setValue('invoices_home') @@ -1362,3 +1406,7 @@ function lst_tipo_relacion_change(nv, ov){ } + +function lst_serie_change(nv, ov){ + show(nv) +} diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index e9bcb1c..ab9fcdb 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -54,6 +54,7 @@ var controllers = { $$('cmd_prefactura').attachEvent('onItemClick', cmd_prefactura_click) $$('cmd_cfdi_relacionados').attachEvent('onItemClick', cmd_cfdi_relacionados_click) $$('lst_metodo_pago').attachEvent('onChange', lst_metodo_pago_change) + $$('lst_serie').attachEvent('onChange', lst_serie_change) var tb_invoice = $$('tv_invoice').getTabbar() tb_invoice.attachEvent('onChange', tb_invoice_change) @@ -66,8 +67,8 @@ var controllers = { } -function get_uso_cfdi_to_table(args){ - webix.ajax().sync().get('/values/usocfdi', args, function(text, data){ +function get_uso_cfdi_to_table(){ + webix.ajax().sync().get('/values/usocfdi', function(text, data){ var values = data.json() table_usocfdi.clear() table_usocfdi.insert(values) diff --git a/source/static/js/controller/partners.js b/source/static/js/controller/partners.js index ae208eb..7b331ae 100644 --- a/source/static/js/controller/partners.js +++ b/source/static/js/controller/partners.js @@ -22,7 +22,7 @@ function cmd_new_partner_click(id, e, node){ $$('multi_partners').setValue('partners_new') $$('tab_partner').setValue('Datos Fiscales') - get_uso_cfdi_to_table({}) + get_uso_cfdi_to_table() query = table_usocfdi.chain().find({fisica: true}).data() $$('lst_uso_cfdi_socio').getList().parse(query) } @@ -54,7 +54,7 @@ function cmd_edit_partner_click(id, e, node){ $$('form_partner').setValues(values) $$('forma_pago').getList().load('/values/formapago') - get_uso_cfdi_to_table({}) + get_uso_cfdi_to_table() if(values.tipo_persona == 1){ query = table_usocfdi.chain().find({fisica: true}).data() }else if(values.tipo_persona == 2){ @@ -339,6 +339,6 @@ function rfc_lost_focus(prev_view){ function multi_partners_change(prevID, nextID){ - webix.message(prevID) - webix.message(nextID) + //~ webix.message(prevID) + //~ webix.message(nextID) } diff --git a/source/static/js/ui/partners.js b/source/static/js/ui/partners.js index e1613e7..96c925e 100644 --- a/source/static/js/ui/partners.js +++ b/source/static/js/ui/partners.js @@ -18,6 +18,8 @@ var grid_partners_cols = [ sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}}, {id: 'nombre', header: ['Razón Social', {content: 'textFilter'}], fillspace:true, sort: 'string'}, + {id: 'saldo_cliente', header: ['Saldo Cliente', {content: 'numberFilter'}], + width: 150, sort: 'int', format: webix.i18n.priceFormat, css: 'right'}, ]