diff --git a/source/app/controllers/cfdi_xml.py.orig b/source/app/controllers/cfdi_xml.py.orig deleted file mode 100644 index b88cff8..0000000 --- a/source/app/controllers/cfdi_xml.py.orig +++ /dev/null @@ -1,470 +0,0 @@ -#!/usr/bin/env python - -import datetime -from xml.etree import ElementTree as ET -from xml.dom.minidom import parseString - -from logbook import Logger - -#~ from settings import DEBUG - - -log = Logger('XML') -CFDI_ACTUAL = 'cfdi33' -NOMINA_ACTUAL = 'nomina12' - -SAT = { - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'cfdi32': { - 'version': '3.2', - 'prefix': 'cfdi', - 'xmlns': 'http://www.sat.gob.mx/cfd/3', - 'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd', - }, - 'cfdi33': { - 'version': '3.3', - 'prefix': 'cfdi', - 'xmlns': 'http://www.sat.gob.mx/cfd/3', - 'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd', - }, - 'nomina11': { - 'version': '1.1', - 'prefix': 'nomina', - 'xmlns': 'http://www.sat.gob.mx/nomina', - 'schema': 'http://www.sat.gob.mx/nomina http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina11.xsd', - }, - 'nomina': { - 'version': '1.2', - 'prefix': 'nomina12', - 'xmlns': 'http://www.sat.gob.mx/nomina12', - 'schema': 'http://www.sat.gob.mx/nomina12 http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina12.xsd', - }, - 'locales': { - 'version': '1.0', - 'prefix': 'implocal', - 'xmlns': 'http://www.sat.gob.mx/implocal', - 'schema': ' http://www.sat.gob.mx/implocal http://www.sat.gob.mx/sitio_internet/cfd/implocal/implocal.xsd', - }, - 'donativo': { - 'version': '1.1', - 'prefix': 'donat', - 'xmlns': 'http://www.sat.gob.mx/donat', - 'schema': ' http://www.sat.gob.mx/donat http://www.sat.gob.mx/sitio_internet/cfd/donat/donat11.xsd', - 'leyenda': 'Este comprobante ampara un donativo, el cual será destinado por la donataria a los fines propios de su objeto social. En el caso de que los bienes donados hayan sido deducidos previamente para los efectos del impuesto sobre la renta, este donativo no es deducible. La reproducción no autorizada de este comprobante constituye un delito en los términos de las disposiciones fiscales.', - }, - 'ine': { - 'version': '1.1', - 'prefix': 'ine', - 'xmlns': 'http://www.sat.gob.mx/ine', - 'schema': ' http://www.sat.gob.mx/ine http://www.sat.gob.mx/sitio_internet/cfd/ine/ine11.xsd', - }, - 'edu': { - 'version': '1.0', - 'prefix': 'iedu', - 'xmlns': 'http://www.sat.gob.mx/iedu', - 'schema': ' http://www.sat.gob.mx/iedu http://www.sat.gob.mx/sitio_internet/cfd/ine/iedu.xsd', - }, -} - - -class CFDI(object): - - def __init__(self, version=CFDI_ACTUAL): - self._sat_cfdi = SAT[version] - self._xsi = SAT['xsi'] - self._pre = self._sat_cfdi['prefix'] - self._cfdi = None - self._complemento = None - self._impuestos_locales = False - self._donativo = False - self._ine = False -<<<<<<< HEAD - self._edu = False -======= - self._is_nomina = False ->>>>>>> nomina - self.error = '' - - def _now(self): - return datetime.datetime.now().isoformat()[:19] - - def get_xml(self, datos): - if not self._validate(datos): - return '' - - self._comprobante(datos['comprobante']) - self._relacionados(datos['relacionados']) - self._emisor(datos['emisor']) - self._receptor(datos['receptor']) - self._conceptos(datos['conceptos']) - self._impuestos(datos['impuestos']) - self._locales(datos['impuestos']) - self._donatarias(datos['donativo']) - self._complementos(datos['complementos']) - - if 'nomina' in datos: - self._nomina(datos['nomina']) - - return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8')) - - def add_sello(self, sello): - self._cfdi.attrib['Sello'] = sello - return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8')) - - def _to_pretty_xml(self, source): - tree = parseString(source) - xml = tree.toprettyxml(encoding='utf-8').decode('utf-8') - return xml - - def _validate(self, datos): - if datos['impuestos']: - if datos['impuestos']['total_locales_trasladados'] or \ - datos['impuestos']['total_locales_retenciones']: - self._impuestos_locales = True - - if datos['donativo']: - self._donativo = True - - if datos['complementos']: - if 'ine' in datos['complementos']: - self._ine = True - - self._edu = datos['edu'] - - if 'nomina' in datos: - self._is_nomina = True - return self._validate_nomina(datos) - - return True - - def _validate_nomina(self, datos): - return True - - def _comprobante(self, datos): - attributes = {} - attributes['xmlns:{}'.format(self._pre)] = self._sat_cfdi['xmlns'] - attributes['xmlns:xsi'] = self._xsi - - schema_locales = '' - if self._impuestos_locales: - name = 'xmlns:{}'.format(SAT['locales']['prefix']) - attributes[name] = SAT['locales']['xmlns'] - schema_locales = SAT['locales']['schema'] - - schema_donativo = '' - if self._donativo: - name = 'xmlns:{}'.format(SAT['donativo']['prefix']) - attributes[name] = SAT['donativo']['xmlns'] - schema_donativo = SAT['donativo']['schema'] - - schema_ine = '' - if self._ine: - name = 'xmlns:{}'.format(SAT['ine']['prefix']) - attributes[name] = SAT['ine']['xmlns'] - schema_ine = SAT['ine']['schema'] - -<<<<<<< HEAD - schema_edu = '' - if self._edu: - name = 'xmlns:{}'.format(SAT['edu']['prefix']) - attributes[name] = SAT['edu']['xmlns'] - schema_edu = SAT['edu']['schema'] - - attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \ - schema_locales + schema_donativo + schema_ine + schema_edu -======= - schema_nomina = '' - if self._nomina: - name = 'xmlns:{}'.format(SAT['nomina']['prefix']) - attributes[name] = SAT['nomina']['xmlns'] - schema_nomina = SAT['nomina']['schema'] - - attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \ - schema_locales + schema_donativo + schema_ine + schema_nomina ->>>>>>> nomina - attributes.update(datos) - - if not 'Version' in attributes: - attributes['Version'] = self._sat_cfdi['version'] - if not 'Fecha' in attributes: - attributes['Fecha'] = self._now() - - self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes) - return - - def _relacionados(self, datos): - if not datos or not datos['tipo'] or not datos['cfdis']: - return - - node_name = '{}:CfdiRelacionados'.format(self._pre) - value = {'TipoRelacion': datos['tipo']} - node = ET.SubElement(self._cfdi, node_name, value) - for uuid in datos['cfdis']: - node_name = '{}:CfdiRelacionado'.format(self._pre) - value = {'UUID': uuid} - ET.SubElement(node, node_name, value) - return - - def _emisor(self, datos): - node_name = '{}:Emisor'.format(self._pre) - emisor = ET.SubElement(self._cfdi, node_name, datos) - return - - def _receptor(self, datos): - node_name = '{}:Receptor'.format(self._pre) - emisor = ET.SubElement(self._cfdi, node_name, datos) - return - - def _conceptos(self, datos): - from xml.sax.saxutils import escape, unescape - - conceptos = ET.SubElement(self._cfdi, '{}:Conceptos'.format(self._pre)) - for row in reversed(datos): - # ~ print (row['Descripcion']) - # ~ xml = escape(xml.encode('ascii', 'xmlcharrefreplace').decode('utf-8'), False) - # ~ row['Descripcion'] = escape(row['Descripcion'].replace('\n', ' '), False) - # ~ row['Descripcion'] = row['Descripcion'].replace('\n', ' ') - # ~ print (row['Descripcion']) - - complemento = {} - if 'complemento' in row: - complemento = row.pop('complemento') - cuenta_predial = row.pop('CuentaPredial', '') - pedimento = row.pop('Pedimento', '') - student = row.pop('student', '') - - taxes = {} - if 'impuestos' in row: - taxes = row.pop('impuestos') - node_name = '{}:Concepto'.format(self._pre) - concepto = ET.SubElement(conceptos, node_name, row) - - if taxes: - node_name = '{}:Impuestos'.format(self._pre) - impuestos = ET.SubElement(concepto, node_name) - if 'traslados' in taxes and taxes['traslados']: - node_name = '{}:Traslados'.format(self._pre) - traslados = ET.SubElement(impuestos, node_name) - for traslado in taxes['traslados']: - ET.SubElement( - traslados, '{}:Traslado'.format(self._pre), traslado) - if 'retenciones' in taxes and taxes['retenciones']: - node_name = '{}:Retenciones'.format(self._pre) - retenciones = ET.SubElement(impuestos, node_name) - for retencion in taxes['retenciones']: - ET.SubElement( - retenciones, '{}:Retencion'.format(self._pre), retencion) - - if pedimento: - attributes = {'NumeroPedimento': pedimento} - node_name = '{}:InformacionAduanera'.format(self._pre) - ET.SubElement(concepto, node_name, attributes) - - if cuenta_predial: - attributes = {'Numero': cuenta_predial} - node_name = '{}:CuentaPredial'.format(self._pre) - ET.SubElement(concepto, node_name, attributes) - - if student: - node_name = '{}:ComplementoConcepto'.format(self._pre) - complemento = ET.SubElement(concepto, node_name) - ET.SubElement(complemento, 'iedu:instEducativas', student) - return - - def _impuestos(self, datos): - if self._is_nomina: - return - - if not datos: - node_name = '{}:Impuestos'.format(self._pre) - ET.SubElement(self._cfdi, node_name) - return - - attributes = {} - fields = ('TotalImpuestosTrasladados', 'TotalImpuestosRetenidos') - for field in fields: - if field in datos: - attributes[field] = datos[field] - node_name = '{}:Impuestos'.format(self._pre) - impuestos = ET.SubElement(self._cfdi, node_name, attributes) - - if 'retenciones' in datos and datos['retenciones']: - retenciones = ET.SubElement(impuestos, '{}:Retenciones'.format(self._pre)) - for row in datos['retenciones']: - ET.SubElement(retenciones, '{}:Retencion'.format(self._pre), row) - - if 'traslados' in datos and datos['traslados']: - traslados = ET.SubElement(impuestos, '{}:Traslados'.format(self._pre)) - for row in datos['traslados']: - ET.SubElement(traslados, '{}:Traslado'.format(self._pre), row) - return - - def _nomina(self, datos): - pre = SAT['nomina']['prefix'] - - if self._complemento is None: - self._complemento = ET.SubElement( - self._cfdi, '{}:Complemento'.format(self._pre)) - - emisor = datos.pop('emisor', None) - receptor = datos.pop('receptor', None) - percepciones = datos.pop('percepciones', None) - deducciones = datos.pop('deducciones', None) - otros_pagos = datos.pop('otros_pagos', ()) - incapacidades = datos.pop('incapacidades', ()) - - nomina = ET.SubElement( - self._complemento, '{}:Nomina'.format(pre), datos['nomina']) - - if emisor: - ET.SubElement(nomina, '{}:Emisor'.format(pre), emisor) - - if receptor: - node = ET.SubElement(nomina, '{}:Receptor'.format(pre), receptor) - - if percepciones: - details = percepciones.pop('details', None) - hours_extra = percepciones.pop('hours_extra', None) - separacion = percepciones.pop('separacion', None) - if details: - node = ET.SubElement(nomina, '{}:Percepciones'.format(pre), percepciones) - for row in details: - nodep = ET.SubElement(node, '{}:Percepcion'.format(pre), row) - if row['TipoPercepcion'] == '019' and hours_extra: - for he in hours_extra: - ET.SubElement(nodep, '{}:HorasExtra'.format(pre), he) - hours_extra = None - if separacion: - ET.SubElement(node, '{}:SeparacionIndemnizacion'.format(pre), separacion) - - if deducciones: - details = deducciones.pop('details', None) - if details: - deducciones = ET.SubElement(nomina, '{}:Deducciones'.format(pre), deducciones) - for row in details: - ET.SubElement(deducciones, '{}:Deduccion'.format(pre), row) - - if otros_pagos: - node = ET.SubElement(nomina, '{}:OtrosPagos'.format(pre)) - for row in otros_pagos: - subsidio = row.pop('subsidio', None) - subnode = ET.SubElement(node, '{}:OtroPago'.format(pre), row) - if subsidio: - ET.SubElement(subnode, '{}:SubsidioAlEmpleo'.format(pre), subsidio) - - if incapacidades: - node = ET.SubElement(nomina, '{}:Incapacidades'.format(pre)) - for row in incapacidades: - ET.SubElement(node, '{}:Incapacidad'.format(pre), row) - return - - def _locales(self, datos): - if not self._impuestos_locales: - return - - if self._complemento is None: - self._complemento = ET.SubElement( - self._cfdi, '{}:Complemento'.format(self._pre)) - - attributes = {} - attributes['version'] = SAT['locales']['version'] - if not datos['total_locales_trasladados']: - datos['total_locales_trasladados'] = '0.00' - attributes['TotaldeTraslados'] = datos['total_locales_trasladados'] - if not datos['total_locales_retenciones']: - datos['total_locales_retenciones'] = '0.00' - attributes['TotaldeRetenciones'] = datos['total_locales_retenciones'] - - node = ET.SubElement( - self._complemento, 'implocal:ImpuestosLocales', attributes) - - for retencion in datos['locales_retenciones']: - ET.SubElement(node, 'implocal:RetencionesLocales', retencion) - for traslado in datos['locales_trasladados']: - ET.SubElement(node, 'implocal:TrasladosLocales', traslado) - return - - def _donatarias(self, datos): - if not datos: - return - - if self._complemento is None: - self._complemento = ET.SubElement( - self._cfdi, '{}:Complemento'.format(self._pre)) - - attributes = {} - attributes['version'] = SAT['donativo']['version'] - attributes['leyenda'] = SAT['donativo']['leyenda'] - attributes.update(datos) - - node = ET.SubElement(self._complemento, 'donat:Donatarias', attributes) - - return - - def _complementos(self, datos): - if not datos: - return - - if self._complemento is None: - self._complemento = ET.SubElement( - self._cfdi, '{}:Complemento'.format(self._pre)) - - if 'ine' in datos: - atributos = {'Version': SAT['ine']['version']} - atributos.update(datos['ine']) - ET.SubElement(self._complemento, 'ine:INE', atributos) - - if 'ce' in datos: - pre = 'cce11' - datos = datos.pop('ce') - emisor = datos.pop('emisor') - propietario = datos.pop('propietario') - receptor = datos.pop('receptor') - destinatario = datos.pop('destinatario') - conceptos = datos.pop('conceptos') - - attributes = {} - attributes['xmlns:{}'.format(pre)] = \ - 'http://www.sat.gob.mx/ComercioExterior11' - attributes['xsi:schemaLocation'] = \ - 'http://www.sat.gob.mx/ComercioExterior11 ' \ - 'http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd' - attributes.update(datos) - ce = ET.SubElement( - complemento, '{}:ComercioExterior'.format(pre), attributes) - - attributes = {} - if 'Curp' in emisor: - attributes = {'Curp': emisor.pop('Curp')} - node = ET.SubElement(ce, '{}:Emisor'.format(pre), attributes) - ET.SubElement(node, '{}:Domicilio'.format(pre), emisor) - - if propietario: - ET.SubElement(ce, '{}:Propietario'.format(pre), propietario) - - attributes = {} - if 'NumRegIdTrib' in receptor: - attributes = {'NumRegIdTrib': receptor.pop('NumRegIdTrib')} - node = ET.SubElement(ce, '{}:Receptor'.format(pre), attributes) - ET.SubElement(node, '{}:Domicilio'.format(pre), receptor) - - attributes = {} - if 'NumRegIdTrib' in destinatario: - attributes = {'NumRegIdTrib': destinatario.pop('NumRegIdTrib')} - if 'Nombre' in destinatario: - attributes.update({'Nombre': destinatario.pop('Nombre')}) - node = ET.SubElement(ce, '{}:Destinatario'.format(pre), attributes) - ET.SubElement(node, '{}:Domicilio'.format(pre), destinatario) - - node = ET.SubElement(ce, '{}:Mercancias'.format(pre)) - fields = ('Marca', 'Modelo', 'SubModelo', 'NumeroSerie') - for row in conceptos: - detalle = {} - for f in fields: - if f in row: - detalle[f] = row.pop(f) - concepto = ET.SubElement(node, '{}:Mercancia'.format(pre), row) - if detalle: - ET.SubElement( - concepto, '{}:DescripcionesEspecificas'.format(pre), detalle) - return diff --git a/source/app/main.py.orig b/source/app/main.py.orig deleted file mode 100644 index ed48fbc..0000000 --- a/source/app/main.py.orig +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 - -import falcon -from falcon_multipart.middleware import MultipartMiddleware -from beaker.middleware import SessionMiddleware - -from middleware import ( - AuthMiddleware, - JSONTranslator, - ConnectionMiddleware, - static, - handle_404 -) -from models.db import StorageEngine -from controllers.main import (AppEmpresas, - AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig, - AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios, - AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco, -<<<<<<< HEAD - AppMovimientosBanco, AppTickets, AppStudents -======= - AppMovimientosBanco, AppTickets, AppEmployees, AppNomina ->>>>>>> nomina -) - - -from settings import DEBUG, MV, PATH_SESSIONS - - -db = StorageEngine() - -api = falcon.API(middleware=[ - AuthMiddleware(), - JSONTranslator(), - ConnectionMiddleware(), - MultipartMiddleware(), -]) -api.req_options.auto_parse_form_urlencoded = True -api.add_sink(handle_404, '') - -api.add_route('/empresas', AppEmpresas(db)) -api.add_route('/', AppLogin(db)) -api.add_route('/logout', AppLogout(db)) -api.add_route('/admin', AppAdmin(db)) -api.add_route('/emisor', AppEmisor(db)) -api.add_route('/folios', AppFolios(db)) -api.add_route('/main', AppMain(db)) -api.add_route('/values/{table}', AppValues(db)) -api.add_route('/files/{table}', AppFiles(db)) -api.add_route('/config', AppConfig(db)) -api.add_route('/doc/{type_doc}/{id_doc}', AppDocumentos(db)) -api.add_route('/partners', AppPartners(db)) -api.add_route('/products', AppProducts(db)) -api.add_route('/invoices', AppInvoices(db)) -api.add_route('/preinvoices', AppPreInvoices(db)) -api.add_route('/tickets', AppTickets(db)) -api.add_route('/cuentasbanco', AppCuentasBanco(db)) -api.add_route('/movbanco', AppMovimientosBanco(db)) -<<<<<<< HEAD -api.add_route('/students', AppStudents(db)) -======= -api.add_route('/employees', AppEmployees(db)) -api.add_route('/nomina', AppNomina(db)) ->>>>>>> nomina - - -# ~ Activa si usas waitress y NO estas usando servidor web -# ~ api.add_sink(static, '/static') - -session_options = { - 'session.type': 'file', - 'session.cookie_expires': True, - 'session.httponly': True, - 'session.secure': True, - 'session.data_dir': PATH_SESSIONS['data'], - 'session.lock_dir': PATH_SESSIONS['lock'], -} -if DEBUG or MV: - session_options['session.secure'] = False - -app = SessionMiddleware(api, session_options) - diff --git a/source/app/models/db.py.orig b/source/app/models/db.py.orig deleted file mode 100644 index e5b9f7a..0000000 --- a/source/app/models/db.py.orig +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env python - -from . import main - - -class StorageEngine(object): - - def __init__(self): - pass - - def authenticate(self, args): - return main.authenticate(args) - - def get_employees(self, values): - return main.Empleados.get_by(values) - - def get_nomina(self, values): - return main.CfdiNomina.get_by(values) - - def nomina(self, values): - opt = values.pop('opt') - if opt == 'cancel': - return main.CfdiNomina.cancel(int(values['id'])) - - def empresa_agregar(self, values): - return main.empresa_agregar(values['alta_rfc'], False) - - def empresa_borrar(self, values): - return main.empresa_borrar(values['rfc']) - - def _get_empresas(self, values): - return main.get_empresas() - - def get_values(self, table, values=None, session=None): - if table in ('allusuarios', 'usuarioupdate'): - return getattr(self, '_get_{}'.format(table))(values, session) - return getattr(self, '_get_{}'.format(table))(values) - - def _get_schoolgroups(self, values): - return main.Grupos.get_by(values) - - def _get_nivedusat(self, values): - return main.SATNivelesEducativos.get_by() - - def _get_niveduall(self, values): - return main.NivelesEducativos.get_all() - - def _get_titlelogin(self, values): - return main.get_title_app(2) - - def _get_canopenpre(self, values): - return main.PreFacturasDetalle.can_open(values['id']) - - def _get_importinvoice(self, values): - return main.import_invoice() - - def _get_main(self, values): - return main.config_main() - - def _get_configtimbrar(self, values): - return main.config_timbrar() - - def _get_invoicenotes(self, values): - return main.Facturas.get_notes(values['id']) - - def save_invoice_notes(self, values): - return main.Facturas.save_notes(values) - - def _get_configticket(self, values): - return main.config_ticket() - - def _get_saldocuenta(self, values): - return main.CuentasBanco.get_saldo(values['id']) - - def _get_validartimbrar(self, values): - return main.validar_timbrar() - - def _get_preproductos(self, values): - return main.PreFacturasDetalle.facturar(values['id']) - - def upload_file(self, session, table, file_obj): - if not 'rfc' in session: - return {'status': 'error'} - return main.upload_file(session['rfc'], table, file_obj) - - def get_config(self, values): - return main.Configuracion.get_(values) - - def add_config(self, values): - return main.Configuracion.add(values) - - def add_cert(self, file_obj): - return main.Certificado.add(file_obj) - - def validate_cert(self, values, session): - return main.Certificado.validate(values, session) - - def validate_email(self, values): - return main.test_correo(values) - - def send_email(self, values, session): - return main.Facturas.send(values['id'], session['rfc']) - - def enviar_prefac(self, values): - return main.PreFacturas.enviar(values['id']) - - def _get_cancelinvoice(self, values): - return main.Facturas.cancel(values['id']) - - def _get_statussat(self, values): - return main.Facturas.get_status_sat(values['id']) - - def _get_filteryears(self, values): - years1 = main.Facturas.filter_years() - years2 = main.PreFacturas.filter_years() - return [years1, years2] - - def _get_filteryearsticket(self, values): - return main.Tickets.filter_years() - - def _get_filteryearsnomina(self, values): - return main.CfdiNomina.filter_years() - - def _get_cuentayears(self, values): - return main.CuentasBanco.get_years() - - def _get_cert(self, values): - return main.Certificado.get_data() - - def _get_cp(self, values): - return main.get_cp(values['cp']) - - def _get_formapago(self, values): - return main.SATFormaPago.get_activos(values) - - def _get_tiporelacion(self, values): - return main.SATTipoRelacion.get_activos(values) - - def _get_condicionespago(self, values): - return main.CondicionesPago.get_() - - def _get_categorias(self, values): - return main.Categorias.get_all() - - def _get_newkey(self, values): - return main.Productos.next_key() - - def _get_unidades(self, values): - return main.SATUnidades.get_activos() - - def add_moneda(self, values): - return main.SATMonedas.add(values) - - def add_unidad(self, values): - return main.SATUnidades.add(values) - - def add_impuesto(self, values): - return main.SATImpuestos.add(values) - - def add_usuario(self, values): - return main.Usuarios.add(values) - - def edit_usuario(self, values): - return main.Usuarios.edit(values) - - def _get_taxes(self, values): - return main.SATImpuestos.get_activos() - - def _get_alltaxes(self, values): - return main.SATImpuestos.get_() - - def _get_allcurrencies(self, values): - return main.SATMonedas.get_() - - def _get_allbancos(self, values): - return main.SATBancos.get_() - - def _get_allunidades(self, values): - return main.SATUnidades.get_() - - def _get_allformasdepago(self, values): - return main.SATFormaPago.get_() - - def _get_allusoscfdi(self, values): - return main.SATUsoCfdi.get_all() - - def _get_allusuarios(self, values, session): - return main.Usuarios.get_(session['userobj']) - - def _get_usuarioupdate(self, values, session): - return main.Usuarios.actualizar(values, session['userobj']) - - def _get_taxupdate(self, values): - return main.SATImpuestos.actualizar(values) - - def _get_currencyupdate(self, values): - return main.SATMonedas.actualizar(values) - - def _get_bancoupdate(self, values): - return main.SATBancos.actualizar(values) - - def _get_unidadupdate(self, values): - return main.SATUnidades.actualizar(values) - - def _get_formasdepagoupdate(self, values): - return main.SATFormaPago.actualizar(values) - - def _get_usocfdiupdate(self, values): - return main.SATUsoCfdi.actualizar(values) - - def _get_emisorcuentasbanco(self, values): - return main.CuentasBanco.emisor() - - def _get_satkey(self, values): - return main.get_sat_key(values['key']) - - def _get_satmonedas(self, values): - return main.get_sat_monedas(values['key']) - - def _get_satunidades(self, values): - return main.get_sat_unidades(values['key']) - - def _get_satproductos(self, values): - return main.get_sat_productos(values['key']) - - def _get_series(self, values): - return main.Folios.get_all() - - def _get_monedas(self, values): - return main.SATMonedas.get_activos() - - def _get_monedasid(self, values): - return main.SATMonedas.get_activos_by_id() - - def _get_bancosid(self, values): - return main.SATBancos.get_activos_by_id() - - def _get_regimenes(self, values): - return main.Emisor.get_regimenes() - - def _get_usocfdi(self, values): - return main.SATUsoCfdi.get_activos() - - def _get_ebancomov(self, values): - return main.MovimientosBanco.con(values['id']) - - def delete(self, table, id): - if table == 'partner': - return main.Socios.remove(id) - if table == 'product': - return main.Productos.remove(id) - if table == 'invoice': - return main.Facturas.remove(id) - if table == 'folios': - return main.Folios.remove(id) - if table == 'preinvoice': - return main.PreFacturas.remove(id) - if table == 'satimpuesto': - return main.SATImpuestos.remove(id) - if table == 'satunit': - return main.SATUnidades.remove(id) - if table == 'cuentasbanco': - return main.CuentasBanco.remove(id) - if table == 'movbanco': - return main.MovimientosBanco.remove(id) - if table == 'usuario': - return main.Usuarios.remove(id) - if table == 'config': - return main.Configuracion.remove(id) -<<<<<<< HEAD - if table == 'nivedu': - return main.NivelesEducativos.remove(id) - if table == 'students': - return main.Alumnos.remove(id) -======= - if table == 'employee': - return main.Empleados.remove(id) - if table == 'nomina': - return main.CfdiNomina.remove(id) ->>>>>>> nomina - return False - - def _get_client(self, values): - return main.Socios.get_by_client(values) - - def _get_student(self, values): - return main.Alumnos.get_by_name(values) - - def _get_product(self, values): - return main.Productos.get_by(values) - - def _get_productokey(self, values): - return main.Productos.get_by_key(values) - - def get_partners(self, values): - return main.Socios.get_(values) - - def partner(self, values): - id = int(values.pop('id', '0')) - if id: - return main.Socios.actualizar(values, id) - return main.Socios.add(values) - - def get_products(self, values): - return main.Productos.get_(values) - - def products(self, values): - id = int(values.pop('id', '0')) - if id: - return main.Productos.actualizar(values, id) - - opt = values.get('opt', '') - if opt: - return main.Productos.opt(values) - - return main.Productos.add(values) - - def invoice(self, values, user): - id = int(values.pop('id', '0')) - if id: - return main.Facturas.actualizar(values, id) - - return main.Facturas.add(values, user) - - def preinvoice(self, values): - id = int(values.pop('id', '0')) - #~ if id: - #~ return main.PreFacturas.actualizar(values, id) - return main.PreFacturas.add(values) - - def get_students(self, values): - return main.Alumnos.get_by(values) - - def students(self, values): - opt = values.pop('opt') - if opt == 'add': - return main.Alumnos.add(values['values']) - if opt == 'edit': - return main.Alumnos.actualizar(values['values']) - - def tickets(self, values, user): - opt = values.pop('opt') - if opt == 'add': - return main.Tickets.add(values, user) - if opt == 'cancel': - return main.Tickets.cancel(values) - if opt == 'invoice': - return main.Tickets.invoice(values, user) - if opt == 'print': - return main.Tickets.printer(values) - - def get_tickets(self, values): - return main.Tickets.get_by(values) - - def get_invoices(self, values): - return main.Facturas.get_(values) - - def get_preinvoices(self, values): - return main.PreFacturas.get_(values) - - 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) - - def emisor(self, values): - return main.Emisor.add(values) - - def cuentasbanco(self, values): - return main.CuentasBanco.add(values) - - def add_movbanco(self, values): - return main.MovimientosBanco.add(values) - - def get_cuentasbanco(self, values): - return main.CuentasBanco.get_(values) - - def get_folios(self): - return main.Folios.get_() - - def add_folios(self, values): - return main.Folios.add(values) - - def add_nivel_educativo(self, values): - return main.NivelesEducativos.add(values) - - def get_doc(self, type_doc, id, rfc): - return main.get_doc(type_doc, id, rfc) - - def get_movimientosbanco(self, values): - return main.MovimientosBanco.get_(values) - - def importar_bdfl(self): - return main.importar_bdfl() diff --git a/source/app/models/main.py b/source/app/models/main.py index d086fdf..a70e66e 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1713,7 +1713,7 @@ class MovimientosBanco(BaseModel): .select() .where( (MovimientosBanco.cuenta==cuenta) & - (MovimientosBanco.fecha>>>>>> nomina - } - if not obj is None: - titulo = '{} - {}' - data['empresa'] = titulo.format(data['empresa'], obj.nombre) - data['escuela'] = obj.es_escuela - - return data - - -def config_timbrar(): - try: - obj = Emisor.select()[0] - except IndexError: - return {'cfdi_donativo': False} - - conf = { - 'cfdi_donativo': obj.es_ong, - 'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'), - 'cfdi_ine': Configuracion.get_bool('chk_config_ine'), - 'cfdi_edu': Configuracion.get_bool('chk_config_edu'), - 'cfdi_metodo_pago': Configuracion.get_bool('chk_config_ocultar_metodo_pago'), - 'cfdi_condicion_pago': Configuracion.get_bool('chk_config_ocultar_condiciones_pago'), - 'cfdi_open_pdf': Configuracion.get_bool('chk_config_open_pdf'), - 'cfdi_show_pedimento': Configuracion.get_bool('chk_config_show_pedimento'), - 'cfdi_tax_locales': Configuracion.get_bool('chk_config_tax_locales'), - 'cfdi_tax_decimals': Configuracion.get_bool('chk_config_tax_decimals'), - 'cfdi_with_taxes': Configuracion.get_bool('chk_config_price_with_taxes_in_invoice'), - } - - return conf - - -def config_ticket(): - conf = { - 'open_pdf': Configuracion.get_bool('chk_ticket_pdf_show'), - 'direct_print': Configuracion.get_bool('chk_ticket_direct_print'), - 'edit_cant': Configuracion.get_bool('chk_ticket_edit_cant'), - 'total_up': Configuracion.get_bool('chk_ticket_total_up'), - } - return conf - - -class Configuracion(BaseModel): - clave = TextField(unique=True) - valor = TextField(default='') - - def __str__(self): - return '{} = {}'.format(self.clave, self.valor) - - @classmethod - def get_bool(cls, key): - data = (Configuracion - .select(Configuracion.valor) - .where(Configuracion.clave == key) - ) - if data: - return util.get_bool(data[0].valor) - return False - - @classmethod - def get_(cls, keys): - if isinstance(keys, str): - data = (Configuracion - .select(Configuracion.valor) - .where(Configuracion.clave == keys) - ) - if data: - return data[0].valor - return '' - - if keys['fields'] == 'productos': - fields = ( - 'chk_config_cuenta_predial', - 'chk_config_codigo_barras', - 'chk_config_precio_con_impuestos', - 'chk_llevar_inventario', - ) - data = (Configuracion - .select() - .where(Configuracion.clave.in_(fields)) - ) - values = {r.clave: r.valor for r in data} - values['default_tax'] = SATImpuestos.select()[0].id - values['default_unidad'] = SATUnidades.get_default() - return values - - if keys['fields'] == 'configtemplates': - try: - emisor = Emisor.select()[0] - is_ong = emisor.es_ong - except IndexError: - is_ong = False - - values = {'txt_plantilla_donataria': is_ong} - fields = ( - ('chk_usar_punto_de_venta', 'txt_plantilla_ticket'), - ) - - for s, key in fields: - value = util.get_bool(Configuracion.get_(s)) - values[key] = value - - return values - - if keys['fields'] == 'configotros': - fields = ( - 'chk_config_ocultar_metodo_pago', - 'chk_config_ocultar_condiciones_pago', - 'chk_config_send_zip', - 'chk_config_open_pdf', - 'chk_config_show_pedimento', - 'chk_config_tax_locales', - 'chk_config_tax_decimals', - 'chk_config_price_with_taxes_in_invoice', - 'chk_config_anticipo', - 'chk_config_cuenta_predial', - 'chk_config_codigo_barras', - 'chk_config_precio_con_impuestos', - 'chk_llevar_inventario', - 'chk_config_ine', - 'chk_config_edu', - 'chk_usar_punto_de_venta', - 'chk_ticket_pdf_show', - 'chk_ticket_direct_print', - 'chk_ticket_edit_cant', - 'chk_ticket_total_up', - 'chk_usar_nomina', - ) - data = (Configuracion - .select() - .where(Configuracion.clave.in_(fields)) - ) - values = {r.clave: util.get_bool(r.valor) for r in data} - tp = 'txt_ticket_printer' - values[tp] = Configuracion.get_(tp) - return values - - if keys['fields'] == 'correo': - 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') - data = (Configuracion - .select() - .where(Configuracion.clave.in_(fields)) - ) - elif keys['fields'] == 'templates': - fields = ( - 'txt_plantilla_factura_32', - 'txt_plantilla_factura_33', - 'txt_plantilla_factura_33j', - 'txt_plantilla_ticket', - 'txt_plantilla_donataria', - ) - data = (Configuracion - .select() - .where(Configuracion.clave.in_(fields)) - ) - elif keys['fields'] == 'timbrar': - fields = ( - 'chk_config_ocultar_metodo_pago', - 'chk_config_ocultar_condiciones_pago', - 'chk_config_anticipo', - 'chk_config_ine', - 'chk_config_open_pdf', - ) - data = (Configuracion - .select() - .where(Configuracion.clave.in_(fields)) - ) - - values = {r.clave: r.valor for r in data} - return values - - @classmethod - def add(cls, values): - # ~ print (values) - try: - for k, v in values.items(): - #~ print (k, v) - obj, created = Configuracion.get_or_create(clave=k) - obj.valor = v - obj.save() - return {'ok': True} - except Exception as e: - log.error(str(e)) - return {'ok': False, 'msg': str(e)} - - @classmethod - def remove(cls, key): - q = Configuracion.delete().where(Configuracion.clave==key) - return bool(q.execute()) - - class Meta: - order_by = ('clave',) - indexes = ( - (('clave', 'valor'), True), - ) - - -class Tags(BaseModel): - tag = TextField(index=True, unique=True) - - class Meta: - order_by = ('tag',) - - -class TipoDireccion(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoTitulo(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoTelefono(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoCorreo(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoPariente(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoResponsable(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoMovimientoAlumno(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class TipoMovimientoAlmacen(BaseModel): - nombre = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - def __str__(self): - return self.nombre - - -class Sucursales(BaseModel): - nombre = TextField(default='') - direccion = TextField(default='') - serie_facturas = TextField(default='') - serie_tickets = TextField(default='') - - class Meta: - order_by = ('nombre',) - - -class Usuarios(BaseModel): - usuario = TextField(unique=True) - nombre = TextField(default='') - apellidos = TextField(default='') - correo = TextField(default='') - contraseña = PasswordField() - es_superusuario = BooleanField(default=False) - es_admin = BooleanField(default=False) - es_activo = BooleanField(default=True) - fecha_ingreso = DateTimeField(default=util.now) - ultimo_ingreso = DateTimeField(null=True) - sucursal = ForeignKeyField(Sucursales, null=True) - - def __str__(self): - t = '{} {} ({})' - return t.format(self.nombre, self.apellidos, self.usuario) - - class Meta: - order_by = ('nombre', 'apellidos') - - @classmethod - def add(cls, values): - values['contraseña'] = values.pop('contra') - try: - Usuarios.create(**values) - return {'ok': True} - except Exception as e: - log.error(e) - msg = 'Ocurrio un error, consulta a soporte técnico' - return {'ok': False, 'msg': msg} - - @classmethod - def remove(cls, id): - q = Usuarios.delete().where(Usuarios.id==int(id)) - return bool(q.execute()) - - @classmethod - def edit(self, values): - # ~ print (values) - id = int(values.pop('id')) - try: - if 'contra' in values: - values['contraseña'] = values.pop('contra') - q = Usuarios.update(**values).where(Usuarios.id==id) - result = {'ok': bool(q.execute())} - except IntegrityError: - msg = 'El usuario ya existe' - result = {'ok': False, 'msg': msg} - - return result - - @classmethod - def actualizar(self, values, user): - id = int(values['id']) - v = {'0': False, '1': True} - - if values['field'] == 'es_superusuario' and not user.es_superusuario: - msg = 'Solo un super usuario puede hacer este cambio' - return {'ok': False, 'msg': msg} - - if values['field'] == 'es_activo': - q = (Usuarios - .update(**{'es_activo': v[values['value']]}) - .where(Usuarios.id==id)) - result = bool(q.execute()) - elif values['field'] == 'es_admin': - q = (Usuarios - .update(**{'es_admin': v[values['value']]}) - .where(Usuarios.id==id)) - result = bool(q.execute()) - elif values['field'] == 'es_superusuario': - q = (Usuarios - .update(**{'es_superusuario': v[values['value']]}) - .where(Usuarios.id==id)) - result = bool(q.execute()) - - return {'ok': result} - - @classmethod - def get_(cls, user): - if user.es_superusuario: - rows = Usuarios.select().dicts() - else: - filters = (Usuarios.es_superusuario == False) - rows = Usuarios.select().where(filters).dicts() - for row in rows: - del row['contraseña'] - return tuple(rows) - - -class Registro(BaseModel): - usuario = TextField() - accion = TextField(default='') - tabla = TextField(default='') - fecha = DateTimeField(default=util.now) - - def __str__(self): - t = '{} {}-{} ({})' - return t.format(self.usuario, self.accion, self.tabla, self.fecha) - - class Meta: - order_by = ('usuario', 'fecha') - - @classmethod - def add(cls, values): - try: - Registro.create(**values) - return - except: - return - - -class SATRegimenes(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - fisica = BooleanField(default=False) - moral = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return '{} ({})'.format(self.name, self.key) - - @classmethod - def get_(cls, ids): - if isinstance(ids, int): - ids = [ids] - return SATRegimenes.select().where(SATRegimenes.id.in_(ids)) - - @classmethod - def get_activos(cls, rfc): - where = ((SATRegimenes.activo==True) & (SATRegimenes.fisica==True)) - if (len(rfc) == 12): - where = ((SATRegimenes.activo==True) & (SATRegimenes.moral==True)) - - rows = (SATRegimenes - .select( - SATRegimenes.id, - SATRegimenes.name.alias('value')) - .where(where) - .dicts() - ) - return tuple(rows) - - -class Emisor(BaseModel): - rfc = TextField(unique=True) - nombre = TextField(default='') - nombre_comercial = TextField(default='') - calle = TextField(default='') - no_exterior = TextField(default='') - no_interior = TextField(default='') - colonia = TextField(default='') - municipio = TextField(default='') - estado = TextField(default='') - pais = TextField(default='México') - codigo_postal = TextField(default='') - cp_expedicion = TextField(default='') - es_moral = BooleanField(default=False) - es_ong = BooleanField(default=False) - es_escuela = BooleanField(default=False) - autorizacion = TextField(default='') - fecha_autorizacion = DateField(null=True) - fecha_dof = DateField(null=True) - telefono = TextField(default='') - correo = TextField(default='') - web = TextField(default='') - curp = TextField(default='') - correo_timbrado = TextField(default='') - token_timbrado = TextField(default='') - token_soporte = TextField(default='') - logo = TextField(default='') - registro_patronal = TextField(default='') - regimenes = ManyToManyField(SATRegimenes, related_name='emisores') - - def __str__(self): - t = '{} ({})' - return t.format(self.nombre, self.rfc) - - class Meta: - order_by = ('nombre',) - - @classmethod - def get_(cls, rfc): - regimenes = SATRegimenes.get_activos(rfc) - row = {'regimenes': regimenes} - - obj = Emisor.select().where(Emisor.rfc==rfc) - if bool(obj): - obj = obj[0] - row['emisor'] = { - 'emisor_rfc': obj.rfc, - 'emisor_curp': obj.curp, - 'emisor_nombre': obj.nombre, - 'emisor_cp': obj.codigo_postal, - 'emisor_cp2': obj.cp_expedicion, - 'emisor_calle': obj.calle, - 'emisor_no_exterior': obj.no_exterior, - 'emisor_no_interior': obj.no_interior, - 'emisor_colonia': obj.colonia, - 'emisor_municipio': obj.municipio, - 'emisor_estado': obj.estado, - 'emisor_pais': obj.pais, - 'emisor_logo': obj.logo, - 'emisor_nombre_comercial': obj.nombre_comercial, - 'emisor_telefono': obj.telefono, - 'emisor_correo': obj.correo, - 'emisor_web': obj.web, - 'es_escuela': obj.es_escuela, - 'es_ong': obj.es_ong, - 'ong_autorizacion': obj.autorizacion, - 'ong_fecha': obj.fecha_autorizacion, - 'ong_fecha_dof': obj.fecha_dof, - 'correo_timbrado': obj.correo_timbrado, - 'token_timbrado': obj.token_timbrado, - 'token_soporte': obj.token_soporte, - 'emisor_registro_patronal': obj.registro_patronal, - 'regimenes': [row.id for row in obj.regimenes] - } - else: - row['emisor'] = {'emisor_rfc': rfc} - - return {'ok': True, 'row': row} - - @classmethod - def get_auth(cls): - try: - obj = Emisor.select()[0] - data = { - 'RFC': obj.rfc, - 'USER': obj.correo_timbrado, - 'PASS': obj.token_timbrado, - 'REPO': obj.token_soporte, - } - return data - except: - return {} - - @classmethod - def get_regimenes(cls): - try: - obj = Emisor.select()[0] - except IndexError: - return () - - rows = [{'id': row.key, 'value': row.name} for row in obj.regimenes] - return tuple(rows) - - def _clean(self, values): - fields = util.clean(values) - fields['rfc'] = fields.pop('emisor_rfc') - fields['curp'] = fields.pop('emisor_curp', '') - fields['nombre'] = fields.pop('emisor_nombre') - fields['codigo_postal'] = fields.pop('emisor_cp') - fields['cp_expedicion'] = fields.pop('emisor_cp2', '') or fields['codigo_postal'] - fields['calle'] = fields.pop('emisor_calle', '') - fields['no_exterior'] = fields.pop('emisor_no_exterior', '') - fields['no_interior'] = fields.pop('emisor_no_interior', '') - fields['colonia'] = fields.pop('emisor_colonia', '') - fields['municipio'] = fields.pop('emisor_municipio', '') - fields['estado'] = fields.pop('emisor_estado', '') - fields['pais'] = fields.pop('emisor_pais', 'México') - fields['logo'] = fields.pop('emisor_logo', '') - fields['nombre_comercial'] = fields.pop('emisor_nombre_comercial', '') - fields['telefono'] = fields.pop('emisor_telefono', '') - fields['correo'] = fields.pop('emisor_correo', '') - fields['web'] = fields.pop('emisor_web', '') - fields['es_escuela'] = bool(fields['es_escuela'].replace('0', '')) - fields['es_ong'] = bool(fields['es_ong'].replace('0', '')) - fields['autorizacion'] = fields.pop('ong_autorizacion', '') - fields['fecha_autorizacion'] = fields.pop('ong_fecha', None) - fields['fecha_dof'] = fields.pop('ong_fecha_dof', None) - if len(fields['rfc']) == 12: - fields['es_moral'] = True - fields['registro_patronal'] = fields.pop('emisor_registro_patronal', '') - fields['regimenes'] = SATRegimenes.get_( - util.loads(fields['regimenes'])) - return fields - - @classmethod - def add(cls, values): - fields = cls._clean(cls, values) - obj, created = Emisor.get_or_create(rfc=fields['rfc']) - obj.regimenes = fields.pop('regimenes') - q = Emisor.update(**fields).where(Emisor.id==obj.id) - return {'ok': bool(q.execute())} - - -class Certificado(BaseModel): - key = BlobField(null=True) - key_enc = TextField(default='') - cer = BlobField(null=True) - cer_pem = TextField(default='') - cer_txt = TextField(default='') - p12 = BlobField(null=True) - serie = TextField(default='') - rfc = TextField(default='') - desde = DateTimeField(null=True) - hasta = DateTimeField(null=True) - - def __str__(self): - return self.serie - - @classmethod - def get_data(cls): - obj = cls.get_(cls) - row = { - 'cert_rfc': obj.rfc, - 'cert_serie': obj.serie, - 'cert_desde': obj.desde, - 'cert_hasta': obj.hasta, - } - return row - - def get_(cls): - return Certificado.select()[0] - - @classmethod - def add(cls, file_obj): - if file_obj.filename.endswith('key'): - path_key = util.save_temp(file_obj.file.read()) - Configuracion.add({'path_key': path_key}) - elif file_obj.filename.endswith('cer'): - path_cer = util.save_temp(file_obj.file.read()) - Configuracion.add({'path_cer': path_cer}) - return {'status': 'server'} - - @classmethod - def validate(cls, values, session): - row = {} - result = False - - obj = cls.get_(cls) - paths = Configuracion.get_({'fields': 'path_cer'}) - cert = util.Certificado(paths) - auth = Emisor.get_auth() - data = cert.validate(values['contra'], session['rfc'], auth) - if data: - msg = 'Certificado guardado correctamente' - q = Certificado.update(**data).where(Certificado.id==obj.id) - if q.execute(): - result = True - row = { - 'cert_rfc': data['rfc'], - 'cert_serie': data['serie'], - 'cert_desde': data['desde'], - 'cert_hasta': data['hasta'], - } - else: - msg = cert.error - - Configuracion.add({'path_key': ''}) - Configuracion.add({'path_cer': ''}) - - return {'ok': result, 'msg': msg, 'data': row} - - - -class Folios(BaseModel): - serie = TextField(unique=True) - inicio = IntegerField(default=1) - default = BooleanField(default=False) - usarcon = TextField(default='') - - class Meta: - order_by = ('-default', 'serie', 'inicio') - indexes = ( - (('serie', 'inicio'), True), - ) - - @classmethod - def get_default(cls): - folio = Folios.select()[0] - return folio.serie - - @classmethod - def get_all(cls): - rows = (Folios - .select( - Folios.id, - Folios.serie.alias('value'), - Folios.usarcon, - ) - .dicts() - ) - return tuple(rows) - - @classmethod - def get_(cls): - rows = (Folios - .select( - Folios.id, - SQL(" '-' AS delete"), - Folios.serie, - Folios.inicio, - case(Folios.usarcon, ( - ('I', 'Ingreso'), - ('E', 'Egreso'), - ('T', 'Traslado'), - ), 'Todos').alias('usarcon'), - case(Folios.default, ( - (True, 'Si'), - (False, 'No'), - )).alias('pre') - ) - .dicts() - ) - return tuple(rows) - - @classmethod - def add(cls, values): - uc = { - '': 'Todos', - 'I': 'Ingreso', - 'E': 'Egreso', - 'T': 'Traslado', - } - pre = { - True: 'Si', - False: 'No', - } - - if 'default' in values: - values['default'] = True - try: - obj = Folios.create(**values) - except IntegrityError: - msg = 'Serie ya existe' - return {'ok': False, 'msg': msg} - row = { - 'id': obj.id, - 'delete' : '-', - 'serie' : obj.serie, - 'inicio' : obj.inicio, - 'usarcon' : uc[obj.usarcon], - 'pre' : pre[obj.default], - } - return {'ok': True, 'row': row} - - @classmethod - def remove(cls, id): - q = Folios.delete().where(Folios.id==id) - return bool(q.execute()) - - -class Categorias(BaseModel): - categoria = TextField() - padre = ForeignKeyField('self', null=True, related_name='hijos') - - class Meta: - order_by = ('categoria',) - indexes = ( - (('categoria', 'padre'), True), - ) - - @classmethod - def exists(cls, filters): - return Categorias.select().where(filters).exists() - - @classmethod - def get_by_id(cls, id): - try: - return Categorias.get(Categorias.id==id) - except: - return None - - @classmethod - def get_all(cls): - rows = (Categorias.select( - Categorias.id, - Categorias.categoria.alias('value'), - Categorias.padre.alias('parent_id')) - ).dicts() - for row in rows: - if row['parent_id'] is None: - row['parent_id'] = 0 - return tuple(rows) - - -class CondicionesPago(BaseModel): - condicion = TextField(unique=True) - - class Meta: - order_by = ('condicion',) - - def __str__(self): - return self.condicion - - @classmethod - def get_(cls): - q = CondicionesPago.select(CondicionesPago.condicion).tuples() - data = [r[0] for r in q] - return data - - @classmethod - def get_or(cls, value): - if value is None: - return value - obj, _ = CondicionesPago.get_or_create(condicion=value) - return obj - - -class SATUnidades(BaseModel): - key = TextField(unique=True, index=True) - name = TextField(default='', index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name') - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return '{} ({})'.format(self.name, self.key) - - @classmethod - def get_by_name(self, name): - try: - return SATUnidades.get(SATUnidades.name==name) - except: - return None - - @classmethod - def get_(self): - rows = SATUnidades.select().dicts() - return tuple(rows) - - @classmethod - def add(self, values): - try: - SATUnidades.create(**values) - return {'ok': True} - except: - return {'ok': False} - - @classmethod - def actualizar(self, values): - id = int(values['id']) - if values['field'] == 'activo': - v = {'0': False, '1': True} - q = (SATUnidades - .update(**{'activo': v[values['value']]}) - .where(SATUnidades.id==id)) - result = bool(q.execute()) - elif values['field'] == 'default': - q = SATUnidades.update(**{'default': False}) - q.execute() - - v = {'false': False, 'true': True} - q = (SATUnidades - .update(**{'default': v[values['value']]}) - .where(SATUnidades.id==id)) - result = bool(q.execute()) - - return {'ok': result} - - @classmethod - def get_default(cls): - obj = SATUnidades.select()[0] - if obj.default: - return obj.id - return 0 - - @classmethod - def get_activos(cls): - rows = (SATUnidades - .select( - SATUnidades.id, - SATUnidades.name.alias('value')) - .where(SATUnidades.activo==True) - .dicts() - ) - return tuple(rows) - - @classmethod - def remove(cls, id): - with database_proxy.transaction(): - try: - q = SATUnidades.delete().where(SATUnidades.id==id) - return bool(q.execute()) - except IntegrityError: - return False - - -class SATFormaPago(BaseModel): - key = TextField(unique=True, index=True) - name = TextField(default='', index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Forma de pago: ({}) {}'.format(self.key, self.name) - - @classmethod - def get_(self): - rows = SATFormaPago.select().dicts() - return tuple(rows) - - @classmethod - def get_by_key(cls, key): - return SATFormaPago.get(SATFormaPago.key==key) - - @classmethod - def get_activos(cls, values): - field = SATFormaPago.id - if values: - field = SATFormaPago.key.alias('id') - rows = (SATFormaPago - .select(field, SATFormaPago.name.alias('value')) - .where(SATFormaPago.activo==True) - .dicts() - ) - return tuple(rows) - - @classmethod - def actualizar(self, values): - id = int(values['id']) - if values['field'] == 'activo': - v = {'0': False, '1': True} - q = (SATFormaPago - .update(**{'activo': v[values['value']]}) - .where(SATFormaPago.id==id)) - result = bool(q.execute()) - elif values['field'] == 'default': - q = SATFormaPago.update(**{'default': False}) - q.execute() - - v = {'false': False, 'true': True} - q = (SATFormaPago - .update(**{'default': v[values['value']]}) - .where(SATFormaPago.id==id)) - result = bool(q.execute()) - - return {'ok': result} - - -class SATAduanas(BaseModel): - key = TextField(unique=True, index=True) - name = TextField(default='', index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name',) - indexes = ( - (('key', 'name'), True), - ) - - -class SATMonedas(BaseModel): - key = TextField(unique=True, index=True) - name = TextField(default='', index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Moneda: ({}) {}'.format(self.key, self.name) - - @classmethod - def add(self, values): - try: - SATMonedas.create(**values) - return {'ok': True} - except: - return {'ok': False} - - @classmethod - def get_(self): - rows = SATMonedas.select().dicts() - return tuple(rows) - - @classmethod - def get_activos(cls): - rows = (SATMonedas - .select( - SATMonedas.key.alias('id'), - SATMonedas.name.alias('value')) - .where(SATMonedas.activo==True) - .dicts() - ) - return tuple(rows) - - @classmethod - def get_activos_by_id(cls): - rows = (SATMonedas - .select( - SATMonedas.id, - SATMonedas.name.alias('value')) - .where(SATMonedas.activo==True) - .dicts() - ) - return tuple(rows) - - @classmethod - def actualizar(self, values): - id = int(values['id']) - if values['field'] == 'activo': - v = {'0': False, '1': True} - q = (SATMonedas - .update(**{'activo': v[values['value']]}) - .where(SATMonedas.id==id)) - result = bool(q.execute()) - elif values['field'] == 'default': - q = SATMonedas.update(**{'default': False}) - q.execute() - - v = {'false': False, 'true': True} - q = (SATMonedas - .update(**{'default': v[values['value']]}) - .where(SATMonedas.id==id)) - result = bool(q.execute()) - - return {'ok': result} - - -class SATImpuestos(BaseModel): - key = TextField(index=True) - name = TextField(default='', index=True) - factor = TextField(default='T') - tipo = TextField(default='T') - tasa = DecimalField(default=0.0, decimal_places=6, auto_round=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name',) - indexes = ( - (('key', 'factor', 'tipo', 'tasa'), True), - ) - - @classmethod - def get_o_crea(self, values): - obj, _ = SATImpuestos.get_or_create(**values) - return obj - - @classmethod - def add(self, values): - tasa = float(values['tasa']) - tipo = 'T' - if tasa < 0: - tipo = 'R' - - row = { - 'key': IMPUESTOS.get(values['impuesto']), - 'name': values['impuesto'], - 'tipo': tipo, - 'tasa': abs(tasa), - } - - try: - obj = SATImpuestos.create(**row) - row['id'] = obj.id - row['delete'] = '-' - return {'ok': True, 'row': row} - except IntegrityError: - return {'ok': False, 'msg': 'El impuesto ya existe'} - - @classmethod - def remove(cls, id): - with database_proxy.transaction(): - try: - q = SATImpuestos.delete().where(SATImpuestos.id==id) - return bool(q.execute()) - except IntegrityError: - return False - - @classmethod - def get_(self): - rows = (SATImpuestos.select( - SATImpuestos.id, - SQL(" '-' AS delete"), - SATImpuestos.name, - SATImpuestos.tipo, - SATImpuestos.tasa, - SATImpuestos.activo, - SATImpuestos.default) - .dicts() - ) - return tuple(rows) - - @classmethod - def get_activos(self): - rows = SATImpuestos.select().where(SATImpuestos.activo==True).dicts() - return tuple(rows) - - @classmethod - def actualizar(self, values): - id = int(values['id']) - if values['field'] == 'activo': - v = {'0': False, '1': True} - q = (SATImpuestos - .update(**{'activo': v[values['value']]}) - .where(SATImpuestos.id==id)) - result = bool(q.execute()) - elif values['field'] == 'default': - q = SATImpuestos.update(**{'default': False}) - q.execute() - - v = {'false': False, 'true': True} - q = (SATImpuestos - .update(**{'default': v[values['value']]}) - .where(SATImpuestos.id==id)) - result = bool(q.execute()) - - return {'ok': result} - - -class SATTipoRelacion(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - - class Meta: - order_by = ('-default', 'name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de relación: ({}) {}'.format(self.key, self.name) - - @classmethod - def get_activos(cls, values): - field = SATTipoRelacion.id - if values: - field = SATTipoRelacion.key.alias('id') - rows = (SATTipoRelacion - .select(field, SATTipoRelacion.name.alias('value')) - .where(SATTipoRelacion.activo==True) - .dicts() - ) - return ({'id': '-', 'value': ''},) + tuple(rows) - - - -class SATBancos(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(index=True) - razon_social = TextField(default='') - rfc = TextField(default='') - activo = BooleanField(default=False) - - class Meta: - order_by = ('-activo', 'name') - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Banco: {} ({})'.format(self.name, self.key) - - @classmethod - def get_(cls): - rows = SATBancos.select().dicts() - return tuple(rows) - - @classmethod - def get_activos_by_id(cls): - rows = (SATBancos - .select( - SATBancos.id, - SATBancos.name.alias('value')) - .where(SATBancos.activo==True) - .dicts() - ) - return tuple(rows) - - @classmethod - def actualizar(cls, values): - id = int(values['id']) - if values['field'] == 'activo': - v = {'0': False, '1': True} - q = (SATBancos - .update(**{'activo': v[values['value']]}) - .where(SATBancos.id==id)) - result = bool(q.execute()) - return {'ok': result} - - @classmethod - def get_by_key(cls, key): - if not key: - return - try: - obj = SATBancos.get(SATBancos.key==key) - return obj - except SATBancos.DoesNotExist: - msg = 'SATBancos no existe: {}'.format(key) - log.error(msg) - return - - -class SATNivelesEducativos(BaseModel): - name = TextField(index=True) - - class Meta: - order_by = ('name',) - - def __str__(self): - return self.name - - @classmethod - def get_by(cls): - rows = SATNivelesEducativos.select( - SATNivelesEducativos.name).tuples() - return tuple([r[0] for r in rows]) - - -class NivelesEducativos(BaseModel): - nombre = TextField() - autorizacion = TextField(default='') - - class Meta: - order_by = ('nombre',) - indexes = ( - (('nombre', 'autorizacion'), True), - ) - - def __str__(self): - return '{} ({})'.format(self.nombre, self.autorizacion) - - @classmethod - def get_all(cls): - rows = NivelesEducativos.select().dicts() - return tuple(rows) - - def _add_group(self, obj): - Grupos.get_or_create(**{'nivel': obj}) - return - - @classmethod - def add(cls, values): - try: - obj = NivelesEducativos.create(**values) - # Revisar - cls._add_group(cls, obj) - result = {'ok': True} - except IntegrityError: - msg = 'Nivel Educativo existente' - result = {'ok': False, 'msg': msg} - return result - - @classmethod - def remove(cls, id): - q = NivelesEducativos.delete().where(NivelesEducativos.id==int(id)) - return bool(q.execute()) - - -class Grupos(BaseModel): - nivel = ForeignKeyField(NivelesEducativos) - grado = TextField(default='') - nombre = TextField(default='') - - class Meta: - order_by = ('nivel', 'grado', 'nombre') - indexes = ( - (('nivel', 'grado', 'nombre'), True), - ) - - def __str__(self): - return '{} {} {}'.format(self.nivel.nombre, self.grado, self.nombre) - - @classmethod - def get_by(cls, values): - rows = (Grupos.select( - Grupos.id.alias('id'), - NivelesEducativos.nombre.alias('value')) - .join(NivelesEducativos) - .switch(Grupos) - .dicts() - ) - return tuple(rows) - - -class CuentasBanco(BaseModel): - de_emisor = BooleanField(default=False) - activa = BooleanField(default=True) - nombre = TextField() - banco = ForeignKeyField(SATBancos) - fecha_apertura = DateField(null=True) - cuenta = TextField(default='') - clabe = TextField(default='') - moneda = ForeignKeyField(SATMonedas) - saldo_inicial = 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) - - class Meta: - order_by = ('nombre',) - indexes = ( - (('banco', 'cuenta'), True), - ) - - def __str__(self): - return '{} ({})'.format(self.banco.name, self.cuenta[-4:]) - - @classmethod - def actualizar_saldo(cls, id, saldo): - fields = {'saldo': saldo} - q = CuentasBanco.update(**fields).where(CuentasBanco.id==id) - return bool(q.execute()) - - @classmethod - def get_saldo(cls, id): - try: - obj = CuentasBanco.get(CuentasBanco.id==id) - return obj.saldo - except CuentasBanco.DoesNotExist: - return 0 - - @classmethod - def remove(cls, id): - try: - with database_proxy.atomic() as txn: - q = MovimientosBanco.delete().where(MovimientosBanco.cuenta==id) - q.execute() - q = CuentasBanco.delete().where(CuentasBanco.id==id) - q.execute() - return True - except: - return False - - @classmethod - def get_years(cls): - data = [{'id': -1, 'value': 'Todos'}] - year1 = (CuentasBanco - .select(fn.Min(CuentasBanco.fecha_apertura.year)) - .where(CuentasBanco.de_emisor==True, CuentasBanco.activa==True) - .group_by(CuentasBanco.fecha_apertura.year) - .order_by(CuentasBanco.fecha_apertura.year) - .scalar() - ) - - if year1: - year2 = util.now().year + 1 - data += [{'id': y, 'value': y} for y in range(int(year1), year2)] - - return data - - @classmethod - def get_(cls, values): - if values['tipo'] == '1': - rows = (CuentasBanco - .select() - .where(CuentasBanco.de_emisor==True, CuentasBanco.activa==True) - ) - if not (len(rows)): - return {'ok': False} - - first = rows[0] - rows = [{'id': r.id, 'value': '{} ({})'.format( - r.banco.name, r.cuenta[-4:])} for r in rows] - data = { - 'ok': True, - 'rows': tuple(rows), - 'moneda': first.moneda.name, - 'saldo': first.saldo, - } - return data - - return - - @classmethod - def emisor(cls): - rows = (CuentasBanco - .select( - CuentasBanco.id, - CuentasBanco.activa, - CuentasBanco.nombre, - SATBancos.name.alias('banco'), - CuentasBanco.fecha_apertura, - CuentasBanco.cuenta, - CuentasBanco.clabe, - SATMonedas.name.alias('moneda'), - CuentasBanco.saldo - ) - .join(SATBancos).switch(CuentasBanco) - .join(SATMonedas).switch(CuentasBanco) - .where(CuentasBanco.de_emisor==True).dicts() - ) - return tuple(rows) - - @classmethod - def add(cls, values): - w = '37137137137137137' - dv = str( - (10 - - sum([(int(v) * int(values['clabe'][i])) % 10 for i, v in enumerate(w)]) - % 10) % 10) - if dv != values['clabe'][-1]: - msg = 'Digito de control de la CLABE es incorrecto' - return {'ok': False, 'msg': msg} - - fecha_deposito = values.pop('fecha_deposito', None) - - with database_proxy.transaction(): - try: - obj = CuentasBanco.create(**values) - except IntegrityError: - msg = 'Esta cuenta ya existe' - return {'ok': False, 'msg': msg} - - nuevo_mov= { - 'cuenta': obj.id, - 'fecha': fecha_deposito, - 'descripcion': 'Saldo inicial', - 'forma_pago': SATFormaPago.get_by_key('99'), - 'deposito': values['saldo'], - 'saldo': values['saldo'], - } - MovimientosBanco.add(nuevo_mov) - - rows = (CuentasBanco - .select( - CuentasBanco.id, - CuentasBanco.activa, - CuentasBanco.nombre, - SATBancos.name.alias('banco'), - CuentasBanco.fecha_apertura, - CuentasBanco.cuenta, - CuentasBanco.clabe, - SATMonedas.name.alias('moneda'), - CuentasBanco.saldo - ) - .join(SATBancos).switch(CuentasBanco) - .join(SATMonedas).switch(CuentasBanco) - .where(CuentasBanco.id==obj.id).dicts() - ) - data = {'ok': True, 'row': rows[0]} - 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',) - - def _ultimo_saldo(self, cuenta, fecha): - query = (MovimientosBanco - .select() - .where( - (MovimientosBanco.cuenta==cuenta) & - (MovimientosBanco.fecharow.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 add(cls, values): - ids = values.pop('ids', '') - actualizar = False - if 'saldo' in values: - saldo = values['saldo'] - else: - actualizar = True - hora = values.pop('hora') - values['fecha'] = '{}T{}'.format(values['fecha'][:10], hora) - values['cuenta'] = int(values['cuenta']) - values['retiro'] = util.get_float(values['retiro']) - values['deposito'] = util.get_float(values['deposito']) - values['forma_pago'] = int(values['forma_pago']) - - ultimo_saldo = cls._ultimo_saldo( - cls, 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 = cls._actualizar_saldos(cls, obj) - if ids: - FacturasPagos.add(obj, util.loads(ids)) - - return {'ok': True, 'saldo': saldo} - - @classmethod - def remove(cls, id): - try: - obj = MovimientosBanco.get(MovimientosBanco.id==id) - except MovimientosBanco.DoesNotExist: - return False - - if obj.conciliado or obj.cancelado: - return False - - with database_proxy.transaction(): - obj.cancelado = True - obj.save() - FacturasPagos.cancelar(obj) - - obj = cls._movimiento_anterior(cls, obj.cuenta, obj.fecha) - cls._actualizar_saldos(cls, obj) - - return True - - @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 = 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, - MovimientosBanco.descripcion, - MovimientosBanco.retiro, - MovimientosBanco.deposito, - MovimientosBanco.saldo) - .where(filtros) - .dicts() - ) - - return {'ok': True, 'rows': rows} - - -class CfdiPagos(BaseModel): - movimiento = ForeignKeyField(MovimientosBanco) - fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) - fecha_timbrado = DateTimeField(null=True) - xml = TextField(default='') - uuid = UUIDField(null=True) - estatus = TextField(default='Guardado') - estatus_sat = TextField(default='') - notas = TextField(default='') - cancelado = BooleanField(default=False) - - class Meta: - order_by = ('movimiento',) - - -class SATUsoCfdi(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=False) - default = BooleanField(default=False) - fisica = BooleanField(default=True) - moral = BooleanField(default=False) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Uso del CFDI: {} ({})'.format(self.name, self.key) - - @classmethod - def actualizar(self, values): - id = int(values['id']) - if values['field'] == 'activo': - v = {'0': False, '1': True} - q = (SATUsoCfdi - .update(**{'activo': v[values['value']]}) - .where(SATUsoCfdi.id==id)) - result = bool(q.execute()) - return {'ok': result} - - @classmethod - def get_all(self): - rows = SATUsoCfdi.select().dicts() - return tuple(rows) - - @classmethod - def get_id(self, key): - if key is None: - return - return SATUsoCfdi.get(SATUsoCfdi.key==key).id - - @classmethod - def get_key(self, id): - if id is None: - return - return SATUsoCfdi.get(SATUsoCfdi.id==id).key - - @classmethod - def get_activos(cls): - rows = (SATUsoCfdi - .select( - SATUsoCfdi.key.alias('id'), - SATUsoCfdi.name.alias('value'), - SATUsoCfdi.fisica, - SATUsoCfdi.moral, - ) - .where(SATUsoCfdi.activo==True) - .dicts() - ) - return tuple(rows) - - -class SATEstados(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - pais = TextField(default='') - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name', 'pais'), True), - ) - - def __str__(self): - return 'Estado: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATEstados.get(SATEstados.key==key) - return obj - except SATEstados.DoesNotExist: - msg = 'SATEstados no existe: {}'.format(key) - log.error(msg) - return - - -class SATOrigenRecurso(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Origen Recurso: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATOrigenRecurso.get(SATOrigenRecurso.key==key) - return obj - except SATOrigenRecurso.DoesNotExist: - msg = 'SATOrigenRecurso no existe: {}'.format(key) - log.error(msg) - return - - -class SATPeriodicidadPago(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Periodicidad de Pago: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATPeriodicidadPago.get(SATPeriodicidadPago.key==key) - return obj - except SATPeriodicidadPago.DoesNotExist: - msg = 'SATPeriodicidadPago no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoContrato(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Contrato: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoContrato.get(SATTipoContrato.key==key) - return obj - except SATTipoContrato.DoesNotExist: - msg = 'SATTipoContrato no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoDeduccion(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - clave = TextField(default='') - nombre = TextField(default='') - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Deducción: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoDeduccion.get(SATTipoDeduccion.key==key) - return obj - except SATTipoDeduccion.DoesNotExist: - msg = 'SATTipoDeduccion no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoHoras(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Horas: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoHoras.get(SATTipoHoras.key==key) - return obj - except SATTipoHoras.DoesNotExist: - msg = 'SATTipoHoras no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoIncapacidad(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Incapacidad: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoIncapacidad.get(SATTipoIncapacidad.key==key) - return obj - except SATTipoIncapacidad.DoesNotExist: - msg = 'SATTipoIncapacidad no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoJornada(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Jornada: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoJornada.get(SATTipoJornada.key==key) - return obj - except SATTipoJornada.DoesNotExist: - msg = 'SATTipoJornada no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoNomina(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Nómina: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoNomina.get(SATTipoNomina.key==key) - return obj - except SATTipoNomina.DoesNotExist: - msg = 'SATTipoNomina no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoOtroPago(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - clave = TextField(default='') - nombre = TextField(default='') - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Otro Pago: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoOtroPago.get(SATTipoOtroPago.key==key) - return obj - except SATTipoOtroPago.DoesNotExist: - msg = 'SATTipoOtroPago no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoPercepcion(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - clave = TextField(default='') - nombre = TextField(default='') - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Tipo de Percepción: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoPercepcion.get(SATTipoPercepcion.key==key) - return obj - except SATTipoPercepcion.DoesNotExist: - msg = 'SATTipoPercepcion no existe: {}'.format(key) - log.error(msg) - return - - -class SATTipoRegimen(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Regimen de contratación: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATTipoRegimen.get(SATTipoRegimen.key==key) - return obj - except SATTipoRegimen.DoesNotExist: - msg = 'SATTipoRegimen no existe: {}'.format(key) - log.error(msg) - return - - -class SATRiesgoPuesto(BaseModel): - key = TextField(index=True, unique=True) - name = TextField(default='', index=True) - activo = BooleanField(default=True) - - class Meta: - order_by = ('name',) - indexes = ( - (('key', 'name'), True), - ) - - def __str__(self): - return 'Riesgo Puesto: {} ({})'.format(self.name, self.key) - - @classmethod - def get_by_key(cls, key): - try: - obj = SATRiesgoPuesto.get(SATRiesgoPuesto.key==key) - return obj - except SATRiesgoPuesto.DoesNotExist: - msg = 'SATRiesgoPuesto no existe: {}'.format(key) - log.error(msg) - return - - -class TipoCambio(BaseModel): - dia = DateField(default=util.now) - moneda = ForeignKeyField(SATMonedas) - tipo_cambio = DecimalField(max_digits=15, decimal_places=6, auto_round=True) - - class Meta: - order_by = ('-dia',) - - -class Addendas(BaseModel): - nombre = TextField(unique=True) - addenda = TextField() - - class Meta: - order_by = ('nombre',) - - -class Socios(BaseModel): - tipo_persona = IntegerField(default=1) - rfc = TextField(index=True) - nombre = TextField(index=True) - slug = TextField(default='') - nombre_comercial = TextField(index=True, default='') - calle = TextField(default='') - no_exterior = TextField(default='') - no_interior = TextField(default='') - colonia = TextField(default='') - municipio = TextField(default='') - estado = TextField(default='') - pais = TextField(default='') - codigo_postal = TextField(default='') - notas = TextField(default='') - telefonos = TextField(default='') - es_activo = BooleanField(default=True) - es_ong = BooleanField(default=False) - fecha_alta = DateField(default=util.now) - dias_pago = IntegerField(default=0) - dias_habiles = BooleanField(default=False) - es_cliente = BooleanField(default=False) - es_proveedor = BooleanField(default=False) - cuenta_cliente = TextField(default='') - cuenta_proveedor = TextField(default='') - saldo_cliente = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - saldo_proveedor = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - web = TextField(default='') - correo_facturas = TextField(default='') - forma_pago = ForeignKeyField(SATFormaPago, null=True) - condicion_pago = ForeignKeyField(CondicionesPago, null=True) - addenda = ForeignKeyField(Addendas, null=True) - uso_cfdi = ForeignKeyField(SATUsoCfdi, null=True) - tags = ManyToManyField(Tags, related_name='socios_tags') - - def __str__(self): - t = '{} ({})' - return t.format(self.nombre, self.rfc) - - class Meta: - order_by = ('nombre',) - indexes = ( - (('rfc', 'slug'), True), - ) - - def _clean(self, values): - fields = util.clean(values) - fields['rfc'] = fields['rfc'].upper() - fields['nombre'] = util.spaces(fields['nombre']) - fields['slug'] = util.to_slug(fields['nombre']) - 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)) - - fb = ('dias_habiles', 'es_activo', 'es_cliente', - 'es_proveedor', 'es_ong') - for name in fb: - fields[name] = bool(fields[name].replace('0', '')) - return fields - - @classmethod - def get_(cls, values): - #~ print ('values', values) - id = values.get('id', 0) - if id: - id = int(values['id']) - row = Socios.select().where(Socios.id==id).dicts()[0] - row['uso_cfdi_socio'] = SATUsoCfdi.get_key(row.pop('uso_cfdi')) - if not row['condicion_pago'] is None: - row['condicion_pago'] = \ - str(CondicionesPago.get(id=row['condicion_pago'])) - return row - - #~ return {'data': data['rows'][:100], 'pos':0, 'total_count': 1300} - #~ start = 0 - #~ count = 0 - #~ end = 100 - #~ if values: - #~ {'start': '100', 'count': '100', 'continue': 'true'} - #~ start = int(values['start']) - #~ cont = int(values['count']) - #~ end = start + count - - total = Socios.select().count() - - rows = (Socios - .select( - Socios.id, - Socios.rfc, - Socios.nombre, - Socios.saldo_cliente) - .dicts() - ) - return {'pos': 0, 'total_count': total, 'data': tuple(rows)} - - @classmethod - def get_by_client(cls, values): - id = int(values.get('id', 0)) - if id: - row = (Socios - .select( - Socios.id, Socios.nombre, Socios.rfc, - SATFormaPago.key.alias('forma_pago'), - SATUsoCfdi.key.alias('uso_cfdi')) - .join(SATFormaPago, JOIN.LEFT_OUTER).switch(Socios) - .join(SATUsoCfdi, JOIN.LEFT_OUTER).switch(Socios) - .where((Socios.id==id) & (Socios.es_cliente==True)) - .dicts() - ) - if len(row): - return {'ok': True, 'row': row[0]} - return {'ok': False} - - name = values.get('name', '') - if name: - rows = (Socios - .select(Socios.id, Socios.nombre, Socios.rfc, - SATFormaPago.key.alias('forma_pago'), - SATUsoCfdi.key.alias('uso_cfdi')) - .join(SATFormaPago, JOIN.LEFT_OUTER).switch(Socios) - .join(SATUsoCfdi, JOIN.LEFT_OUTER).switch(Socios) - .where((Socios.es_cliente==True) & - (Socios.rfc.contains(name) | - Socios.nombre.contains(name))) - .dicts()) - return tuple(rows) - return {'ok': False} - - @classmethod - def add(cls, values): - fields = cls._clean(cls, values) - try: - obj = Socios.create(**fields) - except IntegrityError as e: - msg = 'Ya existe el RFC y Razón Social' - data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} - return data - - #~ 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 - - @classmethod - def actualizar(cls, values, id): - fields = cls._clean(cls, values) - try: - q = Socios.update(**fields).where(Socios.id==id) - q.execute() - except IntegrityError: - msg = 'Ya existe el RFC y Razón Social' - data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} - return data - - row = { - 'id': id, - 'rfc': fields['rfc'], - 'nombre': fields['nombre'], - } - data = {'ok': True, 'row': row, 'new': False} - return data - - @classmethod - def remove(cls, id): - count = (Facturas - .select(fn.COUNT(Facturas.id)).join(Socios) - .where(Socios.id==id) - .count()) - if count: - return False - - q = Socios.delete().where(Socios.id==id) - return bool(q.execute()) - - -class Contactos(BaseModel): - socio = ForeignKeyField(Socios) - titulo = ForeignKeyField(TipoTitulo) - foto = TextField(default='') - nombre = TextField(index=True) - paterno = TextField(index=True) - materno = TextField(default='') - fecha_nacimiento = DateField(null=True) - notas = TextField(default='') - - class Meta: - order_by = ('socio', 'nombre') - indexes = ( - (('socio', 'nombre', 'paterno', 'materno'), True), - ) - - -class ContactoDirecciones(BaseModel): - contacto = ForeignKeyField(Contactos) - tipo = ForeignKeyField(TipoDireccion) - direccion = TextField() - - class Meta: - order_by = ('contacto',) - indexes = ( - (('contacto', 'tipo', 'direccion'), True), - ) - - -class ContactoTelefonos(BaseModel): - contacto = ForeignKeyField(Contactos) - tipo = ForeignKeyField(TipoTelefono) - telefono = TextField() - - class Meta: - order_by = ('contacto',) - indexes = ( - (('contacto', 'tipo', 'telefono'), True), - ) - - -class ContactoCorreos(BaseModel): - contacto = ForeignKeyField(Contactos) - tipo = ForeignKeyField(TipoCorreo) - correo = TextField() - - class Meta: - order_by = ('contacto',) - indexes = ( - (('contacto', 'tipo', 'correo'), True), - ) - - -class Alumnos(BaseModel): - rfc = TextField(null=True) - curp = TextField(index=True, unique=True) - foto = TextField(default='') - nombre = TextField(index=True) - paterno = TextField(index=True) - materno = TextField(default='') - calle = TextField(default='') - no_exterior = TextField(default='') - no_interior = TextField(default='') - colonia = TextField(default='') - municipio = TextField(default='') - estado = TextField(default='') - pais = TextField(default='') - codigo_postal = TextField(default='') - notas = TextField(default='') - telefonos = TextField(default='') - correos = TextField(default='') - es_activo = BooleanField(default=True) - fecha_alta = DateField(default=util.now) - fecha_nacimiento = DateField(null=True) - factura = ForeignKeyField(Socios, null=True) - grupo = ForeignKeyField(Grupos, null=True) - - def __str__(self): - t = '{} {} {}' - return t.format(self.nombre, self.paterno, self.materno) - - class Meta: - order_by = ('nombre', 'paterno') - - def _clean(self, values): - fields = util.clean(util.loads(values)) - fields['rfc'] = fields['rfc'].upper().strip() - fields['curp'] = fields['curp'].upper() - fields['nombre'] = util.spaces(fields['nombre']) - fields['paterno'] = util.spaces(fields['paterno']) - fields['materno'] = util.spaces(fields['materno']) - return fields - - def _get(self, where): - rows = (Alumnos - .select() - .where(where) - .dicts() - ) - return tuple(rows) - - @classmethod - def get_by_name(cls, values): - rows = () - name = values.get('name', '') - if name: - rows = (Alumnos - .select( - Alumnos.id, - Alumnos.nombre, - Alumnos.paterno, - Alumnos.materno, - Alumnos.rfc) - .where((Alumnos.es_activo==True) & - (Alumnos.nombre.contains(name) | - Alumnos.paterno.contains(name) | - Alumnos.materno.contains(name) | - Alumnos.rfc.contains(name))) - .dicts()) - rows = tuple(rows) - - return rows - - @classmethod - def get_by(cls, values): - if 'id' in values: - id = int(values['id']) - w = (Alumnos.id==id) - rows = cls._get(cls, w) - return rows[0] - - if not values: - w = None - - return cls._get(cls, w) - - @classmethod - def add(cls, values): - fields = cls._clean(cls, values) - try: - obj = Alumnos.create(**fields) - except IntegrityError as e: - msg = 'Ya existe un alumno con este CURP' - data = {'ok': False, 'msg': msg} - return data - - data = {'ok': True} - return data - - @classmethod - def actualizar(cls, values): - fields = cls._clean(cls, values) - id = int(fields.pop('id')) - try: - q = Alumnos.update(**fields).where(Alumnos.id==id) - q.execute() - except IntegrityError: - msg = 'Ya existe un Alumno con este CURP' - data = {'ok': False, 'msg': msg} - return data - - data = {'ok': True} - return data - - @classmethod - def remove(cls, id): - q = Alumnos.delete().where(Alumnos.id==id) - return bool(q.execute()) - - -class AlumnosParientes(BaseModel): - alumno = ForeignKeyField(Alumnos) - tipo_pariente = ForeignKeyField(TipoPariente) - foto = TextField(default='') - nombre = TextField(index=True) - paterno = TextField(index=True) - materno = TextField(default='') - fecha_nacimiento = DateField(null=True) - puede_recoger = BooleanField(default=False) - - class Meta: - order_by = ('alumno',) - - -class ParienteDirecciones(BaseModel): - pariente = ForeignKeyField(AlumnosParientes) - tipo = ForeignKeyField(TipoDireccion) - direccion = TextField() - - class Meta: - order_by = ('pariente',) - - -class ParienteTelefonos(BaseModel): - pariente = ForeignKeyField(AlumnosParientes) - tipo = ForeignKeyField(TipoTelefono) - telefono = TextField() - - class Meta: - order_by = ('pariente',) - - -class ParienteCorreos(BaseModel): - pariente = ForeignKeyField(AlumnosParientes) - tipo = ForeignKeyField(TipoCorreo) - correo = TextField() - - class Meta: - order_by = ('pariente',) - - -class Almacenes(BaseModel): - nombre = TextField(default='') - ubicacion = TextField(default='') - - class Meta: - order_by = ('nombre',) - - -class Productos(BaseModel): - almacen = ForeignKeyField(Almacenes, null=True) - categoria = ForeignKeyField(Categorias, null=True) - clave = TextField(unique=True, index=True) - clave_sat = TextField(default='') - descripcion = TextField(index=True) - unidad = ForeignKeyField(SATUnidades) - valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - ultimo_costo = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - inventario = BooleanField(default=False) - existencia = DecimalField(default=0.0, max_digits=18, decimal_places=2, - auto_round=True) - minimo = DecimalField(default=0.0, max_digits=18, decimal_places=2, - auto_round=True) - codigo_barras = TextField(default='') - cuenta_predial = TextField(default='') - es_activo = BooleanField(default=True) - impuestos = ManyToManyField(SATImpuestos, related_name='productos') - tags = ManyToManyField(Tags, related_name='productos_tags') - - class Meta: - order_by = ('descripcion',) - - @classmethod - def next_key(cls): - try: - with database_proxy.transaction(): - value = (Productos - .select(fn.Max(cast(Productos.clave, 'int')).alias('fm')) - .order_by(SQL('fm')) - .scalar()) - except Exception as e: - values = (Productos - .select(Productos.clave) - .order_by(Productos.clave) - .tuples() - ) - values = [int(v[0]) for v in values if v[0].isdigit()] - value = 0 - if values: - value = max(values) - - value = value or 0 - value += 1 - return {'value': value} - - @classmethod - def get_by_key(cls, values): - clave = values.get('key', '') - row = (Productos - .select( - Productos.id, - Productos.clave, - Productos.clave_sat, - Productos.descripcion, - SATUnidades.name.alias('unidad'), - Productos.valor_unitario, - Productos.descuento) - .join(SATUnidades).switch(Productos) - .where((Productos.es_activo==True) & - ((Productos.clave==clave) | (Productos.codigo_barras==clave))) - .dicts() - ) - if len(row): - id = row[0]['id'] - model_pt = Productos.impuestos.get_through_model() - taxes = tuple(model_pt - .select( - model_pt.productos_id.alias('product'), - model_pt.satimpuestos_id.alias('tax')) - .where(model_pt.productos_id==id).dicts()) - return {'ok': True, 'row': row[0], 'taxes': taxes} - - return {'ok': False} - - def _validate_import(self, product): - product['categoria'] = Categorias.get_by_id(int(product['categoria'])) - product['unidad'] = SATUnidades.get_by_name(product['unidad']) - product['inventario'] = bool(product['inventario']) - if not product['inventario']: - product['existencia'] = 0.0 - - impuestos = product['impuestos'].split('|') - if not impuestos: - taxes = [SATImpuestos.select().where(SATImpuestos.id==6)] - else: - taxes = [] - for i in range(0, len(impuestos), 3): - w = ( - (SATImpuestos.key == impuestos[i]) & - (SATImpuestos.name == impuestos[i+1]) & - (SATImpuestos.tasa == float(impuestos[i+2])) - ) - try: - taxes.append(SATImpuestos.get(w)) - except: - pass - - product['impuestos'] = taxes - w = (Productos.clave==product['clave']) - return product, w - - def _import(self): - emisor = Emisor.select()[0] - rows, msg = util.import_products(emisor.rfc) - if not rows: - return {'ok': False, 'msg': msg} - - cs = 0 - np = 0 - ap = 0 - for p in rows: - data, w = self._validate_import(self, p) - if data['unidad'] is None: - msg = 'Producto: {} - No se encontró la unidad'.format( - data['clave']) - log.error(msg) - continue - - result = util.get_sat_key('productos', data['clave_sat']) - if not result['ok']: - msg = 'Producto: {} - Clave SAT incorrecta: {}'.format( - data['clave'], data['clave_sat']) - log.error(msg) - cs += 1 - continue - - # ~ print (data) - taxes = data.pop('impuestos') - try: - with database_proxy.transaction(): - if Productos.select().where(w).exists(): - q = Productos.update(**data).where(w) - q.execute() - obj = Productos.get(w) - obj.impuestos = taxes - log.info('\tProducto actualizado: {}'.format(data['clave'])) - ap += 1 - else: - obj = Productos.create(**data) - obj.impuestos = taxes - log.info('\tProducto agregado: {}'.format(data['clave'])) - np += 1 - except Exception as e: - msg = 'Error al importar producto: {}'.format(data['clave']) - log.error(msg) - log.error(e) - - msg = 'Productos encontrados: {}
'.format(len(rows)) - msg += 'Productos agregados: {}
'.format(np) - msg += 'Productos actualizados: {}
'.format(ap) - msg += 'Productos con problemas: {}
'.format(len(rows) - np - ap) - msg += 'Productos con clave SAT erronea: {}'.format(cs) - return {'ok': True, 'msg': msg} - - @classmethod - def opt(cls, values): - if values['opt'] == 'import': - return cls._import(cls) - - return {'ok': False, 'msg': 'Sin opción'} - - @classmethod - def get_by(cls, values): - clave = values.get('id', '') - if clave: - row = (Productos - .select( - Productos.id, - Productos.clave, - Productos.clave_sat, - Productos.descripcion, - SATUnidades.name.alias('unidad'), - Productos.valor_unitario, - Productos.descuento) - .join(SATUnidades).switch(Productos) - .where((Productos.es_activo==True) & - ((Productos.id==clave) | (Productos.clave==clave))) - .dicts()) - if len(row): - id = row[0]['id'] - model_pt = Productos.impuestos.get_through_model() - taxes = tuple(model_pt - .select( - model_pt.productos_id.alias('product'), - model_pt.satimpuestos_id.alias('tax')) - .where(model_pt.productos_id==id).dicts()) - return {'ok': True, 'row': row[0], 'taxes': taxes} - - return {'ok': False} - - name = values.get('name', '') - if name: - rows = (Productos - .select( - Productos.id, - Productos.clave, - Productos.clave_sat, - Productos.descripcion, - SATUnidades.name.alias('unidad'), - Productos.valor_unitario) - .join(SATUnidades) - .switch(Productos) - .where((Productos.es_activo==True) & - ((Productos.descripcion.contains(name)) | - (Productos.clave.contains(name)))) - .dicts() - ) - return tuple(rows) - return {'ok': False} - - @classmethod - def get_(cls, values): - if values: - id = int(values['id']) - row = (Productos - .select( - Productos.id, - Productos.es_activo.alias('es_activo_producto'), - Productos.categoria, - Productos.clave, - Productos.clave_sat, - Productos.descripcion, - Productos.unidad, - Productos.valor_unitario, - Productos.cuenta_predial, - Productos.inventario, - Productos.existencia, - Productos.minimo, - ) - .where(Productos.id==id).dicts()[0] - ) - obj = Productos.get(Productos.id==id) - taxes = [row.id for row in obj.impuestos] - return {'row': row, 'taxes': taxes} - - rows = (Productos - .select( - Productos.id, - Productos.es_activo, - Productos.clave_sat, - Productos.clave, - Productos.descripcion, - SATUnidades.name.alias('unidad'), - Productos.valor_unitario) - .join(SATUnidades) - .dicts() - ) - return {'ok': True, 'rows': tuple(rows)} - - def _clean(self, values): - taxes = util.loads(values.pop('taxes')) - descripcion = util.spaces(values.pop('descripcion')) - fields = util.clean(values) - - fields.pop('precio_con_impuestos', '') - fields['es_activo'] = fields.pop('es_activo_producto') - fields['descripcion'] = descripcion - fields['unidad'] = int(fields['unidad']) - fields['valor_unitario'] = fields['valor_unitario'].replace( - '$', '').replace(',', '') - - fb = ('es_activo', 'inventario') - for name in fb: - fields[name] = bool(fields[name].replace('0', '')) - - return fields, taxes - - @classmethod - def add(cls, values): - fields, taxes = cls._clean(cls, values) - - if Productos.select().where(Productos.clave==fields['clave']).exists(): - msg = 'Clave ya existe' - return {'ok': False, 'msg': msg} - - obj_taxes = SATImpuestos.select().where(SATImpuestos.id.in_(taxes)) - - with database_proxy.transaction(): - obj = Productos.create(**fields) - obj.impuestos = obj_taxes - row = { - 'id': obj.id, - 'es_activo': obj.es_activo, - 'clave': obj.clave, - 'clave_sat': obj.clave_sat, - 'descripcion': obj.descripcion, - 'unidad': obj.unidad.name, - 'valor_unitario': obj.valor_unitario, - } - data = {'ok': True, 'row': row, 'new': True} - return data - - @classmethod - def actualizar(cls, values, id): - if not 'cuenta_predial' in values: - values['cuenta_predial'] = '' - fields, taxes = cls._clean(cls, values) - obj_taxes = SATImpuestos.select().where(SATImpuestos.id.in_(taxes)) - with database_proxy.transaction(): - q = Productos.update(**fields).where(Productos.id==id) - try: - q.execute() - except IntegrityError: - msg = 'Ya existe un producto con esta clave' - data = {'ok': False, 'row': {}, 'new': False, 'msg': msg} - return data - - obj = Productos.get(Productos.id==id) - obj.impuestos = obj_taxes - row = { - 'id': obj.id, - 'es_activo': obj.es_activo, - 'clave': obj.clave, - 'clave_sat': obj.clave_sat, - 'descripcion': obj.descripcion, - 'unidad': obj.unidad.name, - 'valor_unitario': obj.valor_unitario, - } - data = {'ok': True, 'row': row, 'new': False} - return data - - @classmethod - def remove(cls, id): - count = (FacturasDetalle - .select(fn.COUNT(FacturasDetalle.id)).join(Productos) - .where(Productos.id==id) - .count() - ) - if count: - return False - - with database_proxy.transaction(): - obj = Productos.get(Productos.id==id) - obj.impuestos.clear() - obj.tags.clear() - q = Productos.delete().where(Productos.id==id) - return bool(q.execute()) - - -class RangosPrecios(BaseModel): - producto = ForeignKeyField(Productos) - descripcion = TextField(default='') - desde = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - hasta = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - descuento = IntegerField(default=0) - - class Meta: - order_by = ('producto',) - - -class Facturas(BaseModel): - cliente = ForeignKeyField(Socios) - version = TextField(default=CURRENT_CFDI) - serie = TextField(default='') - folio = IntegerField(default=0) - fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) - fecha_timbrado = DateTimeField(null=True) - forma_pago = TextField(default='') - condiciones_pago = TextField(default='') - subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - moneda = TextField(default='MXN') - tipo_cambio = DecimalField(default=1.0, max_digits=15, decimal_places=6, - auto_round=True) - total = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_mn = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - tipo_comprobante = TextField(default='I') - metodo_pago = TextField(default='PUE') - lugar_expedicion = TextField(default='') - confirmacion = TextField(default='') - uso_cfdi = TextField(default='') - total_retenciones = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - total_trasladados = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - xml = TextField(default='') - uuid = UUIDField(null=True) - estatus = TextField(default='Guardada') - estatus_sat = TextField(default='Vigente') - regimen_fiscal = TextField(default='') - notas = TextField(default='') - saldo = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - pagada = BooleanField(default=False) - cancelada = BooleanField(default=False) - fecha_cancelacion = DateTimeField(null=True) - acuse = TextField(default='') - donativo = BooleanField(default=False) - anticipo = BooleanField(default=False) - egreso_anticipo = BooleanField(default=False) - tipo_relacion = TextField(default='') - error = TextField(default='') - - class Meta: - order_by = ('fecha',) - - @classmethod - def cancel(cls, id): - if CANCEL_SIGNATURE: - return cls._cancel_signature(cls, id) - return cls._cancel_xml(cls, id) - - def _cancel_xml(self, id): - msg = 'Factura cancelada correctamente' - auth = Emisor.get_auth() - certificado = Certificado.select()[0] - obj = Facturas.get(Facturas.id==id) - - if obj.version == '3.2': - msg = 'No es posible cancelar CFDI 3.2' - return {'ok': False, 'msg': msg} - - data, result = util.cancel_xml(auth, obj.uuid, certificado) - if data['ok']: - obj.estatus = 'Cancelada' - obj.error = '' - obj.cancelada = True - obj.fecha_cancelacion = result['Fecha'] - obj.acuse = result['Acuse'] - self._actualizar_saldo_cliente(self, obj, True) - else: - obj.error = data['msg'] - obj.save() - return data - - def _cancel_signature(self, id): - msg = 'Factura cancelada correctamente' - auth = Emisor.get_auth() - certificado = Certificado.select()[0] - obj = Facturas.get(Facturas.id==id) - data, result = util.cancel_signature( - obj.uuid, certificado.p12, certificado.rfc, auth) - if data['ok']: - obj.estatus = 'Cancelada' - obj.error = '' - obj.cancelada = True - obj.fecha_cancelacion = result['Fecha'] - obj.acuse = result['Acuse'] - self._actualizar_saldo_cliente(self, obj, True) - else: - obj.error = data['msg'] - obj.save() - return data - - @classmethod - def filter_years(cls): - data = [{'id': -1, 'value': 'Todos'}] - rows = (Facturas - .select(Facturas.fecha.year.alias('year')) - .group_by(Facturas.fecha.year) - .order_by(Facturas.fecha.year) - ) - if not rows is None: - data += [{'id': int(r.year), 'value': int(r.year)} for r in rows] - return tuple(data) - - @classmethod - def get_xml(cls, id): - obj = Facturas.get(Facturas.id==id) - name = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc) - cls._sync_xml(cls, obj) - return obj.xml, name - - @classmethod - def get_notes(cls, id): - obj = Facturas.get(Facturas.id==int(id)) - return {'notes': obj.notas} - - @classmethod - def save_notes(cls, values): - obj = Facturas.get(Facturas.id==int(values['id'])) - obj.notas = values.get('notes', '') - obj.save() - return {'ok': True, 'msg': 'Notas guardadas correctamente'} - - #~ Revisar - def _get_data_cfdi_to_pdf(self, xml, cancel, version): - pre_nomina = PRE['NOMINA'][version] - - data['nomina'] = {} - node = doc.find('{}Complemento/{}Nomina'.format(pre, pre_nomina)) - if not node is None: - data['nomina']['nomina'] = node.attrib.copy() - subnode = node.find('{}Emisor'.format(pre_nomina)) - if not subnode is None: - data['emisor'].update(subnode.attrib.copy()) - subnode = node.find('{}Receptor'.format(pre_nomina)) - data['receptor'].update(subnode.attrib.copy()) - - subnode = node.find('{}Percepciones'.format(pre_nomina)) - data['nomina']['percepciones'] = subnode.attrib.copy() - detalle = [] - for n in subnode.getchildren(): - if 'SeparacionIndemnizacion' in n.tag: - continue - detalle.append(n.attrib.copy()) - data['nomina']['percepciones']['detalle'] = detalle - - data['nomina']['deducciones'] = None - subnode = node.find('{}Deducciones'.format(pre_nomina)) - if not subnode is None: - data['nomina']['deducciones'] = subnode.attrib.copy() - detalle = [] - for n in subnode.getchildren(): - detalle.append(n.attrib.copy()) - data['nomina']['deducciones']['detalle'] = detalle - - data['nomina']['incapacidades'] = None - subnode = node.find('{}Incapacidades'.format(pre_nomina)) - if not subnode is None: - detalle = [] - for n in subnode.getchildren(): - detalle.append(n.attrib.copy()) - data['nomina']['incapacidades'] = detalle - - data['nomina']['otrospagos'] = None - subnode = node.find('{}OtrosPagos'.format(pre_nomina)) - if not subnode is None: - data['nomina']['otrospagos'] = subnode.attrib.copy() - detalle = [] - for n in subnode.getchildren(): - detalle.append(n.attrib.copy()) - ns = n.find('{}SubsidioAlEmpleo'.format(pre_nomina)) - if not ns is None: - data['nomina']['otrospagos']['SubsidioCausado'] = ns.attrib['SubsidioCausado'] - data['nomina']['otrospagos']['detalle'] = detalle - - return data - - def _get_not_in_xml(self, invoice, emisor): - values = {} - - values['notas'] = invoice.notas - values['fechadof'] = str(emisor.fecha_dof) - if invoice.version == '3.2': - return values - - obj = SATRegimenes.get(SATRegimenes.key==invoice.regimen_fiscal) - values['regimenfiscal'] = str(obj) - - obj = SATUsoCfdi.get(SATUsoCfdi.key==invoice.uso_cfdi) - values['usocfdi'] = str(obj) - - mp = { - 'PUE': 'Pago en una sola exhibición', - 'PPD': 'Pago en parcialidades o diferido', - } - values['metododepago'] = 'Método de Pago: ({}) {}'.format( - invoice.metodo_pago, mp[invoice.metodo_pago]) - - obj = SATFormaPago.get(SATFormaPago.key==invoice.forma_pago) - values['formadepago'] = str(obj) - - obj = SATMonedas.get(SATMonedas.key==invoice.moneda) - values['moneda'] = str(obj) - - if invoice.tipo_relacion: - obj = SATTipoRelacion.get(SATTipoRelacion.key==invoice.tipo_relacion) - values['tiporelacion'] = str(obj) - - return values - - @classmethod - def get_pdf(cls, id, rfc, sync=True): - try: - emisor = Emisor.select()[0] - except IndexError: - return b'', 'sin_datos_de_emisor.pdf' - - obj = Facturas.get(Facturas.id==id) - name = '{}{}_{}.pdf'.format(obj.serie, obj.folio, obj.cliente.rfc) - if obj.uuid is None: - return b'', name - - values = cls._get_not_in_xml(cls, obj, emisor) - data = util.get_data_from_xml(obj, values) - doc = util.to_pdf(data, emisor.rfc) - - if sync: - target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/') - cls._sync_pdf(cls, doc, name, target) - - return doc, name - - @classmethod - def get_ods(cls, id, rfc): - try: - emisor = Emisor.select()[0] - except IndexError: - return b'', 'sin_datos_de_emisor.pdf' - - obj = Facturas.get(Facturas.id==id) - name = '{}{}_{}.ods'.format(obj.serie, obj.folio, obj.cliente.rfc) - if obj.uuid is None: - return b'', name - - values = cls._get_not_in_xml(cls, obj, emisor) - data = util.get_data_from_xml(obj, values) - doc = util.to_pdf(data, emisor.rfc, True) - return doc, name - - @classmethod - def get_zip(cls, id, rfc): - obj = Facturas.get(Facturas.id==id) - name_zip = '{}{}_{}.zip'.format(obj.serie, obj.folio, obj.cliente.rfc) - if obj.uuid is None: - return b'', name_zip - - file_xml = cls.get_xml(id) - if not file_xml[0]: - return b'', name_zip - - file_pdf = cls.get_pdf(id, rfc) - if not file_pdf[0]: - return b'', name_zip - - file_zip = util.to_zip(file_xml, file_pdf) - - return file_zip, name_zip - - @util.run_in_thread - def _send(self, id, rfc): - return Facturas.send(id, rfc) - - @util.run_in_thread - def _sync(self, id, auth): - return Facturas.sync(id, auth) - - @util.run_in_thread - def _sync_pdf(self, pdf, name_pdf, target): - auth = Emisor.get_auth() - files = ( - (pdf, name_pdf, target), - ) - util.sync_cfdi(auth, files) - return - - @util.run_in_thread - def _sync_xml(self, obj): - emisor = Emisor.select()[0] - auth = Emisor.get_auth() - name_xml = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc) - target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/') - files = ( - (obj.xml, name_xml, target), - ) - util.sync_cfdi(auth, files) - return - - @util.run_in_thread - def _actualizar_saldo_cliente(self, invoice, cancel=False): - if invoice.tipo_comprobante == 'T': - return - - if invoice.donativo and invoice.forma_pago == '12': - return - - importe = invoice.total_mn - if invoice.tipo_comprobante == 'E': - importe *= -1 - - if cancel: - 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'}) - in_zip = Configuracion.get_bool('chk_config_send_zip') - - if not values: - msg = 'No esta configurado el servidor de correo de salida' - return {'ok': False, 'msg': msg} - - obj = Facturas.get(Facturas.id==id) - if obj.uuid is None: - msg = 'La factura no esta timbrada' - return {'ok': False, 'msg': msg} - - if not obj.cliente.correo_facturas: - msg = 'El cliente no tiene configurado el correo para facturas' - return {'ok': False, 'msg': msg} - - if in_zip: - files = (cls.get_zip(id, rfc),) - else: - files = (cls.get_pdf(id, rfc), cls.get_xml(id)) - - fields = util.make_fields(obj.xml) - server = { - 'servidor': values['correo_servidor'], - 'puerto': values['correo_puerto'], - 'ssl': bool(int(values['correo_ssl'])), - 'usuario': values['correo_usuario'], - 'contra': values['correo_contra'], - } - options = { - 'para': obj.cliente.correo_facturas, - 'copia': values['correo_copia'], - 'confirmar': util.get_bool(values.get('correo_confirmacion', '0')), - 'asunto': util.make_info_mail(values['correo_asunto'], fields), - 'mensaje': util.make_info_mail(values['correo_mensaje'], fields), - 'files': files, - } - data= { - 'server': server, - 'options': options, - } - result = util.send_mail(data) - if not result['ok'] or result['msg']: - return {'ok': False, 'msg': result['msg']} - - msg = 'Factura enviada correctamente' - return {'ok': True, 'msg': msg} - - @classmethod - def sync(cls, id, auth): - obj = Facturas.get(Facturas.id==id) - if obj.uuid is None: - msg = 'La factura no esta timbrada' - return - - emisor = Emisor.select()[0] - pdf, name_pdf = cls.get_pdf(id, auth['RFC'], False) - name_xml = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc) - target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/') - files = ( - (obj.xml, name_xml, target), - (pdf, name_pdf, target), - ) - util.sync_cfdi(auth, files) - return - - def _get_filter_folios(self, values): - if not 'folio' in values: - return '' - - folios = values['folio'].split('-') - if len(folios) == 1: - try: - folio1 = int(folios[0]) - except ValueError: - return '' - - folio2 = folio1 - else: - try: - folio1 = int(folios[0]) - folio2 = int(folios[1]) - except ValueError: - return '' - - return (Facturas.folio.between(folio1, folio2)) - - def _get_por_pagar(self, ids): - filtros = ( - (Facturas.cancelada==False) & - (Facturas.uuid.is_null(False)) & - (Facturas.tipo_comprobante=='I') & - (Facturas.saldo>0) - ) - if ids: - filtros &= (Facturas.id.not_in(ids)) - - rows = tuple(Facturas - .select( - Facturas.id, - Facturas.serie, - Facturas.folio, - Facturas.uuid, - Facturas.fecha, - Facturas.tipo_comprobante, - Facturas.estatus, - Socios.nombre.alias('cliente'), - Facturas.total, - Facturas.saldo, - ) - .where(filtros) - .join(Socios) - .switch(Facturas) - .dicts() - ) - return {'ok': True, 'rows': rows} - - return - - def _get_opt(self, values): - if values['opt'] == 'porpagar': - return self._get_por_pagar(self, util.loads(values['ids'])) - - cfdis = util.loads(values['cfdis']) - - if values['year'] == '-1': - fy = (Facturas.fecha.year > 0) - else: - fy = (Facturas.fecha.year == int(values['year'])) - if values['month'] == '-1': - fm = (Facturas.fecha.month > 0) - else: - fm = (Facturas.fecha.month == int(values['month'])) - - if values['opt'] == 'relacionados': - folios = self._get_filter_folios(self, values) - uuid = values.get('uuid', '') - if uuid: - f_uuid = (cast(Facturas.uuid, 'text').contains(uuid)) - cliente = (Facturas.cliente == int(values['id_cliente'])) - if cfdis: - f_ids = (Facturas.id.not_in(cfdis)) - else: - f_ids = (Facturas.id > 0) - - filters = (fy & fm & cliente & f_ids) - if folios: - filters = filters & folios - elif uuid: - filters = filters & f_uuid - - if values['anticipo'] == '1': - filters = filters & (Facturas.anticipo == True) - - rows = tuple(Facturas - .select(Facturas.id, Facturas.serie, Facturas.folio, - Facturas.uuid, Facturas.fecha, Facturas.tipo_comprobante, - Facturas.estatus, Facturas.total_mn) - .where(filters).dicts() - ) - - return {'ok': True, 'rows': rows} - - @classmethod - def get_(cls, values): - opt = values.get('opt', '') - if opt: - return cls._get_opt(cls, values) - - if 'start' in values: - filters = Facturas.fecha.between( - util.get_date(values['start']), - util.get_date(values['end'], True) - ) - else: - if values['year'] == '-1': - fy = (Facturas.fecha.year > 0) - else: - fy = (Facturas.fecha.year == int(values['year'])) - if values['month'] == '-1': - fm = (Facturas.fecha.month > 0) - else: - 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, - Facturas.total_mn, Socios.nombre.alias('cliente')) - .where(filters) - .join(Socios) - .switch(Facturas).dicts() - ) - return {'ok': True, 'rows': rows} - - @classmethod - def remove(cls, id): - obj = Facturas.get(Facturas.id==id) - if obj.uuid: - return False - - q = FacturasDetalle.delete().where(FacturasDetalle.factura==obj) - q.execute() - q = FacturasImpuestos.delete().where(FacturasImpuestos.factura==obj) - q.execute() - q = FacturasRelacionadas.delete().where(FacturasRelacionadas.factura==obj) - q.execute() - return bool(obj.delete_instance()) - - def _get_folio(self, serie): - inicio_serie = Folios.select( - Folios.inicio).where(Folios.serie==serie).scalar() - - inicio = (Facturas - .select(fn.Max(Facturas.folio).alias('mf')) - .where(Facturas.serie==serie) - .order_by(SQL('mf')) - .scalar()) - - if inicio is None or inicio_serie > inicio: - inicio = inicio_serie - else: - inicio += 1 - - return inicio - - def _calculate_totals(self, invoice, products): - tax_locales = Configuracion.get_bool('chk_config_tax_locales') - tax_decimals = Configuracion.get_bool('chk_config_tax_decimals') - subtotal = 0 - descuento_cfdi = 0 - totals_tax = {} - total_trasladados = None - total_retenciones = None - locales_traslados = 0 - locales_retenciones = 0 - - for product in products: - # ~ print ('\n', product) - id_product = product.pop('id') - id_student = product.pop('id_student', 0) - p = Productos.get(Productos.id==id_product) - - product['unidad'] = p.unidad.key - product['clave'] = p.clave - product['clave_sat'] = p.clave_sat - product['cuenta_predial'] = p.cuenta_predial - - product['factura'] = invoice.id - product['producto'] = id_product - - cantidad = float(product['cantidad']) - valor_unitario = float(product['valor_unitario']) - descuento = float(product['descuento']) - precio_final = valor_unitario - descuento - importe = round(cantidad * precio_final, DECIMALES) - - product['cantidad'] = cantidad - product['valor_unitario'] = valor_unitario - product['descuento'] = round(descuento * cantidad, DECIMALES) - product['precio_final'] = precio_final - product['importe'] = round(cantidad * valor_unitario, DECIMALES) - - descuento_cfdi += product['descuento'] - subtotal += product['importe'] - - if id_student: - student = Alumnos.get(Alumnos.id==id_student) - product['alumno'] = str(student) - product['curp'] = student.curp - product['nivel'] = student.grupo.nivel.nombre - product['autorizacion'] = student.grupo.nivel.autorizacion.strip() - - FacturasDetalle.create(**product) - - base = product['importe'] - product['descuento'] - for tax in p.impuestos: - if tax_locales and tax.tipo == 'R' and tax.key == '000': - base = product['importe'] - if tax_decimals: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES_TAX) - else: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES) - if tax.tipo == 'T' and tax.key != '000': - total_trasladados = (total_trasladados or 0) + impuesto_producto - elif tax.tipo == 'R' and tax.key != '000': - total_retenciones = (total_retenciones or 0) + impuesto_producto - elif tax.tipo == 'T' and tax.key == '000': - locales_traslados += impuesto_producto - elif tax.tipo == 'R' and tax.key == '000': - locales_retenciones += impuesto_producto - - if tax.id in totals_tax: - totals_tax[tax.id].base += base - totals_tax[tax.id].suma_impuestos += impuesto_producto - else: - tax.base = base - tax.suma_impuestos = impuesto_producto - totals_tax[tax.id] = tax - - for tax in totals_tax.values(): - if tax.tipo == 'E': - continue - - invoice_tax = { - 'factura': invoice.id, - 'impuesto': tax.id, - 'base': tax.base, - 'importe': tax.suma_impuestos, - } - FacturasImpuestos.create(**invoice_tax) - - total = subtotal - descuento_cfdi + \ - (total_trasladados or 0) - (total_retenciones or 0) \ - + locales_traslados - locales_retenciones - total_mn = round(total * invoice.tipo_cambio, DECIMALES) - data = { - 'subtotal': subtotal, - 'descuento': descuento_cfdi, - 'total': total, - 'total_mn': total_mn, - 'total_trasladados': total_trasladados, - 'total_retenciones': total_retenciones, - } - return data - - def _guardar_relacionados(self, invoice, relacionados): - for cfdi in relacionados: - data = { - 'factura': invoice, - 'factura_origen': cfdi, - } - FacturasRelacionadas.create(**data) - return - - def _guardar_ine(self, invoice, valores): - if not valores: - return - - data = { - 'factura': invoice, - 'nombre': 'ine', - 'valores': valores, - } - FacturasComplementos.create(**data) - return - - - def _get_serie(self, user, default_serie): - if user.sucursal is None: - return default_serie - - return user.sucursal.serie_facturas or default_serie - - @classmethod - def add(cls, values, user): - productos = util.loads(values.pop('productos')) - relacionados = util.loads(values.pop('relacionados')) - ine = values.pop('ine', {}) - - emisor = Emisor.select()[0] - values['serie'] = cls._get_serie(cls, user, values['serie']) - values['folio'] = cls._get_folio(cls, values['serie']) - values['tipo_cambio'] = float(values['tipo_cambio']) - values['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal - values['anticipo'] = util.get_bool(values['anticipo']) - values['donativo'] = util.get_bool(values['donativo']) - - with database_proxy.atomic() as txn: - obj = Facturas.create(**values) - totals = cls._calculate_totals(cls, obj, productos) - cls._guardar_relacionados(cls, obj, relacionados) - cls._guardar_ine(cls, obj, ine) - obj.subtotal = totals['subtotal'] - obj.descuento = totals['descuento'] - obj.total_trasladados = totals['total_trasladados'] - obj.total_retenciones = totals['total_retenciones'] - obj.total = totals['total'] - obj.saldo = totals['total'] - obj.total_mn = totals['total_mn'] - obj.save() - - msg = 'Factura guardada correctamente. Enviando a timbrar' - row = { - 'id': obj.id, - 'serie': obj.serie, - 'folio': obj.folio, - 'uuid': obj.uuid, - 'fecha': obj.fecha, - 'tipo_comprobante': obj.tipo_comprobante, - 'estatus': obj.estatus, - 'total_mn': obj.total_mn, - 'cliente': obj.cliente.nombre, - } - data = {'ok': True, 'row': row, 'new': True, 'error': False, 'msg': msg} - return data - - def _make_xml(self, invoice, auth): - tax_decimals = Configuracion.get_bool('chk_config_tax_decimals') - tmp = 0 - emisor = Emisor.select()[0] - certificado = Certificado.select()[0] - - is_edu = False - comprobante = {} - relacionados = {} - donativo = {} - complementos = FacturasComplementos.get_(invoice) - - if invoice.donativo: - donativo['noAutorizacion'] = emisor.autorizacion - donativo['fechaAutorizacion'] = str(emisor.fecha_autorizacion) - - if invoice.serie: - comprobante['Serie'] = invoice.serie - if invoice.condiciones_pago: - comprobante['CondicionesDePago'] = invoice.condiciones_pago - # ~ if invoice.descuento: - # ~ comprobante['Descuento'] = invoice.descuento - - comprobante['Folio'] = str(invoice.folio) - comprobante['Fecha'] = invoice.fecha.isoformat()[:19] - comprobante['FormaPago'] = invoice.forma_pago - comprobante['NoCertificado'] = certificado.serie - comprobante['Certificado'] = certificado.cer_txt - comprobante['SubTotal'] = FORMAT.format(invoice.subtotal) - comprobante['Moneda'] = invoice.moneda - comprobante['TipoCambio'] = '1' - if comprobante['Moneda'] != 'MXN': - comprobante['TipoCambio'] = FORMAT.format(invoice.tipo_cambio) - comprobante['Total'] = FORMAT.format(invoice.total) - comprobante['TipoDeComprobante'] = invoice.tipo_comprobante - comprobante['MetodoPago'] = invoice.metodo_pago - comprobante['LugarExpedicion'] = invoice.lugar_expedicion - if invoice.descuento: - comprobante['Descuento'] = FORMAT.format(invoice.descuento) - - if invoice.tipo_relacion: - relacionados = { - 'tipo': invoice.tipo_relacion, - 'cfdis': FacturasRelacionadas.get_(invoice), - } - - emisor = { - 'Rfc': emisor.rfc, - 'Nombre': emisor.nombre, - 'RegimenFiscal': invoice.regimen_fiscal, - } - - receptor = { - 'Rfc': invoice.cliente.rfc, - 'Nombre': invoice.cliente.nombre, - 'UsoCFDI': invoice.uso_cfdi, - } - - conceptos = [] - rows = FacturasDetalle.select().where(FacturasDetalle.factura==invoice) - for row in rows: - concepto = { - 'ClaveProdServ': row.producto.clave_sat, - 'NoIdentificacion': row.producto.clave, - 'Cantidad': FORMAT.format(row.cantidad), - 'ClaveUnidad': row.producto.unidad.key, - 'Unidad': row.producto.unidad.name[:20], - 'Descripcion': row.descripcion, - 'ValorUnitario': FORMAT.format(row.valor_unitario), - 'Importe': FORMAT.format(row.importe), - } - if row.descuento: - concepto['Descuento'] = FORMAT.format(row.descuento) - - if row.cuenta_predial: - concepto['CuentaPredial'] = row.cuenta_predial - - if row.pedimento: - concepto['Pedimento'] = row.pedimento - - if row.autorizacion: - is_edu = True - concepto['student'] = { - 'version': '1.0', - 'nombreAlumno': row.alumno, - 'CURP': row.curp, - 'nivelEducativo': row.nivel, - 'autRVOE': row.autorizacion, - } - - taxes = {} - traslados = [] - retenciones = [] - - for impuesto in row.producto.impuestos: - if impuesto.tipo == 'E': - continue - - if impuesto.key == '000': - continue - - base = row.importe - row.descuento - if tax_decimals: - import_tax = round(impuesto.tasa * base, DECIMALES_TAX) - tmp += import_tax - xml_importe = FORMAT_TAX.format(import_tax) - else: - import_tax = round(impuesto.tasa * base, DECIMALES) - xml_importe = FORMAT.format(import_tax) - tipo_factor = 'Tasa' - if impuesto.factor != 'T': - tipo_factor = 'Cuota' - tax = { - "Base": FORMAT.format(base), - "Impuesto": impuesto.key, - "TipoFactor": tipo_factor, - "TasaOCuota": str(impuesto.tasa), - "Importe": xml_importe, - } - if impuesto.tipo == 'T': - traslados.append(tax) - else: - retenciones.append(tax) - - if traslados: - taxes['traslados'] = traslados - if retenciones: - taxes['retenciones'] = retenciones - concepto['impuestos'] = taxes - conceptos.append(concepto) - - impuestos = {} - traslados = [] - retenciones = [] - total_locales_trasladados = 0 - total_locales_retenciones = 0 - locales_trasladados = [] - locales_retenciones = [] - - if not invoice.total_trasladados is None: - impuestos['TotalImpuestosTrasladados'] = \ - FORMAT.format(invoice.total_trasladados) - if not invoice.total_retenciones is None: - impuestos['TotalImpuestosRetenidos'] = \ - FORMAT.format(invoice.total_retenciones) - - taxes = (FacturasImpuestos - .select() - .where(FacturasImpuestos.factura==invoice)) - for tax in taxes: - if tax.impuesto.key == '000': - tasa = str(round(tax.impuesto.tasa * 100, 2)) - simporte = FORMAT.format(tax.importe) - if tax.impuesto.tipo == 'T': - traslado = { - 'ImpLocTrasladado': tax.impuesto.name, - 'TasadeTraslado': tasa, - 'Importe': simporte, - } - locales_trasladados.append(traslado) - total_locales_trasladados += tax.importe - else: - retencion = { - 'ImpLocRetenido': tax.impuesto.name, - 'TasadeRetencion': tasa, - 'Importe': simporte, - } - locales_retenciones.append(retencion) - total_locales_retenciones += tax.importe - continue - - tipo_factor = 'Tasa' - if tax.impuesto.factor != 'T': - tipo_factor = 'Cuota' - - if tax_decimals: - xml_importe = FORMAT_TAX.format(tax.importe) - else: - xml_importe = FORMAT.format(tax.importe) - - if tax.impuesto.tipo == 'T': - traslado = { - "Impuesto": tax.impuesto.key, - "TipoFactor": tipo_factor, - "TasaOCuota": str(tax.impuesto.tasa), - "Importe": xml_importe, - } - traslados.append(traslado) - else: - retencion = { - "Impuesto": tax.impuesto.key, - "Importe": FORMAT.format(tax.importe), - } - retenciones.append(retencion) - - impuestos['traslados'] = traslados - impuestos['retenciones'] = retenciones - impuestos['total_locales_trasladados'] = '' - if total_locales_trasladados: - impuestos['total_locales_trasladados'] = \ - FORMAT.format(total_locales_trasladados) - impuestos['total_locales_retenciones'] = '' - if total_locales_retenciones: - impuestos['total_locales_retenciones'] = \ - FORMAT.format(total_locales_retenciones) - impuestos['locales_trasladados'] = locales_trasladados - impuestos['locales_retenciones'] = locales_retenciones - - data = { - 'comprobante': comprobante, - 'relacionados': relacionados, - 'emisor': emisor, - 'receptor': receptor, - 'conceptos': conceptos, - 'impuestos': impuestos, - 'donativo': donativo, - 'edu': is_edu, - 'complementos': complementos, - } - return util.make_xml(data, certificado, auth) - - @classmethod - def get_status_sat(cls, id): - obj = Facturas.get(Facturas.id == id) - obj.estatus_sat = util.get_sat(obj.xml) - 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): - auth = Emisor.get_auth() - obj = Facturas.get(Facturas.id == id) - obj.xml = cls._make_xml(cls, obj, auth) - obj.estatus = 'Generada' - obj.save() - - enviar_correo = util.get_bool(Configuracion.get_('correo_directo')) - - anticipo = False - msg = 'Factura timbrada correctamente' - result = util.timbra_xml(obj.xml, auth) - # ~ print (result) - if result['ok']: - obj.xml = result['xml'] - obj.uuid = result['uuid'] - obj.fecha_timbrado = result['fecha'] - obj.estatus = 'Timbrada' - obj.error = '' - obj.save() - 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) - cls._sync(cls, id, auth) - else: - msg = result['error'] - obj.estatus = 'Error' - obj.error = msg - obj.save() - row = {'estatus': 'Error'} - - result = { - 'ok': result['ok'], - 'msg': msg, - 'row': row, - 'anticipo': anticipo - } - - return result - - -class PreFacturas(BaseModel): - cliente = ForeignKeyField(Socios) - serie = TextField(default='PRE') - folio = IntegerField(default=0) - fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) - forma_pago = TextField(default='') - condiciones_pago = TextField(default='') - subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - moneda = TextField(default='MXN') - tipo_cambio = DecimalField(default=1.0, decimal_places=6, auto_round=True) - total = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_mn = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - tipo_comprobante = TextField(default='I') - metodo_pago = TextField(default='PUE') - lugar_expedicion = TextField(default='') - uso_cfdi = TextField(default='') - total_retenciones = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - total_trasladados = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - estatus = TextField(default='Generada') - regimen_fiscal = TextField(default='') - notas = TextField(default='') - donativo = BooleanField(default=False) - tipo_relacion = TextField(default='') - - class Meta: - order_by = ('fecha',) - - @util.run_in_thread - def _send_in_thread(self, id, obj, values): - log.info('Generando PDF...') - files = (self.get_pdf(id),) - log.info('PDF Generado...') - - invoice = PreFacturas.select().where(PreFacturas.id==id).dicts()[0] - fields = { - 'receptor_nombre': obj.cliente.nombre, - 'receptor_rfc': obj.cliente.rfc, - } - fields.update(invoice) - - asunto = 'Enviamos la prefactura: PRE-{}'.format(obj.folio) - server = { - 'servidor': values['correo_servidor'], - 'puerto': values['correo_puerto'], - 'ssl': bool(int(values['correo_ssl'])), - 'usuario': values['correo_usuario'], - 'contra': values['correo_contra'], - } - options = { - 'para': obj.cliente.correo_facturas, - 'copia': values['correo_copia'], - 'confirmar': util.get_bool(values.get('correo_confirmacion', '0')), - 'asunto': asunto, - 'mensaje': util.make_info_mail(values['correo_mensaje'], fields), - 'files': files, - } - data= { - 'server': server, - 'options': options, - } - log.info('Enviando prefactura...') - result = util.send_mail(data) - log.info('Prefactura enviada...') - return - - @classmethod - def enviar(cls, id): - values = Configuracion.get_({'fields': 'correo'}) - if not values: - msg = 'No esta configurado el servidor de correo de salida' - return {'ok': False, 'msg': msg} - - obj = PreFacturas.get(PreFacturas.id==id) - if not obj.cliente.correo_facturas: - msg = 'El cliente no tiene configurado el correo para facturas' - return {'ok': False, 'msg': msg} - - rows = PreFacturasDetalle.count(id) - if rows > 300: - cls._send_in_thread(cls, id, obj, values) - msg = 'Enviando correo...' - return {'ok': True, 'msg': msg} - - files = (cls.get_pdf(id),) - - invoice = PreFacturas.select().where(PreFacturas.id==id).dicts()[0] - fields = { - 'receptor_nombre': obj.cliente.nombre, - 'receptor_rfc': obj.cliente.rfc, - } - fields.update(invoice) - - asunto = 'Enviamos la prefactura: PRE-{}'.format(obj.folio) - server = { - 'servidor': values['correo_servidor'], - 'puerto': values['correo_puerto'], - 'ssl': bool(int(values['correo_ssl'])), - 'usuario': values['correo_usuario'], - 'contra': values['correo_contra'], - } - options = { - 'para': obj.cliente.correo_facturas, - 'copia': values['correo_copia'], - 'confirmar': util.get_bool(values.get('correo_confirmacion', '0')), - 'asunto': asunto, - 'mensaje': util.make_info_mail(values['correo_mensaje'], fields), - 'files': files, - } - data= { - 'server': server, - 'options': options, - } - result = util.send_mail(data) - if not result['ok'] or result['msg']: - return {'ok': False, 'msg': result['msg']} - - msg = 'Pre Factura enviada correctamente' - return {'ok': True, 'msg': msg} - - def _get_info_to_pdf(self, id): - data = {} - obj = PreFacturas.select().where(PreFacturas.id==id).dicts()[0] - regimen = SATRegimenes.get(SATRegimenes.key==obj['regimen_fiscal']) - usocfdi = SATUsoCfdi.get(SATUsoCfdi.key==obj['uso_cfdi']) - formapago = SATFormaPago.get(SATFormaPago.key==obj['forma_pago']) - moneda = SATMonedas.get(SATMonedas.key==obj['moneda']) - - emisor = util.get_dict(Emisor.select().dicts()[0]) - emisor['nointerior'] = emisor['no_interior'] - emisor['noexterior'] = emisor['no_exterior'] - emisor['codigopostal'] = emisor['codigo_postal'] - emisor['regimenfiscal'] = str(regimen) - - receptor = Socios.select().where(Socios.id==obj['cliente']).dicts()[0] - receptor['usocfdi'] = str(usocfdi) - receptor['nointerior'] = receptor['no_interior'] - receptor['noexterior'] = receptor['no_exterior'] - receptor['codigopostal'] = receptor['codigo_postal'] - - data['es_pre'] = True - data['cancelada'] = False - data['donativo'] = obj['donativo'] - - tipos = { - 'I': 'ingreso', - 'E': 'egreso', - 'T': 'traslado', - } - mp = { - 'PUE': 'Pago en una sola exhibición', - 'PPD': 'Pago en parcialidades o diferido', - } - - data['comprobante'] = obj - data['comprobante']['version'] = CURRENT_CFDI - data['comprobante']['folio'] = str(data['comprobante']['folio']) - data['comprobante']['seriefolio'] = '{}-{}'.format( - data['comprobante']['serie'], data['comprobante']['folio']) - data['comprobante']['fecha'] = str(data['comprobante']['fecha']) - data['comprobante']['tipodecomprobante'] = tipos.get( - data['comprobante']['tipo_comprobante']) - data['comprobante']['lugarexpedicion'] = \ - 'C.P. de Expedición: {}'.format( - data['comprobante']['lugar_expedicion']) - data['comprobante']['metododepago'] = 'Método de Pago: ({}) {}'.format( - obj['metodo_pago'], mp[obj['metodo_pago']]) - data['comprobante']['formadepago'] = str(formapago) - data['comprobante']['condicionesdepago'] = \ - data['comprobante']['condiciones_pago'] - data['comprobante']['tipocambio'] = 'Tipo de Cambio: $ {:0.2f}'.format( - data['comprobante']['tipo_cambio']) - data['comprobante']['totalenletras'] = util.to_letters( - data['comprobante']['total'], data['comprobante']['moneda']) - data['comprobante']['moneda'] = str(moneda) - - data['emisor'] = emisor - data['receptor'] = receptor - - data['conceptos'] = PreFacturasDetalle.get_(id) - data['totales'] = {} - data['totales']['moneda'] = data['comprobante']['moneda'] - data['totales']['subtotal'] = str(data['comprobante']['subtotal']) - data['totales']['total'] = str(data['comprobante']['total']) - - if obj['descuento']: - data['totales']['descuento'] = float(obj['descuento']) - - taxes = PreFacturasImpuestos.get_(id) - data['totales']['traslados'] = taxes['traslados'] - data['totales']['retenciones'] = taxes['retenciones'] - data['totales']['taxlocales'] = taxes['taxlocales'] - data['timbre'] = {} - - data['donataria'] = {} - data['ine'] = {} - - return data - - @util.run_in_thread - def _get_pdf_in_thread(self, id): - obj = PreFacturas.get(PreFacturas.id==id) - name = '{}{}_{}.pdf'.format(obj.serie, obj.folio, obj.cliente.rfc) - data = self._get_info_to_pdf(self, id) - doc = util.to_pdf(data, data['emisor']['rfc']) - - emisor = Emisor.select()[0] - target = emisor.rfc + '/Prefacturas/' - files = ( - (doc, name, target), - ) - util.sync_cfdi({'REPO': False}, files) - return - - @classmethod - def get_pdf_in_thread(cls, id): - return cls._get_pdf_in_thread(cls, id) - - @classmethod - def get_pdf(cls, id): - obj = PreFacturas.get(PreFacturas.id==id) - name = '{}{}_{}.pdf'.format(obj.serie, obj.folio, obj.cliente.rfc) - data = cls._get_info_to_pdf(cls, id) - doc = util.to_pdf(data, data['emisor']['rfc']) - return doc, name - - @classmethod - def remove(cls, id): - obj = PreFacturas.get(PreFacturas.id==id) - - q = PreFacturasDetalle.delete().where( - PreFacturasDetalle.factura==obj) - q.execute() - q = PreFacturasImpuestos.delete().where( - PreFacturasImpuestos.factura==obj) - q.execute() - q = PreFacturasRelacionadas.delete().where( - PreFacturasRelacionadas.factura==obj) - q.execute() - return bool(obj.delete_instance()) - - @classmethod - def filter_years(cls): - data = [{'id': -1, 'value': 'Todos'}] - rows = (PreFacturas - .select(PreFacturas.fecha.year) - .group_by(PreFacturas.fecha.year) - .order_by(PreFacturas.fecha.year) - .scalar(as_tuple=True) - ) - if not rows is None: - data += [{'id': int(row), 'value': int(row)} for row in rows] - return tuple(data) - - @classmethod - def get_(cls, values): - if values['year'] == '-1': - fy = (PreFacturas.fecha.year > 0) - else: - fy = (PreFacturas.fecha.year == int(values['year'])) - if values['month'] == '-1': - fm = (PreFacturas.fecha.month > 0) - else: - fm = (PreFacturas.fecha.month == int(values['month'])) - filters = (fy & fm) - - rows = tuple(PreFacturas - .select( - PreFacturas.id, - PreFacturas.folio, - PreFacturas.fecha, - PreFacturas.tipo_comprobante, - PreFacturas.total_mn, - Socios.nombre.alias('cliente')) - .where(filters) - .join(Socios) - .switch(PreFacturas).dicts() - ) - return {'ok': True, 'rows': rows} - - def _get_folio(self, serie): - inicio = (PreFacturas - .select(fn.Max(PreFacturas.folio).alias('mf')) - .where(PreFacturas.serie==serie) - .order_by(SQL('mf')) - .scalar()) - - if inicio is None: - inicio = 1 - else: - inicio += 1 - - return inicio - - def _calculate_totals(self, invoice, products): - tax_locales = Configuracion.get_bool('chk_config_tax_locales') - tax_decimals = Configuracion.get_bool('chk_config_tax_decimals') - tax_decimals = True - subtotal = 0 - descuento_cfdi = 0 - totals_tax = {} - total_trasladados = None - total_retenciones = None - locales_traslados = 0 - locales_retenciones = 0 - - for product in products: - id_product = product.pop('id') - p = Productos.get(Productos.id==id_product) - - product['unidad'] = p.unidad.key - product['clave'] = p.clave - product['clave_sat'] = p.clave_sat - product['cuenta_predial'] = p.cuenta_predial - - product['factura'] = invoice.id - product['producto'] = id_product - - cantidad = float(product['cantidad']) - valor_unitario = float(product['valor_unitario']) - descuento = float(product['descuento']) - precio_final = valor_unitario - descuento - importe = round(cantidad * precio_final, DECIMALES) - - product['cantidad'] = cantidad - product['valor_unitario'] = valor_unitario - product['descuento'] = descuento - product['precio_final'] = precio_final - product['importe'] = round(cantidad * valor_unitario, DECIMALES) - - descuento_cfdi += descuento - subtotal += importe - - PreFacturasDetalle.create(**product) - - base = product['importe'] - product['descuento'] - for tax in p.impuestos: - if tax_locales and tax.tipo == 'R' and tax.key == '000': - base = product['importe'] - if tax_decimals: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES_TAX) - else: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES) - if tax.tipo == 'T' and tax.key != '000': - total_trasladados = (total_trasladados or 0) + impuesto_producto - elif tax.tipo == 'R' and tax.key != '000': - total_retenciones = (total_retenciones or 0) + impuesto_producto - elif tax.tipo == 'T' and tax.key == '000': - locales_traslados += impuesto_producto - elif tax.tipo == 'R' and tax.key == '000': - locales_retenciones += impuesto_producto - - if tax.id in totals_tax: - totals_tax[tax.id].base += base - totals_tax[tax.id].suma_impuestos += impuesto_producto - else: - tax.base = base - tax.suma_impuestos = impuesto_producto - totals_tax[tax.id] = tax - - for tax in totals_tax.values(): - if tax.tipo == 'E': - continue - - invoice_tax = { - 'factura': invoice.id, - 'impuesto': tax.id, - 'base': tax.base, - 'importe': tax.suma_impuestos, - } - PreFacturasImpuestos.create(**invoice_tax) - - total = subtotal - descuento_cfdi + \ - (total_trasladados or 0) - (total_retenciones or 0) \ - + locales_traslados - locales_retenciones - total_mn = round(total * invoice.tipo_cambio, DECIMALES) - data = { - 'subtotal': subtotal + descuento, - 'descuento': descuento_cfdi, - 'total': total, - 'total_mn': total_mn, - 'total_trasladados': total_trasladados, - 'total_retenciones': total_retenciones, - } - return data - - @classmethod - def add(cls, values): - productos = util.loads(values.pop('productos')) - - emisor = Emisor.select()[0] - values['serie'] = 'PRE' - values['folio'] = cls._get_folio(cls, values['serie']) - values['tipo_cambio'] = float(values['tipo_cambio']) - values['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal - - with database_proxy.atomic() as txn: - obj = PreFacturas.create(**values) - totals = cls._calculate_totals(cls, obj, productos) - obj.subtotal = totals['subtotal'] - obj.descuento = totals['descuento'] - obj.total_trasladados = totals['total_trasladados'] - obj.total_retenciones = totals['total_retenciones'] - obj.total = totals['total'] - obj.total_mn = totals['total_mn'] - obj.save() - - msg = 'Factura guardada correctamente' - row = { - 'id': obj.id, - 'folio': obj.folio, - 'fecha': obj.fecha, - 'tipo_comprobante': obj.tipo_comprobante, - 'total_mn': obj.total_mn, - 'cliente': obj.cliente.nombre, - } - data = {'ok': True, 'row': row, 'new': True, 'error': False, 'msg': msg} - return data - - -class FacturasRelacionadas(BaseModel): - factura = ForeignKeyField(Facturas, related_name='original') - factura_origen = ForeignKeyField(Facturas, related_name='relacion') - - class Meta: - order_by = ('factura',) - - @classmethod - def get_(cls, invoice): - query = (FacturasRelacionadas - .select() - .where(FacturasRelacionadas.factura==invoice) - ) - return [str(r.factura_origen.uuid) for r in query] - - -class FacturasComplementos(BaseModel): - factura = ForeignKeyField(Facturas) - nombre = TextField(default='') - valores = TextField(default='') - - class Meta: - order_by = ('factura',) - - @classmethod - def get_(cls, factura): - query = (FacturasComplementos - .select() - .where(FacturasComplementos.factura==factura) - ) - return {r.nombre: util.loads(r.valores) for r in query} - - -class PreFacturasRelacionadas(BaseModel): - factura = ForeignKeyField(PreFacturas, related_name='original') - factura_origen = ForeignKeyField(PreFacturas, related_name='relacion') - - class Meta: - order_by = ('factura',) - - -class FacturasDetalle(BaseModel): - factura = ForeignKeyField(Facturas) - producto = ForeignKeyField(Productos, null=True) - cantidad = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - precio_final = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descripcion = TextField(default='') - unidad = TextField(default='') - clave = TextField(default='') - clave_sat = TextField(default='') - categoria = TextField(default='') - aduana = TextField(default='') - pedimento = TextField(default='') - fecha_pedimento = DateField(null=True) - alumno = TextField(default='') - curp = TextField(default='') - nivel = TextField(default='') - autorizacion = TextField(default='') - cuenta_predial = TextField(default='') - - class Meta: - order_by = ('factura',) - - -class PreFacturasDetalle(BaseModel): - factura = ForeignKeyField(PreFacturas) - producto = ForeignKeyField(Productos, null=True) - descripcion = TextField(default='') - cantidad = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - precio_final = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - aduana = TextField(default='') - pedimento = TextField(default='') - fecha_pedimento = DateField(null=True) - alumno = TextField(default='') - curp = TextField(default='') - nivel = TextField(default='') - autorizacion = TextField(default='') - cuenta_predial = TextField(default='') - - class Meta: - order_by = ('factura',) - - def _get_impuestos(self, id): - model_pt = Productos.impuestos.get_through_model() - impuestos = tuple(model_pt - .select( - model_pt.productos_id.alias('product'), - model_pt.satimpuestos_id.alias('tax')) - .where(model_pt.productos_id==id).dicts()) - return impuestos - - @classmethod - def facturar(cls, id): - data = [] - - q = PreFacturas.select().where(PreFacturas.id==id)[0] - if q.cliente.forma_pago is None: - forma_pago = '' - else: - forma_pago = q.cliente.forma_pago.key - - if q.cliente.uso_cfdi is None: - uso_cfdi = '' - else: - uso_cfdi = q.cliente.uso_cfdi.key - - receptor = { - 'id': q.cliente.id, - 'nombre': q.cliente.nombre, - 'rfc': q.cliente.rfc, - 'forma_pago': forma_pago, - 'uso_cfdi': uso_cfdi, - 'notas': q.notas, - } - - productos = PreFacturasDetalle.select().where( - PreFacturasDetalle.factura==id) - - for p in reversed(productos): - row = {'id': p.producto.id} - row['clave'] = p.producto.clave - row['descripcion'] = p.descripcion - row['unidad'] = p.producto.unidad.name - row['cantidad'] = p.cantidad - row['valor_unitario'] = p.valor_unitario - row['descuento'] = p.descuento - pf = p.valor_unitario - p.descuento - row['importe'] = round(pf * p.cantidad, DECIMALES) - impuestos = cls._get_impuestos(cls, row['id']) - data.append({'row': row, 'taxes': impuestos}) - - return {'rows': data, 'receptor': receptor} - - @classmethod - def count(cls, id): - c = PreFacturasDetalle.select().where( - PreFacturasDetalle.factura==id).count() - return c - - @classmethod - def can_open(cls, id): - c = cls.count(id) - PreFacturas.get_pdf_in_thread(id) - return c < 300 - - @classmethod - def get_(cls, id): - data = [] - - productos = PreFacturasDetalle.select().where( - PreFacturasDetalle.factura==id) - - for p in reversed(productos): - producto = {} - producto['noidentificacion'] = '{}\n(SAT {})'.format( - p.producto.clave, p.producto.clave_sat) - - producto['descripcion'] = p.descripcion - if p.cuenta_predial: - info = '\nCuenta Predial Número: {}'.format(p.cuenta_predial) - producto['descripcion'] += info - - producto['unidad'] = '{}\n({})'.format( - p.producto.unidad.name, p.producto.unidad.key) - producto['cantidad'] = str(p.cantidad) - producto['valorunitario'] = str(p.valor_unitario) - producto['importe'] = str(p.importe) - data.append(producto) - - return data - - -class FacturasImpuestos(BaseModel): - factura = ForeignKeyField(Facturas) - impuesto = ForeignKeyField(SATImpuestos) - base = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('factura',) - indexes = ( - (('factura', 'impuesto'), True), - ) - - -class FacturasPagos(BaseModel): - movimiento = ForeignKeyField(MovimientosBanco) - factura = ForeignKeyField(Facturas) - numero = IntegerField(default=1) - saldo_anterior = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - saldo = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('factura',) - indexes = ( - (('movimiento', 'factura', 'numero'), True), - ) - - def _movimiento_anterior(self, mov, id): - query = (FacturasPagos - .select() - .where(FacturasPagos.factura==id) - ) - if len(query): - return query[-1], len(query) + 1 - else: - return None, 1 - - def _actualizar_saldo_cliente(self, cliente, importe): - q = (Socios - .update(saldo_cliente=Socios.saldo_cliente + importe) - .where(Socios.id==cliente.id) - ) - return bool(q.execute()) - - def _actualizar_saldos(self, factura, saldo_anterior): - query = (FacturasPagos - .select() - .where(FacturasPagos.factura==factura) - ) - saldo = saldo_anterior - for i, row in enumerate(query): - if not saldo_anterior: - saldo_anterior = row.saldo_anterior - row.numero = i + 1 - row.saldo_anterior = saldo_anterior - row.saldo = saldo_anterior - row.importe - row.save() - saldo_anterior = row.saldo - saldo = row.saldo - - factura.saldo = saldo - factura.pagada = False - factura.save() - return - - @classmethod - def cancelar(cls, mov): - query = (FacturasPagos - .select() - .where(FacturasPagos.movimiento==mov) - ) - for row in query: - cls._actualizar_saldo_cliente(cls, row.factura.cliente, row.importe) - factura = row.factura - saldo_anterior = 0 - if row.numero == 1: - saldo_anterior = row.saldo_anterior - row.delete_instance() - cls._actualizar_saldos(cls, factura, saldo_anterior) - return - - @classmethod - def add(cls, mov, ids): - for i, importe in ids.items(): - fac = Facturas.get(Facturas.id==int(i)) - mov_ant, numero = cls._movimiento_anterior(cls, mov, fac) - nuevo = { - 'movimiento': mov, - 'factura': fac, - 'numero': numero, - 'importe': importe, - } - if mov_ant is None: - nuevo['saldo_anterior'] = float(fac.saldo) - else: - nuevo['saldo_anterior'] = float(mov_ant.saldo) - nuevo['saldo'] = nuevo['saldo_anterior'] - importe - 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 - - -class PreFacturasImpuestos(BaseModel): - factura = ForeignKeyField(PreFacturas) - impuesto = ForeignKeyField(SATImpuestos) - base = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('factura',) - indexes = ( - (('factura', 'impuesto'), True), - ) - - @classmethod - def get_(cls, id): - data = { - 'traslados': [], - 'retenciones': [], - 'taxlocales': [], - } - - taxes = PreFacturasImpuestos.select().where( - PreFacturasImpuestos.factura==id) - - for tax in taxes: - if tax.impuesto.tipo == 'T': - title = 'Traslado {} {}'.format( - tax.impuesto.name, str(tax.impuesto.tasa)) - data['traslados'].append((title, str(tax.importe))) - elif tax.impuesto.tipo == 'R': - title = 'Retención {} {}'.format( - tax.impuesto.name, str(tax.impuesto.tasa)) - data['retenciones'].append((title, str(tax.importe))) - - return data - - -class CamposPersonalizados(BaseModel): - nombre = TextField() - slug = TextField(unique=True) - - class Meta: - order_by = ('nombre',) - - -class FacturasPersonalizados(BaseModel): - factura = ForeignKeyField(Facturas) - campo = TextField() - valor = TextField() - - class Meta: - order_by = ('factura',) - - -class Tickets(BaseModel): - cliente = ForeignKeyField(Socios, null=True) - serie = TextField(default='') - folio = IntegerField(default=0) - fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) - forma_pago = TextField(default='') - subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_trasladados = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - estatus = TextField(default='Generado') - notas = TextField(default='') - factura = ForeignKeyField(Facturas, null=True) - cancelado = BooleanField(default=False) - vendedor = TextField(default='') - comision = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - cambio = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('fecha',) - - def _get_folio(self, serie): - inicio = (Tickets - .select(fn.Max(Tickets.folio).alias('mf')) - .where(Tickets.serie==serie) - .order_by(SQL('mf')) - .scalar()) - - if inicio is None: - inicio = 1 - else: - inicio += 1 - - return inicio - - def _without_tax(self, importe, obj): - # ~ Por ahora se asume que solo tiene IVA trasladado 0.16 - for tax in obj.impuestos: - tasa = 1.0 + float(tax.tasa) - base = round(importe / tasa, DECIMALES) - return base - - def _calcular_totales(self, ticket, productos): - subtotal = 0 - descuento_cfdi = 0 - totals_tax = {} - total_trasladados = None - - for producto in productos: - id_producto = producto.pop('id') - p = Productos.get(Productos.id==id_producto) - producto['descripcion'] = p.descripcion - producto['ticket'] = ticket.id - producto['producto'] = id_producto - - cantidad = float(producto['cantidad']) - valor_unitario = self._without_tax(self, producto['valor_unitario'], p) - descuento = float(producto['descuento']) - precio_final = valor_unitario - descuento - importe = round(cantidad * precio_final, DECIMALES) - - producto['cantidad'] = cantidad - producto['valor_unitario'] = valor_unitario - producto['descuento'] = descuento - producto['precio_final'] = precio_final - producto['importe'] = importe - - descuento_cfdi += descuento - subtotal += importe - - TicketsDetalle.create(**producto) - - if p.inventario: - p.existencia -= Decimal(cantidad) - p.save() - - base = producto['importe'] - for tax in p.impuestos: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES) - if tax.tipo == 'T' and tax.key != '000': - total_trasladados = (total_trasladados or 0) + impuesto_producto - - if tax.id in totals_tax: - totals_tax[tax.id].base += base - totals_tax[tax.id].importe += impuesto_producto - else: - tax.base = base - tax.importe = impuesto_producto - totals_tax[tax.id] = tax - - - for tax in totals_tax.values(): - if tax.tipo == 'E': - continue - - ticket_tax = { - 'ticket': ticket.id, - 'impuesto': tax.id, - 'base': tax.base, - 'importe': tax.importe, - } - TicketsImpuestos.create(**ticket_tax) - - total = subtotal + (total_trasladados or 0) - data = { - 'subtotal': subtotal + descuento, - 'descuento': descuento_cfdi, - 'total': total, - 'total_trasladados': total_trasladados, - } - return data - - def _get_serie(self, user, invoice=False): - default_serie = DEFAULT_SERIE_TICKET - if invoice: - default_serie = Folios.get_default() - - if user.sucursal is None: - return default_serie - - if invoice: - return user.sucursal.serie_facturas or default_serie - else: - return user.sucursal.serie_tickets or default_serie - - @classmethod - def get_notes(cls, tickets): - rows = Tickets.select(Tickets.notas).where(Tickets.id.in_(tickets)) - return '\n'.join([r.notas for r in rows]) - - @classmethod - def add(cls, values, user): - productos = util.loads(values.pop('productos')) - values['serie'] = cls._get_serie(cls, user) - values['folio'] = cls._get_folio(cls, values['serie']) - - with database_proxy.atomic() as txn: - obj = Tickets.create(**values) - totals = cls._calcular_totales(cls, obj, productos) - obj.subtotal = totals['subtotal'] - obj.descuento = totals['descuento'] - obj.total_trasladados = totals['total_trasladados'] - obj.total = totals['total'] - obj.save() - - row = { - 'id': obj.id, - 'serie': obj.serie, - 'folio': obj.folio, - 'fecha': obj.fecha, - 'estatus': obj.estatus, - 'total': obj.total, - } - data = {'ok': True, 'row': row} - return data - - def _get_folio_invoice(self, serie): - inicio = (Facturas - .select(fn.Max(Facturas.folio).alias('mf')) - .where(Facturas.serie==serie) - .order_by(SQL('mf')) - .scalar()) - - if inicio is None: - inicio = 1 - else: - inicio += 1 - - return inicio - - def _cancel_tickets(self, invoice, tickets): - query = (Tickets - .update(estatus='Facturado', cancelado=True, factura=invoice) - .where(Tickets.id.in_(tickets)) - ) - result = query.execute() - return result - - def _calculate_totals_invoice(self, invoice, tickets): - subtotal = 0 - descuento_cfdi = 0 - totals_tax = {} - total_trasladados = None - total_retenciones = None - notes = Tickets.get_notes(tickets) - - details = TicketsDetalle.select().where(TicketsDetalle.ticket.in_(tickets)) - - for detail in details: - product = {} - p = detail.producto - product['unidad'] = p.unidad.key - product['clave'] = p.clave - product['clave_sat'] = p.clave_sat - - product['factura'] = invoice.id - product['producto'] = p.id - product['descripcion'] = detail.descripcion - - cantidad = float(detail.cantidad) - valor_unitario = float(detail.valor_unitario) - descuento = float(detail.descuento) - precio_final = valor_unitario - descuento - importe = round(cantidad * precio_final, DECIMALES) - - product['cantidad'] = cantidad - product['valor_unitario'] = valor_unitario - product['descuento'] = round(descuento * cantidad, DECIMALES) - product['precio_final'] = precio_final - product['importe'] = round(cantidad * valor_unitario, DECIMALES) - - descuento_cfdi += product['descuento'] - subtotal += product['importe'] - - FacturasDetalle.create(**product) - - base = product['importe'] - product['descuento'] - for tax in p.impuestos: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES) - if tax.tipo == 'T' and tax.key != '000': - total_trasladados = (total_trasladados or 0) + impuesto_producto - elif tax.tipo == 'R' and tax.key != '000': - total_retenciones = (total_retenciones or 0) + impuesto_producto - elif tax.tipo == 'T' and tax.key == '000': - locales_traslados += impuesto_producto - elif tax.tipo == 'R' and tax.key == '000': - locales_retenciones += impuesto_producto - - if tax.id in totals_tax: - totals_tax[tax.id].base += base - totals_tax[tax.id].suma_impuestos += impuesto_producto - else: - tax.base = base - tax.suma_impuestos = impuesto_producto - totals_tax[tax.id] = tax - - for tax in totals_tax.values(): - if tax.tipo == 'E': - continue - - invoice_tax = { - 'factura': invoice.id, - 'impuesto': tax.id, - 'base': tax.base, - 'importe': tax.suma_impuestos, - } - FacturasImpuestos.create(**invoice_tax) - - total = subtotal - descuento_cfdi + \ - (total_trasladados or 0) - (total_retenciones or 0) - total_mn = round(total * invoice.tipo_cambio, DECIMALES) - data = { - 'subtotal': subtotal, - 'descuento': descuento_cfdi, - 'total': total, - 'total_mn': total_mn, - 'total_trasladados': total_trasladados, - 'total_retenciones': total_retenciones, - 'notas': notes, - } - return data - - @classmethod - def invoice(cls, values, user): - is_invoice_day = util.get_bool(values['is_invoice_day']) - id_client = int(values['client']) - tickets = util.loads(values['tickets']) - - if is_invoice_day: - filters = ( - Socios.rfc == 'XAXX010101000' and - Socios.slug == 'publico_en_general') - try: - client = Socios.get(filters) - except Socios.DoesNotExist: - msg = 'No existe el cliente Público en General. Agregalo primero.' - data = {'ok': False, 'msg': msg} - return data - else: - client = Socios.get(Socios.id==id_client) - - if client.forma_pago is None: - msg = 'La Forma de Pago del cliente, no esta asignada' - data = {'ok': False, 'msg': msg} - return data - - emisor = Emisor.select()[0] - data = {} - data['cliente'] = client - data['serie'] = cls._get_serie(cls, user, True) - data['folio'] = cls._get_folio_invoice(cls, data['serie']) - data['forma_pago'] = client.forma_pago.key - data['tipo_cambio'] = 1.00 - data['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal - if client.uso_cfdi is None: - data['uso_cfdi'] = 'P01' - else: - data['uso_cfdi'] = client.uso_cfdi.key - data['regimen_fiscal'] = emisor.regimenes[0].key - - with database_proxy.atomic() as txn: - obj = Facturas.create(**data) - totals = cls._calculate_totals_invoice(cls, obj, tickets) - obj.subtotal = totals['subtotal'] - obj.descuento = totals['descuento'] - obj.total_trasladados = totals['total_trasladados'] - obj.total_retenciones = totals['total_retenciones'] - obj.total = totals['total'] - obj.saldo = totals['total'] - obj.total_mn = totals['total_mn'] - obj.notas = totals['notas'] - obj.save() - cls._cancel_tickets(cls, obj, tickets) - - msg = 'Factura generada correctamente.

Enviando a timbrar' - data = {'ok': True, 'msg': msg, 'id': obj.id} - return data - - def _update_inventory_if_cancel(self, id): - products = TicketsDetalle.select().where(TicketsDetalle.ticket==id) - for p in products: - if p.producto.inventario: - p.producto.existencia += p.cantidad - p.producto.save() - return - - @classmethod - def cancel(cls, values): - id = int(values['id']) - msg = 'Ticket cancelado correctamente' - u = {'cancelado': True, 'estatus': 'Cancelado'} - with database_proxy.atomic() as txn: - obj = Tickets.update(**u).where(Tickets.id==id) - result = bool(obj.execute()) - if result: - cls._update_inventory_if_cancel(cls, id) - row = {'estatus': 'Cancelado'} - return {'ok': result, 'row': row, 'msg': msg} - - def _get_filters(self, values): - opt = values.get('opt', '') - if not opt: - return - - if opt == 'active': - filters = (Tickets.cancelado==False) - return filters - - if opt == 'today': - t = util.today() - filters = ( - (Tickets.fecha.day == t.day) & - (Tickets.fecha.month == t.month) & - (Tickets.fecha.year == t.year) - ) - return filters - - if opt == 'yearmonth': - if values['year'] == '-1': - fy = (Tickets.fecha.year > 0) - else: - fy = (Tickets.fecha.year == int(values['year'])) - if values['month'] == '-1': - fm = (Tickets.fecha.month > 0) - else: - fm = (Tickets.fecha.month == int(values['month'])) - filters = (fy & fm) - return filters - - if opt == 'dates': - dates = util.loads(values['range']) - filters = Tickets.fecha.between( - util.get_date(dates['start']), - util.get_date(dates['end'], True) - ) - return filters - - return - - @classmethod - def get_by(cls, values): - filters = cls._get_filters(cls, values) - - rows = tuple(Tickets - .select( - Tickets.id, - Tickets.serie, - Tickets.folio, - Tickets.fecha, - Tickets.estatus, - Tickets.total) - .where(filters) - .dicts() - ) - return {'ok': True, 'rows': rows} - - @classmethod - def filter_years(cls): - data = [{'id': -1, 'value': 'Todos'}] - rows = (Tickets - .select(Tickets.fecha.year.alias('year')) - .group_by(Tickets.fecha.year) - .order_by(Tickets.fecha.year) - ) - if not rows is None: - data += [{'id': int(r.year), 'value': int(r.year)} for r in rows] - return tuple(data) - - def _get_info_to_pdf(self, id): - data = {} - obj = Tickets.select().where(Tickets.id==id).dicts()[0] - - formapago = SATFormaPago.get(SATFormaPago.key==obj['forma_pago']) - - emisor = util.get_dict(Emisor.select().dicts()[0]) - - emisor['nointerior'] = emisor['no_interior'] - emisor['noexterior'] = emisor['no_exterior'] - emisor['codigopostal'] = emisor['codigo_postal'] - - data['is_ticket'] = True - data['cancelada'] = obj['cancelado'] - data['donativo'] = '' - - data['comprobante'] = obj - data['comprobante']['version'] = 'ticket' - data['comprobante']['folio'] = str(data['comprobante']['folio']) - data['comprobante']['seriefolio'] = '{}-{}'.format( - data['comprobante']['serie'], data['comprobante']['folio']) - data['comprobante']['fecha'] = str(data['comprobante']['fecha']) - # ~ data['comprobante']['lugarexpedicion'] = \ - # ~ 'C.P. de Expedición: {}'.format( - # ~ data['comprobante']['lugar_expedicion']) - data['comprobante']['formadepago'] = str(formapago) - data['comprobante']['totalenletras'] = util.to_letters( - data['comprobante']['total'], 'peso') - - data['emisor'] = emisor - data['receptor'] = {'nombre': 'Público en general'} - - data['conceptos'] = TicketsDetalle.get_by_ticket(id) - data['totales'] = {} - data['totales']['total'] = str(data['comprobante']['total']) - data['totales']['subtotal'] = str(data['comprobante']['subtotal']) - data['totales']['moneda'] = 'peso' - data['totales']['traslados'] = () - data['totales']['retenciones'] = () - data['totales']['taxlocales'] = () - data['timbre'] = {} - data['donataria'] = {} - data['ine'] = {} - - # ~ if obj['descuento']: - # ~ data['totales']['descuento'] = float(obj['descuento']) - - return data - - @classmethod - def get_pdf(cls, id): - obj = Tickets.get(Tickets.id==id) - name = '{}{}.pdf'.format(obj.serie, obj.folio) - data = cls._get_info_to_pdf(cls, id) - doc = util.to_pdf(data, data['emisor']['rfc']) - return doc, name - - def _format_ticket(self, id): - emisor = util.get_dict(Emisor.select().dicts()[0]) - regimen = Emisor.select()[0].regimenes[0].name - ticket = Tickets.select().where(Tickets.id==id).dicts()[0] - products = TicketsDetalle.get_by_print(id) - emisor['name'] = '{}\n'.format(emisor['nombre']) - emisor['rfc'] = 'RFC: {}\n'.format(emisor['rfc']) - emisor['regimen'] = 'Regimen: {}\n'.format(regimen) - interior = '' - if emisor['no_interior']: - interior = ', {}'.format(emisor['no_interior']) - colonia = '' - if emisor['colonia']: - colonia = ', Col. {}'.format(emisor['colonia']) - municipio = '' - if emisor['municipio']: - municipio = ', {}'.format(emisor['municipio']) - estado = '' - if emisor['estado']: - estado = ', {}'.format(emisor['estado']) - cp = '' - if emisor['codigo_postal']: - cp = ', C.P. {}'.format(emisor['codigo_postal']) - pais = '' - if emisor['pais']: - pais = ', {}'.format(emisor['pais']) - direccion = '{} {}{}{}{}{}{}{}\n\n'.format(emisor['calle'], - emisor['no_exterior'], interior, colonia, municipio, estado, cp, - pais) - emisor['address'] = direccion - - ticket['title'] = 'Ticket: {}{}\n\n'.format(ticket['serie'], - ticket['folio']) - ticket['date'] = 'Fecha y hora: {}\n'.format(ticket['fecha']) - ticket['letters'] = '{}\n\n'.format( - util.to_letters(ticket['total'], 'peso')) - ticket['total'] = 'TOTAL: $ {:>12,.2f}\n\n'.format(ticket['total']) - - data = { - 'emisor': emisor, - 'receptor': {'name': '{}\n\n'.format(PUBLIC)}, - 'ticket': ticket, - 'products': products, - } - return data - - def _get_info_printer(self): - info = {} - value = Configuracion.get_('txt_ticket_printer') - if not value: - return info - - values = value.split(':') - if len(values) == 1: - info = {'ip': values[0], 'usb': ()} - elif len(values) == 2: - info = {'ip': '', 'usb': (int(values[0], 16), int(values[1], 16))} - elif len(values) == 5: - info = {'ip': '', 'usb': ( - int(values[0], 16), - int(values[1], 16), - int(values[2]), - int(values[3], 16), - int(values[4], 16), - ) - } - - return info - - @classmethod - def printer(cls, values): - id = int(values['id']) - - info_printer = cls._get_info_printer(cls) - if not info_printer: - msg = 'Es necesario configurar una impresora.' - result = {'ok': False, 'msg': msg} - return result - - data = cls._format_ticket(cls, id) - result = util.print_ticket(data, info_printer) - msg = 'Ticket impreso correctamente' - if not result: - msg = 'Asegurate de que la impresora este conectada y funcionando.' - result = {'ok': result, 'msg': msg} - return result - - -class TicketsDetalle(BaseModel): - ticket = ForeignKeyField(Tickets) - producto = ForeignKeyField(Productos, null=True) - descripcion = TextField(default='') - cantidad = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - precio_final = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('ticket',) - - def _with_tax(self, product): - precio_final = float(product.precio_final) - base = float(product.precio_final) - for tax in product.producto.impuestos: - impuesto_producto = round(float(tax.tasa) * base, DECIMALES) - precio_final += impuesto_producto - - return precio_final - - @classmethod - def get_by_print(cls, id): - products = TicketsDetalle.select().where(TicketsDetalle.ticket==id) - lines = [] - for p in products: - price_with_tax = cls._with_tax(cls, p) - importe = round(price_with_tax * float(p.cantidad), DECIMALES) - l = '{:>6,.2f} {:<4} {:<14} {:>9,.2f} {:>10,.2f}\n'.format( - p.cantidad, p.producto.unidad.name[:5], p.descripcion[:14], - price_with_tax, importe - ) - lines.append(l) - - return lines - - @classmethod - def get_by_ticket(cls, id): - data = [] - - productos = TicketsDetalle.select().where(TicketsDetalle.ticket==id) - - for p in productos: - producto = {} - producto['noidentificacion'] = p.producto.clave - producto['descripcion'] = p.descripcion - producto['unidad'] = p.producto.unidad.name - producto['cantidad'] = str(p.cantidad) - price_with_tax = cls._with_tax(cls, p) - producto['valorunitario'] = str(price_with_tax) - importe = round(price_with_tax * float(p.cantidad), DECIMALES) - producto['importe'] = str(importe) - data.append(producto) - - return data - - -class TicketsImpuestos(BaseModel): - ticket = ForeignKeyField(Tickets) - impuesto = ForeignKeyField(SATImpuestos) - base = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('ticket',) - indexes = ( - (('ticket', 'impuesto'), True), - ) - - -class SeriesProductos(BaseModel): - factura = ForeignKeyField(FacturasDetalle, null=True) - ticket = ForeignKeyField(TicketsDetalle, null=True) - serie = TextField(default='') - - class Meta: - order_by = ('serie',) - - -class Departamentos(BaseModel): - nombre = TextField(default='', unique=True) - descripcion = TextField(default='') - - class Meta: - order_by = ('nombre',) - - -class Puestos(BaseModel): - departamento = ForeignKeyField(Departamentos, null=True) - nombre = TextField(default='', unique=True) - descripcion = TextField(default='') - - class Meta: - order_by = ('nombre',) - - @classmethod - def get_by_depto(cls, puesto, depto): - departamento = None - if depto: - with database_proxy.transaction(): - departamento, _ = Departamentos.get_or_create(nombre=depto) - data = {'departamento': departamento, 'nombre': puesto} - obj, _ = Puestos.get_or_create(**data) - return obj - - -class Empleados(BaseModel): - num_empleado = TextField(default='') - rfc = TextField(default='', unique=True) - curp = TextField(default='', unique=True) - nombre = TextField(default='') - paterno = TextField(default='') - materno = TextField(default='') - nombre_completo = TextField(default='') - es_activo = BooleanField(default=True) - es_extranjero = BooleanField(default=False) - fecha_alta = DateField(default=util.now) - fecha_ingreso = DateField(null=True) - imss = TextField(default='') - tipo_contrato = ForeignKeyField(SATTipoContrato) - es_sindicalizado = BooleanField(default=False) - tipo_jornada = ForeignKeyField(SATTipoJornada, null=True) - tipo_regimen = ForeignKeyField(SATTipoRegimen) - puesto = ForeignKeyField(Puestos, null=True) - riesgo_puesto = ForeignKeyField(SATRiesgoPuesto, null=True) - periodicidad_pago = ForeignKeyField(SATPeriodicidadPago) - banco = ForeignKeyField(SATBancos, null=True) - cuenta_bancaria = TextField(default='') - clabe = TextField(default='') - salario_base = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - salario_diario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - estado = ForeignKeyField(SATEstados) - codigo_postal = TextField(default='') - notas = TextField(default='') - correo = TextField(default='') - - class Meta: - order_by = ('nombre_completo',) - indexes = ( - (('num_empleado', 'rfc'), True), - ) - - def _validate_import(self, row): - sn = {'si': True, 'no': False} - data = row.copy() - data['nombre_completo'] = '{} {} {}'.format( - row['nombre'], row['paterno'], row['materno']).strip() - if row['fecha_ingreso']: - data['fecha_ingreso'] = util.calc_to_date(row['fecha_ingreso']) - data['tipo_contrato'] = SATTipoContrato.get_by_key(row['tipo_contrato']) - data['es_sindicalizado'] = sn.get(row['es_sindicalizado'].lower(), False) - data['tipo_jornada'] = SATTipoJornada.get_by_key(row['tipo_jornada']) - data['tipo_regimen'] = SATTipoRegimen.get_by_key(row['tipo_regimen']) - data['puesto'] = Puestos.get_by_depto(row['puesto'], row['departamento']) - data['riesgo_puesto'] = SATRiesgoPuesto.get_by_key(row['riesgo_puesto']) - data['periodicidad_pago'] = SATPeriodicidadPago.get_by_key(row['periodicidad_pago']) - data['banco'] = SATBancos.get_by_key(row['banco']) - data['estado'] = SATEstados.get_by_key(row['estado']) - del data['departamento'] - return data - - def _import(self): - emisor = Emisor.select()[0] - rows, msg = util.import_employees(emisor.rfc) - if not rows: - return {'ok': False, 'msg': msg} - - en = 0 - ea = 0 - for row in rows: - # ~ if row['rfc'] == 'BASM740115RW0': - # ~ continue - data = self._validate_import(self, row) - w = (Empleados.rfc==row['rfc']) - with database_proxy.transaction(): - if Empleados.select().where(w).exists(): - q = Empleados.update(**data).where(w) - q.execute() - ea += 1 - else: - obj = Empleados.create(**data) - en += 1 - - msg = 'Empleados encontrados: {}
'.format(len(rows)) - msg += 'Empleados nuevos: {}
'.format(en) - msg += 'Empleados actualizados: {}
'.format(ea) - msg += 'Empleados no importados: {}'.format(len(rows) - en - ea) - return {'ok': True, 'msg': msg} - - def _get(self): - rows = (Empleados - .select( - Empleados.id, - Empleados.num_empleado, - Empleados.rfc, - Empleados.curp, - Empleados.nombre_completo, - Empleados.imss, - Empleados.salario_base, - Empleados.salario_diario, - Empleados.fecha_ingreso) - .dicts() - ) - return {'ok': True, 'rows': tuple(rows)} - - @classmethod - def get_by(cls, values): - if not 'opt' in values: - return cls._get(cls) - - if values['opt'] == 'import': - return cls._import(cls) - - @classmethod - def remove(cls, id): - try: - q = Empleados.delete().where(Empleados.id==id) - return bool(q.execute()) - except IntegrityError: - return False - - -class CfdiNomina(BaseModel): - empleado = ForeignKeyField(Empleados) - version = TextField(default=CURRENT_CFDI) - serie = TextField(default='N') - folio = IntegerField(default=0) - fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S']) - fecha_timbrado = DateTimeField(null=True) - forma_pago = TextField(default='') - condiciones_pago = TextField(default='') - subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - moneda = TextField(default='MXN') - tipo_cambio = DecimalField(default=1.0, max_digits=15, decimal_places=6, - auto_round=True) - total = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_mn = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - tipo_comprobante = TextField(default='N') - metodo_pago = TextField(default='PUE') - lugar_expedicion = TextField(default='') - confirmacion = TextField(default='') - uso_cfdi = TextField(default='') - total_retenciones = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - total_trasladados = DecimalField( - max_digits=20, decimal_places=6, auto_round=True, null=True) - xml = TextField(default='') - uuid = UUIDField(null=True) - estatus = TextField(default='Guardado') - estatus_sat = TextField(default='Vigente') - regimen_fiscal = TextField(default='') - notas = TextField(default='') - saldo = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - pagada = BooleanField(default=False) - cancelada = BooleanField(default=False) - fecha_cancelacion = DateTimeField(null=True) - acuse = TextField(default='') - tipo_relacion = TextField(default='') - error = TextField(default='') - version_nomina = TextField(default=CURRENT_CFDI_NOMINA) - registro_patronal = TextField(default='') - rfc_patron_origen = TextField(default='') - tipo_nomina = ForeignKeyField(SATTipoNomina) - fecha_pago = DateField() - fecha_inicial_pago = DateField() - fecha_final_pago = DateField() - dias_pagados = DecimalField(default=0.0, max_digits=12, decimal_places=2, - auto_round=True) - origen_recurso = ForeignKeyField(SATOrigenRecurso, null=True) - monto_recurso_propio = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - - class Meta: - order_by = ('fecha',) - - def _get_serie(self): - serie = Configuracion.get_('chk_config_serie_nomina') - if not serie: - serie = DEFAULT_SAT_NOMINA['SERIE'] - return serie - - def _get_folio(self, serie): - folio = int(Configuracion.get_('chk_config_folio_nomina') or '0') - inicio = (CfdiNomina - .select(fn.Max(CfdiNomina.folio).alias('mf')) - .where(CfdiNomina.serie==serie) - .order_by(SQL('mf')) - .scalar()) - - if inicio is None: - new = 1 - else: - new = inicio + 1 - - if folio > new: - new = folio - - return new - - def _validate_nomina(self, row): - sn = {'si': True, 'no': False} - data = row.copy() - rfc = data.pop('rfc') - try: - data['empleado'] = Empleados.get(Empleados.rfc==rfc) - except Empleados.DoesNotExist: - msg = 'No existe el Empleado con RFC: {}'.format(rfc) - return {}, msg - - tipo_nomina = SATTipoNomina.get_by_key(row['tipo_nomina']) - if tipo_nomina is None: - msg = 'RFC: {}, Tipo de Nómina no existe: {}'.format(row['tipo_nomina']) - return {}, msg - - data['serie'] = self._get_serie(self) - data['folio'] = self._get_folio(self, data['serie']) - data['forma_pago'] = DEFAULT_SAT_NOMINA['FORMA_PAGO'] - data['uso_cfdi'] = DEFAULT_SAT_NOMINA['USO_CFDI'] - data['tipo_nomina'] = tipo_nomina - data['fecha_pago'] = util.calc_to_date(row['fecha_pago']) - data['fecha_inicial_pago'] = util.calc_to_date(row['fecha_inicial_pago']) - data['fecha_final_pago'] = util.calc_to_date(row['fecha_final_pago']) - data['dias_pagados'] = util.get_days(data['fecha_inicial_pago'], data['fecha_final_pago']) - - return data, '' - - def _validate_percepciones(self, headers, row): - total_gravado = 0.0 - total_exento = 0.0 - total_jubilacion = 0.0 - total_separacion = 0.0 - - data = [] - for i, key in enumerate(headers[::2]): - gravado = 0.0 - exento = 0.0 - if isinstance(row[i * 2], float): - gravado = round(row[i * 2], DECIMALES) - if isinstance(row[i * 2 + 1], float): - exento = round(row[i * 2 + 1], DECIMALES) - - if not gravado and not exento: - continue - tp = SATTipoPercepcion.get_by_key(key) - if tp is None: - continue - - total_gravado += gravado - total_exento += exento - if key in ('039', '044'): - total_jubilacion += gravado + exento - elif key in ('022', '023', '025'): - total_separacion += gravado + exento - new = { - 'tipo_percepcion': tp, - 'importe_gravado': gravado, - 'importe_exento': exento, - } - data.append(new) - - total_sueldos = round(total_gravado + total_exento, DECIMALES) - totals = { - 'total_gravado': total_gravado, - 'total_exento': total_exento, - 'total_jubilacion': total_jubilacion, - 'total_separacion': total_separacion, - 'total_sueldos': total_sueldos, - 'total_percepciones': round( - total_sueldos + total_jubilacion + total_separacion, DECIMALES) - } - - return data, totals, '' - - def _validate_deducciones(self, headers, row): - total_retenciones = 0.0 - total_otras_deducciones = 0.0 - - data = [] - for i, value in enumerate(row): - key = headers[0][i] - importe = 0.0 - if isinstance(value, float): - importe = round(value, DECIMALES) - - if not importe: - continue - - td = SATTipoDeduccion.get_by_key(key) - if td is None: - continue - - if key == '002': - total_retenciones += importe - else: - total_otras_deducciones += importe - - new = { - 'tipo_deduccion': td, - 'importe': importe, - } - data.append(new) - - totals = { - 'total_retenciones': total_retenciones, - 'total_otras_deducciones': total_otras_deducciones, - 'total_deducciones': round( - total_retenciones + total_otras_deducciones, DECIMALES) - } - - return data, totals, '' - - def _validate_otros_pagos(self, headers, row): - total_otros_pagos = 0.0 - - data = [] - subsidio_causado = round(row[0], DECIMALES) - for i, value in enumerate(row): - if not i: - continue - - key = headers[0][i] - importe = 0.0 - if isinstance(value, float): - importe = round(value, DECIMALES) - - if not importe: - continue - - td = SATTipoOtroPago.get_by_key(key) - if td is None: - continue - - total_otros_pagos += importe - - new = { - 'tipo_otro_pago': td, - 'importe': importe, - } - if key == '002': - new['subsidio_causado'] = subsidio_causado - data.append(new) - - totals = {'total_otros_pagos': total_otros_pagos} - - return data, totals, '' - - def _validate_horas_extras(self, row): - data = [] - for i, key in enumerate(row[::4]): - days = 0 - if isinstance(row[i * 4], float): - days = int(row[i * 4]) - key = row[i * 4 + 1] - the = SATTipoHoras.get_by_key(key) - if the is None: - continue - - hours = 0 - if isinstance(row[i * 4 + 2], float): - hours = int(row[i * 4 + 2]) - importe = 0.0 - if isinstance(row[i * 4 + 3], float): - importe = round(row[i * 4 + 3], DECIMALES) - if not hours or not importe: - continue - - new = { - 'dias': days, - 'tipos_horas': the, - 'horas_extra': hours, - 'importe_pagado': importe, - } - data.append(new) - - return data, '' - - def _validate_incapacidades(self, row): - data = [] - for i, key in enumerate(row[::3]): - key = row[i * 3] - ti = SATTipoIncapacidad.get_by_key(key) - if ti is None: - continue - - days = 0 - if isinstance(row[i * 3 + 1], float): - days = int(row[i * 3 + 1]) - importe = 0.0 - if isinstance(row[i * 3 + 2], float): - importe = round(row[i * 3 + 2], DECIMALES) - if not days or not importe: - continue - - new = { - 'dias': ti, - 'tipo': days, - 'importe': importe, - } - data.append(new) - - return data, '' - - def _validate_exists(self, values): - result = (CfdiNomina - .select() - .where( - (CfdiNomina.empleado==values['empleado']) & - (CfdiNomina.fecha_pago==values['fecha_pago']) & - (CfdiNomina.fecha_inicial_pago==values['fecha_inicial_pago']) & - (CfdiNomina.fecha_final_pago==values['fecha_final_pago']) & - (CfdiNomina.total==values['total'])) - .exists()) - return result - - def _import(self): - util.log_file('nomina', kill=True) - emisor = Emisor.select()[0] - data, msg = util.import_nomina(emisor.rfc) - if not data: - return {'ok': False, 'msg': msg} - - hp = data['percepciones'][0] - percepciones = data['percepciones'][2:] - hd = data['deducciones'][:1] - deducciones = data['deducciones'][2:] - ho = data['otros_pagos'][:1] - otros_pagos = data['otros_pagos'][2:] - horas_extras = data['horas_extras'][2:] - incapacidades = data['incapacidades'][2:] - - for i, row in enumerate(data['nomina']): - row['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal - row['regimen_fiscal'] = emisor.regimenes[0].key - row['registro_patronal'] = emisor.registro_patronal - new_nomina, msg = self._validate_nomina(self, row) - if msg: - util.log_file('nomina', msg) - continue - - new_percepciones, total_percepciones, msg = \ - self._validate_percepciones(self, hp, percepciones[i]) - if msg: - util.log_file('nomina', msg) - continue - - new_deducciones, total_deducciones, msg = \ - self._validate_deducciones(self, hd, deducciones[i]) - if msg: - util.log_file('nomina', msg) - continue - - new_otros_pagos, total_otros_pagos, msg = \ - self._validate_otros_pagos(self, ho, otros_pagos[i]) - if msg: - util.log_file('nomina', msg) - continue - - new_horas_extras, msg = self._validate_horas_extras(self, horas_extras[i]) - if msg: - util.log_file('nomina', msg) - continue - - new_incapacidades, msg = self._validate_incapacidades(self, incapacidades[i]) - if msg: - util.log_file('nomina', msg) - continue - - totals = total_percepciones.copy() - totals.update(total_deducciones) - totals.update(total_otros_pagos) - totals['subtotal'] = round(totals['total_percepciones'] + - totals['total_otros_pagos'], DECIMALES) - totals['descuento'] = totals['total_deducciones'] - totals['total'] = round(totals['subtotal'] - - totals['descuento'], DECIMALES) - - new_nomina['subtotal'] = totals['subtotal'] - new_nomina['descuento'] = totals['descuento'] - new_nomina['total'] = totals['total'] - new_nomina['total_mn'] = totals['total'] - - if self._validate_exists(self, new_nomina): - info = '{}'.format(new_nomina['empleado'].nombre_completo) - msg = 'Nomina existente: {}'.format(info) - util.log_file('nomina', msg) - continue - - try: - with database_proxy.transaction(): - obj = CfdiNomina.create(**new_nomina) - for row in new_percepciones: - row['cfdi'] = obj - CfdiNominaPercepciones.create(**row) - for row in new_deducciones: - row['cfdi'] = obj - CfdiNominaDeducciones.create(**row) - for row in new_otros_pagos: - row['cfdi'] = obj - CfdiNominaOtroPago.create(**row) - for row in new_horas_extras: - row['cfdi'] = obj - CfdiNominaHorasExtra.create(**row) - for row in new_incapacidades: - row['cfdi'] = obj - CfdiNominaIncapacidad.create(**row) - - concepto = { - 'cfdi': obj, - 'valor_unitario': totals['subtotal'], - 'importe': totals['subtotal'], - 'descuento': totals['total_deducciones'], - } - CfdiNominaDetalle.create(**concepto) - - totals['cfdi'] = obj - CfdiNominaTotales.create(**totals) - except Exception as e: - msg = 'ERROR: {}-{}'.format(new_nomina['serie'], new_nomina['folio']) - util.log_file('nomina', msg) - util.log_file('nomina', str(e)) - continue - - msg = 'Nómina importada correctamente' - return {'ok': True, 'msg': msg} - - def _get(self, where=''): - if not where: - where = ((CfdiNomina.uuid.is_null(True)) & (CfdiNomina.cancelada==False)) - rows = (CfdiNomina - .select( - CfdiNomina.id, - CfdiNomina.serie, - CfdiNomina.folio, - CfdiNomina.fecha, - CfdiNomina.estatus, - CfdiNomina.fecha_pago, - CfdiNomina.total, - Empleados.nombre_completo.alias('empleado') - ) - .where(where) - .join(Empleados) - .switch(CfdiNomina) - .order_by(CfdiNomina.id) - .dicts() - ) - return {'ok': True, 'rows': tuple(rows)} - - def _make_xml(self, cfdi, auth): - emisor = Emisor.select()[0] - empleado = cfdi.empleado - certificado = Certificado.select()[0] - totals = CfdiNominaTotales.select().where(CfdiNominaTotales.cfdi==cfdi)[0] - - comprobante = {} - relacionados = {} - complementos = None - - comprobante['Serie'] = cfdi.serie - comprobante['Folio'] = str(cfdi.folio) - comprobante['Fecha'] = cfdi.fecha.isoformat()[:19] - comprobante['FormaPago'] = cfdi.forma_pago - comprobante['NoCertificado'] = certificado.serie - comprobante['Certificado'] = certificado.cer_txt - comprobante['SubTotal'] = FORMAT.format(cfdi.subtotal) - comprobante['Moneda'] = cfdi.moneda - comprobante['Total'] = FORMAT.format(cfdi.total) - comprobante['TipoDeComprobante'] = cfdi.tipo_comprobante - comprobante['MetodoPago'] = cfdi.metodo_pago - comprobante['LugarExpedicion'] = cfdi.lugar_expedicion - if cfdi.descuento: - comprobante['Descuento'] = FORMAT.format(cfdi.descuento) - - # ~ if invoice.tipo_relacion: - # ~ relacionados = { - # ~ 'tipo': invoice.tipo_relacion, - # ~ 'cfdis': FacturasRelacionadas.get_(invoice), - # ~ } - - cfdi_emisor = { - 'Rfc': emisor.rfc, - 'Nombre': emisor.nombre, - 'RegimenFiscal': cfdi.regimen_fiscal, - } - - receptor = { - 'Rfc': cfdi.empleado.rfc, - 'Nombre': cfdi.empleado.nombre_completo, - 'UsoCFDI': cfdi.uso_cfdi, - } - - conceptos = [] - rows = CfdiNominaDetalle.select().where(CfdiNominaDetalle.cfdi==cfdi) - for row in rows: - concepto = { - 'ClaveProdServ': row.clave_sat, - 'Cantidad': '1', - 'ClaveUnidad': row.clave_unidad, - 'Descripcion': row.descripcion, - 'ValorUnitario': FORMAT.format(row.valor_unitario), - 'Importe': FORMAT.format(row.importe), - } - if row.descuento: - concepto['Descuento'] = FORMAT.format(row.descuento) - - conceptos.append(concepto) - - nomina = { - 'Version': cfdi.version_nomina, - 'TipoNomina': cfdi.tipo_nomina.key, - 'FechaPago': str(cfdi.fecha_pago), - 'FechaInicialPago': str(cfdi.fecha_inicial_pago), - 'FechaFinalPago': str(cfdi.fecha_final_pago), - 'NumDiasPagados': FORMAT3.format(cfdi.dias_pagados), - } - if totals.total_percepciones: - nomina['TotalPercepciones'] = FORMAT.format(totals.total_percepciones) - if totals.total_deducciones: - nomina['TotalDeducciones'] = FORMAT.format(totals.total_deducciones) - if totals.total_otros_pagos: - nomina['TotalOtrosPagos'] = FORMAT.format(totals.total_otros_pagos) - - nomina_emisor = {} - if emisor.curp: - nomina_emisor['Curp'] = emisor.curp - if emisor.registro_patronal: - nomina_emisor['RegistroPatronal'] = emisor.registro_patronal - - nomina_receptor = { - 'Curp': empleado.curp, - 'TipoContrato': empleado.tipo_contrato.key, - 'Sindicalizado': {True: 'Si', False: 'No'}.get(empleado.es_sindicalizado), - 'TipoJornada': empleado.tipo_jornada.key, - 'TipoRegimen': empleado.tipo_regimen.key, - 'NumEmpleado': str(empleado.num_empleado), - 'RiesgoPuesto': empleado.riesgo_puesto.key, - 'PeriodicidadPago': empleado.periodicidad_pago.key, - 'ClaveEntFed': empleado.estado.key, - } - - if empleado.imss: - nomina_receptor['NumSeguridadSocial'] = empleado.imss.replace('-', '') - - if empleado.fecha_ingreso: - nomina_receptor['FechaInicioRelLaboral'] = str(empleado.fecha_ingreso) - days = util.get_days(empleado.fecha_ingreso, cfdi.fecha_final_pago) - weeks = days // 7 - if weeks: - ant = 'P{}W'.format(weeks) - else: - ant = 'P{}D'.format(days) - nomina_receptor['Antigüedad'] = ant - - if empleado.puesto: - if empleado.puesto.departamento: - nomina_receptor['Departamento'] = empleado.puesto.departamento.nombre - nomina_receptor['Puesto'] = empleado.puesto.nombre - - if empleado.clabe: - nomina_receptor['CuentaBancaria'] = empleado.clabe.replace('-', '') - elif empleado.cuenta_bancaria: - nomina_receptor['CuentaBancaria'] = empleado.cuenta_bancaria.replace('-', '') - nomina_receptor['Banco'] = empleado.banco.key - - if empleado.salario_base: - nomina_receptor['SalarioBaseCotApor'] = FORMAT.format(empleado.salario_base) - if empleado.salario_diario: - nomina_receptor['SalarioDiarioIntegrado'] = FORMAT.format(empleado.salario_diario) - - percepciones = { - 'TotalSueldos': FORMAT.format(totals.total_sueldos), - 'TotalGravado': FORMAT.format(totals.total_gravado), - 'TotalExento': FORMAT.format(totals.total_exento), - } - if totals.total_separacion: - percepciones['TotalSeparacionIndemnizacion'] = FORMAT.format(totals.total_separacion) - if totals.total_jubilacion: - percepciones['TotalJubilacionPensionRetiro'] = FORMAT.format(totals.total_jubilacion) - - rows = CfdiNominaPercepciones.select().where( - CfdiNominaPercepciones.cfdi==cfdi) - details = [] - for row in rows: - concepto = row.concepto or row.tipo_percepcion.nombre or row.tipo_percepcion.name - p = { - 'TipoPercepcion': row.tipo_percepcion.key, - 'Clave': row.tipo_percepcion.clave or row.tipo_percepcion.key, - 'Concepto': concepto[:100], - 'ImporteGravado': FORMAT.format(row.importe_gravado), - 'ImporteExento': FORMAT.format(row.importe_exento), - } - details.append(p) - percepciones['details'] = details - - rows = CfdiNominaHorasExtra.select().where(CfdiNominaHorasExtra.cfdi==cfdi) - details = [] - for row in rows: - n = { - 'Dias': str(row.dias), - 'TipoHoras': row.tipos_horas.key, - 'HorasExtra': str(row.horas_extra), - 'ImportePagado': FORMAT.format(row.importe_pagado), - } - details.append(n) - percepciones['hours_extra'] = details - - deducciones = { - 'TotalOtrasDeducciones': FORMAT.format(totals.total_otras_deducciones), - } - if totals.total_retenciones: - deducciones['TotalImpuestosRetenidos'] = \ - FORMAT.format(totals.total_retenciones) - - rows = CfdiNominaDeducciones.select().where(CfdiNominaDeducciones.cfdi==cfdi) - details = [] - for row in rows: - concepto = row.concepto or row.tipo_deduccion.nombre or row.tipo_deduccion.name - p = { - 'TipoDeduccion': row.tipo_deduccion.key, - 'Clave': row.tipo_deduccion.clave or row.tipo_deduccion.key, - 'Concepto': concepto[:100], - 'Importe': FORMAT.format(row.importe), - } - details.append(p) - deducciones['details'] = details - - rows = CfdiNominaOtroPago.select().where(CfdiNominaOtroPago.cfdi==cfdi) - otros_pagos = [] - for row in rows: - concepto = row.concepto or row.tipo_otro_pago.nombre or row.tipo_otro_pago.name - p = { - 'TipoOtroPago': row.tipo_otro_pago.key, - 'Clave': row.tipo_otro_pago.clave or row.tipo_otro_pago.key, - 'Concepto': concepto[:100], - 'Importe': FORMAT.format(row.importe), - } - if row.tipo_otro_pago.key == '002' and row.subsidio_causado: - p['subsidio'] = { - 'SubsidioCausado': FORMAT.format(row.subsidio_causado) - } - otros_pagos.append(p) - - rows = CfdiNominaIncapacidad.select().where(CfdiNominaIncapacidad.cfdi==cfdi) - incapacidades = [] - for row in rows: - n = { - 'DiasIncapacidad': str(row.dias), - 'TipoIncapacidad': row.tipo.key, - 'ImporteMonetario': FORMAT.format(row.importe), - } - incapacidades.append(n) - - nomina = { - 'nomina': nomina, - 'emisor': nomina_emisor, - 'receptor': nomina_receptor, - 'percepciones': percepciones, - 'deducciones': deducciones, - 'otros_pagos': otros_pagos, - 'incapacidades': incapacidades, - } - - data = { - 'comprobante': comprobante, - 'relacionados': relacionados, - 'emisor': cfdi_emisor, - 'receptor': receptor, - 'conceptos': conceptos, - 'complementos': complementos, - 'nomina': nomina, - 'impuestos': {}, - 'donativo': {}, - } - return util.make_xml(data, certificado, auth) - - def _stamp_id(self, id): - auth = Emisor.get_auth() - obj = CfdiNomina.get(CfdiNomina.id==id) - obj.xml = self._make_xml(self, obj, auth) - obj.estatus = 'Generado' - obj.save() - - result = util.timbra_xml(obj.xml, auth) - # ~ print (result) - if result['ok']: - obj.xml = result['xml'] - obj.uuid = result['uuid'] - obj.fecha_timbrado = result['fecha'] - obj.estatus = 'Timbrado' - obj.error = '' - obj.save() - # ~ cls._sync(cls, id, auth) - else: - msg = result['error'] - obj.estatus = 'Error' - obj.error = msg - obj.save() - - - return result['ok'], obj.error - - def _stamp(self): - msg = '' - where = ((CfdiNomina.uuid.is_null(True)) & (CfdiNomina.cancelada==False)) - rows = CfdiNomina.select().where(where).order_by(CfdiNomina.id) - util.log_file('nomina', kill=True) - msg_error = '' - ok_stamp = 0 - for row in rows: - msg = 'Timbrando el recibo: {}-{}'.format(row.serie, row.folio) - util.log_file('nomina', msg) - result, msg = self._stamp_id(self, row.id) - if result: - msg = 'Recibo: {}-{}, timbrado correctamente'.format(row.serie, row.folio) - ok_stamp += 1 - util.log_file('nomina', msg) - else: - msg = 'Error la timbrar: {}-{}, {}'.format(row.serie, row.folio, msg) - util.log_file('nomina', msg) - msg_error = msg - break - - ok = False - if ok_stamp: - msg = 'Se timbraron {} recibos'.format(ok_stamp) - ok = True - - error = False - if msg_error: - error = True - - return {'ok': ok, 'msg_ok': msg, 'error': error, 'msg_error': msg_error} - - @classmethod - def get_by(cls, values): - if not values: - return cls._get(cls) - - if values['opt'] == 'dates': - dates = util.loads(values['range']) - filters = CfdiNomina.fecha.between( - util.get_date(dates['start']), - util.get_date(dates['end'], True) - ) - return cls._get(cls, filters) - - if values['opt'] == 'yearmonth': - if values['year'] == '-1': - fy = (CfdiNomina.fecha.year > 0) - else: - fy = (CfdiNomina.fecha.year == int(values['year'])) - if values['month'] == '-1': - fm = (CfdiNomina.fecha.month > 0) - else: - fm = (CfdiNomina.fecha.month == int(values['month'])) - filters = (fy & fm) - return cls._get(cls, filters) - - if values['opt'] == 'import': - return cls._import(cls) - - if values['opt'] == 'stamp': - return cls._stamp(cls) - - @classmethod - def remove(cls, id): - obj = CfdiNomina.get(CfdiNomina.id==id) - if obj.uuid: - return False - - q = CfdiNominaDetalle.delete().where(CfdiNominaDetalle.cfdi==obj) - q.execute() - q = CfdiNominaTotales.delete().where(CfdiNominaTotales.cfdi==obj) - q.execute() - q = CfdiNominaPercepciones.delete().where(CfdiNominaPercepciones.cfdi==obj) - q.execute() - q = CfdiNominaDeducciones.delete().where(CfdiNominaDeducciones.cfdi==obj) - q.execute() - q = CfdiNominaOtroPago.delete().where(CfdiNominaOtroPago.cfdi==obj) - q.execute() - q = CfdiNominaHorasExtra.delete().where(CfdiNominaHorasExtra.cfdi==obj) - q.execute() - q = CfdiNominaIncapacidad.delete().where(CfdiNominaIncapacidad.cfdi==obj) - q.execute() - - return bool(obj.delete_instance()) - - @classmethod - def get_xml(cls, id): - obj = CfdiNomina.get(CfdiNomina.id==id) - name = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.empleado.rfc) - # ~ cls._sync_xml(cls, obj) - return obj.xml, name - - @classmethod - def filter_years(cls): - data = [{'id': -1, 'value': 'Todos'}] - rows = (CfdiNomina - .select(CfdiNomina.fecha.year.alias('year')) - .group_by(CfdiNomina.fecha.year) - .order_by(CfdiNomina.fecha.year) - ) - if not rows is None: - data += [{'id': int(r.year), 'value': int(r.year)} for r in rows] - return tuple(data) - - @classmethod - def cancel(cls, id): - msg = 'Recibo cancelado correctamente' - auth = Emisor.get_auth() - certificado = Certificado.select()[0] - obj = CfdiNomina.get(CfdiNomina.id==id) - - if obj.uuid is None: - msg = 'Solo se pueden cancelar recibos timbrados' - return {'ok': False, 'msg': msg} - - data, result = util.cancel_xml(auth, obj.uuid, certificado) - if data['ok']: - data['msg'] = 'Recibo cancelado correctamente' - data['row']['estatus'] = 'Cancelado' - obj.estatus = data['row']['estatus'] - obj.error = '' - obj.cancelada = True - obj.fecha_cancelacion = result['Fecha'] - obj.acuse = result['Acuse'] - else: - obj.error = data['msg'] - obj.save() - return data - - -class CfdiNominaDetalle(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - clave_sat = TextField(default=DEFAULT_SAT_NOMINA['CLAVE']) - cantidad = DecimalField(default=1.0, max_digits=18, decimal_places=6, - auto_round=True) - clave_unidad = TextField(default=DEFAULT_SAT_NOMINA['UNIDAD']) - descripcion = TextField(default=DEFAULT_SAT_NOMINA['DESCRIPCION']) - valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - importe = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6, - auto_round=True) - - class Meta: - order_by = ('cfdi',) - - -class CfdiNominaTotales(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_percepciones = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - total_gravado = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_exento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_deducciones = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_otros_pagos = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_sueldos = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_separacion = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_jubilacion = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_retenciones = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - total_otras_deducciones = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - total = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - - -class CfdiNominaJubilacion(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - total_una_exhibicion = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - total_parcialidad = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - monto_diario = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - ingreso_acumulable = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - ingreso_no_acumulable = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - - -class CfdiNominaSeparacion(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - total_pagado = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - years_servicio = IntegerField(default=0) - ultimo_sueldo = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - ingreso_acumulable = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - ingreso_no_acumulable = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - - -class CfdiNominaPercepciones(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - tipo_percepcion = ForeignKeyField(SATTipoPercepcion) - concepto = TextField(default='') - importe_gravado = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - importe_exento = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - valor_mercado = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - precio_al_ortorgarse = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - - -class CfdiNominaDeducciones(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - tipo_deduccion = ForeignKeyField(SATTipoDeduccion) - concepto = TextField(default='') - importe = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - - -class CfdiNominaOtroPago(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - tipo_otro_pago = ForeignKeyField(SATTipoOtroPago) - concepto = TextField(default='') - importe = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - subsidio_causado = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - saldo_a_favor = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - year = IntegerField(default=0) - remanente_saldo = DecimalField(default=0.0, max_digits=20, decimal_places=6, - auto_round=True) - - -class CfdiNominaIncapacidad(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - dias = IntegerField(default=0) - tipo = ForeignKeyField(SATTipoIncapacidad) - importe = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - - -class CfdiNominaHorasExtra(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - dias = IntegerField(default=0) - tipos_horas = ForeignKeyField(SATTipoHoras) - horas_extra = IntegerField(default=0) - importe_pagado = DecimalField(default=0.0, max_digits=20, - decimal_places=6, auto_round=True) - - -class CfdiNominaSubcontratos(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - rfc = TextField(default='') - porcentaje = DecimalField(default=0.0, max_digits=12, decimal_places=2, - auto_round=True) - - class Meta: - order_by = ('cfdi',) - - -class CfdiNominaOtros(BaseModel): - cfdi = ForeignKeyField(CfdiNomina) - node = TextField(default='') - key = TextField(default='') - value = TextField(default='') - - -class CfdiNominaRelacionados(BaseModel): - cfdi = ForeignKeyField(CfdiNomina, related_name='original') - cfdi_origen = ForeignKeyField(CfdiNomina, related_name='relacion') - - class Meta: - order_by = ('cfdi',) - - -def authenticate(args): - respuesta = {'login': False, 'msg': 'No Autorizado', 'user': ''} - values = util.get_con(args['rfc']) - if not values: - return respuesta, None - - conectar(values) - try: - obj = Usuarios.get(usuario=args['usuario'], es_activo=True) - except Usuarios.DoesNotExist: - return respuesta, None - except ProgrammingError as e: - log.error(e) - return respuesta, None - - if not obj.contraseña.check_password(args['contra']): - return respuesta, None - - obj.ultimo_ingreso = util.now() - obj.save() - respuesta['msg'] = '' - respuesta['login'] = True - respuesta['user'] = str(obj) - respuesta['super'] = obj.es_superusuario - #~ respuesta['admin'] = obj.es_superusuario or obj.es_admin - return respuesta, obj - - -def get_cp(cp): - con = sqlite3.connect(PATH_CP) - cursor = con.cursor() - sql = """ - SELECT colonia, municipio, estado - FROM colonias, municipios, estados - WHERE colonias.id_municipio=municipios.id - AND municipios.id_estado=estados.id - AND cp=? - ORDER BY colonia""" - cursor.execute(sql, (cp,)) - rows = cursor.fetchall() - cursor.close() - con.close() - - data = {} - if rows: - data = { - 'estado': rows[0][2], - 'municipio': rows[0][1], - } - if len(rows) == 1: - data['colonia'] = rows[0][0] - else: - data['colonia'] = [r[0] for r in rows] - return data - - -def get_sat_key(key): - return util.get_sat_key('productos', key) - - -def get_sat_monedas(key): - return util.get_sat_monedas(key) - - -def get_sat_unidades(key): - return util.get_sat_unidades(key) - - -def get_sat_productos(key): - return util.get_sat_productos(key) - - -def get_title_app(by=1): - html = { - 1: '{}', - 2: 'Bienvenido a {}', - 3: '{}', - } - return html[by].format(TITLE_APP) - - -def test_correo(values): - server = { - 'servidor': values['correo_servidor'], - 'puerto': values['correo_puerto'], - 'ssl': util.get_bool(values['correo_ssl']), - 'usuario': values['correo_usuario'], - 'contra': values['correo_contra'], - } - ccp = values.get('correo_copia', '') - options = { - 'para': values['correo_usuario'], - 'copia': ccp, - 'confirmar': util.get_bool(values['correo_confirmacion']), - 'asunto': values['correo_asunto'], - 'mensaje': values['correo_mensaje'].replace('\n', '
'), - 'files': [], - } - data= { - 'server': server, - 'options': options, - } - return util.send_mail(data) - - -def _init_values(rfc): - data = ( - {'clave': 'version', 'valor': VERSION}, - {'clave': 'migracion', 'valor': '0'}, - {'clave': 'rfc_publico', 'valor': 'XAXX010101000'}, - {'clave': 'rfc_extranjero', 'valor': 'XEXX010101000'}, - {'clave': 'decimales', 'valor': '2'}, - {'clave': 'path_key', 'valor': ''}, - {'clave': 'path_cer', 'valor': ''}, - ) - for row in data: - try: - with database_proxy.atomic() as txn: - Configuracion.create(**row) - except IntegrityError: - pass - - if not Certificado.select().count(): - Certificado.create(rfc=rfc) - - log.info('Valores iniciales insertados...') - return - - -def _crear_tablas(rfc): - tablas = [Addendas, Categorias, Certificado, CondicionesPago, Configuracion, - Folios, Registro, CamposPersonalizados, - Emisor, Facturas, FacturasDetalle, FacturasImpuestos, FacturasPagos, - FacturasRelacionadas, FacturasComplementos, FacturasPersonalizados, - SeriesProductos, Almacenes, Productos, RangosPrecios, Sucursales, - PreFacturas, PreFacturasDetalle, PreFacturasImpuestos, - PreFacturasRelacionadas, Tickets, TicketsDetalle, TicketsImpuestos, - SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes, - SATTipoRelacion, SATUnidades, SATUsoCfdi, SATBancos, - SATNivelesEducativos, SATEstados, SATRiesgoPuesto, SATPeriodicidadPago, - SATOrigenRecurso, SATTipoContrato, SATTipoDeduccion, SATTipoHoras, - SATTipoIncapacidad, SATTipoJornada, SATTipoNomina, SATTipoOtroPago, - SATTipoPercepcion, SATTipoRegimen, - Socios, Contactos, ContactoCorreos, ContactoDirecciones, Empleados, - ContactoTelefonos, Departamentos, Puestos, - Tags, Usuarios, CuentasBanco, TipoCambio, MovimientosBanco, - TipoCorreo, TipoDireccion, TipoPariente, TipoResponsable, TipoTelefono, - TipoTitulo, TipoMovimientoAlumno, TipoMovimientoAlmacen, - CfdiPagos, NivelesEducativos, Alumnos, AlumnosParientes, Grupos, - ParienteDirecciones, ParienteTelefonos, ParienteCorreos, - CfdiNomina, CfdiNominaDeducciones, CfdiNominaDetalle, - CfdiNominaHorasExtra, CfdiNominaIncapacidad, CfdiNominaJubilacion, - CfdiNominaOtroPago, CfdiNominaOtros, CfdiNominaPercepciones, - CfdiNominaRelacionados, CfdiNominaSeparacion, CfdiNominaSubcontratos, - CfdiNominaTotales, - Emisor.regimenes.get_through_model(), - Socios.tags.get_through_model(), - Productos.impuestos.get_through_model(), - Productos.tags.get_through_model(), - ] - log.info('Creando tablas...') - database_proxy.create_tables(tablas, True) - log.info('Tablas creadas correctamente...') - - usuarios = ( - {'usuario': 'superadmin', 'contraseña': DEFAULT_PASSWORD, - 'es_superusuario': True}, - {'usuario': 'admin', 'contraseña': DEFAULT_PASSWORD, 'es_admin': True}, - ) - - for usuario in usuarios: - try: - with database_proxy.atomic() as txn: - Usuarios.create(**usuario) - log.info('Usuario creado correctamente...') - except IntegrityError: - log.info('Usuario ya existe...') - - _init_values(rfc) - _importar_valores('', rfc) - - return True - - -def _migrate_tables(): - from playhouse.migrate import PostgresqlMigrator, migrate - - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - - tablas = [Sucursales, SATEstados, SATRiesgoPuesto, SATPeriodicidadPago, - SATOrigenRecurso, SATTipoContrato, SATTipoDeduccion, SATTipoHoras, - SATTipoIncapacidad, SATTipoJornada, SATTipoNomina, SATTipoOtroPago, - SATTipoPercepcion, SATTipoRegimen, Departamentos, Puestos, Empleados, - CfdiNomina, CfdiNominaDeducciones, CfdiNominaDetalle, - CfdiNominaHorasExtra, CfdiNominaIncapacidad, CfdiNominaJubilacion, - CfdiNominaOtroPago, CfdiNominaOtros, CfdiNominaPercepciones, - CfdiNominaRelacionados, CfdiNominaSeparacion, CfdiNominaSubcontratos, - CfdiNominaTotales, - ] - log.info('Creando tablas nuevas...') - database_proxy.create_tables(tablas, True) - log.info('Tablas creadas correctamente...') - - log.info('Iniciando migración de tablas...') - migrations = [] - migrator = PostgresqlMigrator(database_proxy) - - columns = [c.name for c in database_proxy.get_columns('usuarios')] - if not 'sucursal_id' in columns: - sucursal = ForeignKeyField(Sucursales, null=True, to_field=Sucursales.id) - migrations.append( - migrator.add_column('usuarios', 'sucursal_id', sucursal)) - - columns = [c.name for c in database_proxy.get_columns('emisor')] - if not 'registro_patronal' in columns: - registro_patronal = TextField(default='') - migrations.append( - migrator.add_column( - 'emisor', 'registro_patronal', registro_patronal)) - if not 'curp' in columns: - curp = TextField(default='') - migrations.append( - migrator.add_column('emisor', 'curp', curp)) - - if migrations: - with database_proxy.atomic() as txn: - migrate(*migrations) - - log.info('Tablas migradas correctamente...') - return - - -def _agregar_superusuario(): - args = util.get_con() - if not args: - return - - conectar(args) - usuario = input('Introduce el nuevo nombre para el superusuario: ').strip() - if not usuario: - msg = 'El nombre de usuario es requerido' - log.erro(msg) - return - ok, contraseña = util.get_pass() - if not ok: - log.error(contraseña) - return - try: - obj = Usuarios.create( - usuario=usuario, contraseña=contraseña, es_superusuario=True) - except IntegrityError: - msg = 'El usuario ya existe' - log.error(msg) - return - - log.info('SuperUsuario creado correctamente...') - return - - -def _cambiar_contraseña(): - args = util.get_con() - if not args: - return - - conectar(args) - usuario = input('Introduce el nombre de usuario: ').strip() - if not usuario: - msg = 'El nombre de usuario es requerido' - log.error(msg) - return - - try: - obj = Usuarios.get(usuario=usuario) - except Usuarios.DoesNotExist: - msg = 'El usuario no existe' - log.error(msg) - return - - ok, contraseña = util.get_pass() - if not ok: - log.error(contraseña) - return - - obj.contraseña = contraseña - obj.save() - - log.info('Contraseña cambiada correctamente...') - return - - -def _add_emisor(rfc, args): - util._valid_db_companies() - con = sqlite3.connect(COMPANIES) - cursor = con.cursor() - sql = """ - INSERT INTO names - VALUES (?, ?)""" - try: - cursor.execute(sql, (rfc, args)) - except sqlite3.IntegrityError as e: - log.error(e) - return False - - con.commit() - cursor.close() - con.close() - return True - - -def _delete_emisor(rfc): - util._valid_db_companies() - con = sqlite3.connect(COMPANIES) - cursor = con.cursor() - sql = """ - DELETE FROM names - WHERE rfc = ?""" - try: - cursor.execute(sql, (rfc,)) - except Exception as e: - log.error(e) - return False - - con.commit() - cursor.close() - con.close() - return True - - -def _iniciar_bd(): - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - if _crear_tablas(rfc): - return - - log.error('No se pudieron crear las tablas') - return - - -def _agregar_rfc(no_bd): - rfc = input('Introduce el nuevo RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - datos = input('Introduce los datos de conexión: ').strip() - if not datos: - msg = 'Los datos de conexión son requeridos' - log.error(msg) - return - - opt = util.parse_con(datos) - if not opt: - log.error('Datos de conexión incompletos') - return - - args = opt.copy() - if conectar(args): - if _add_emisor(rfc, util.dumps(opt)): - if no_bd: - log.info('RFC agregado correctamente...') - return - _crear_tablas(rfc) - log.info('RFC agregado correctamente...') - return - - log.error('No se pudo agregar el RFC') - return - - -def _borrar_rfc(): - rfc = input('Introduce el RFC a borrar: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - confirm = input('¿Estás seguro de borrar el RFC?') - - if _delete_emisor(rfc): - util.delete_db(rfc.lower()) - log.info('RFC borrado correctamente...') - return - - log.error('No se pudo borrar el RFC') - return - - -def _listar_rfc(detalle): - data = util.get_rfcs() - for row in data: - if detalle: - msg = 'RFC: {}\n\t{}'.format(row[0], row[1]) - else: - msg = 'RFC: {}'.format(row[0]) - log.info(msg) - return - - -def get_empresas(): - data = util.get_rfcs() - rows = [] - for row in data: - rows.append({'delete': '-', 'rfc': row[0]}) - return tuple(rows) - - -def empresa_agregar(rfc, no_bd): - rfc = rfc.upper() - if util.get_con(rfc): - msg = 'El RFC ya esta dado de alta' - return {'ok': False, 'msg': msg} - - user = rfc.replace('&', '').lower() - if not no_bd: - if not util.crear_rol(user): - msg = 'No se pudo crear el usuario, es probable que ya exista' - return {'ok': False, 'msg': msg} - - if not util.crear_db(user): - msg = 'No se pudo crear la base de datos' - return {'ok': False, 'msg': msg} - - args = { - "type": "postgres", - "name": user, - "user": user, - "password": user, - } - if not no_bd: - if not conectar(args.copy()): - msg = 'No se pudo conectar a la base de datos' - return {'ok': False, 'msg': msg} - - if not _add_emisor(rfc, util.dumps(args)): - msg = 'No se pudo guardar el nuevo emisor' - return {'ok': False, 'msg': msg} - - if not no_bd: - if not _crear_tablas(rfc): - msg = 'No se pudo crear las tablas' - return {'ok': False, 'msg': msg} - - msg = 'Emisor dado de alta correctamente' - row = {'delete': '-', 'rfc': rfc} - return {'ok': True, 'msg': msg, 'row': row} - - -def empresa_borrar(rfc): - if _delete_emisor(rfc): - util.delete_db(rfc.lower()) - return True - - -def _importar_valores(archivo='', rfc=''): - if not rfc: - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - - if not archivo: - archivo = INIT_VALUES - - log.info('Importando datos...') - regimen = '' - rows = util.loads(open(archivo, 'r').read()) - for row in rows: - log.info('\tImportando tabla: {}'.format(row['tabla'])) - table = globals()[row['tabla']] - for r in row['datos']: - try: - with database_proxy.atomic() as txn: - table.create(**r) - except IntegrityError: - pass - - log.info('Importación terminada...') - return - - -def _importar_socios(rows): - log.info('\tImportando Clientes...') - totals = len(rows) - for i, row in enumerate(rows): - msg = '\tGuardando cliente {} de {}'.format(i+1, totals) - log.info(msg) - try: - with database_proxy.atomic() as txn: - Socios.create(**row) - except IntegrityError: - msg = '\tSocio existente: {}'.format(row['nombre']) - log.info(msg) - log.info('\tClientes importados...') - return - - -def _existe_factura(row): - filtro = (Facturas.uuid==row['uuid']) - if row['uuid'] is None: - filtro = ( - (Facturas.serie==row['serie']) & - (Facturas.folio==row['folio']) - ) - return Facturas.select().where(filtro).exists() - - -def _importar_facturas(rows): - log.info('\tImportando Facturas...') - totals = len(rows) - for i, row in enumerate(rows): - msg = '\tGuardando factura {} de {}'.format(i+1, totals) - log.info(msg) - - try: - detalles = row.pop('detalles') - impuestos = row.pop('impuestos') - cliente = row.pop('cliente') - row['cliente'] = Socios.get(**cliente) - with database_proxy.atomic() as txn: - if _existe_factura(row): - msg = '\tFactura existente: {}{}'.format( - row['serie'], row['folio']) - log.info(msg) - continue - obj = Facturas.create(**row) - for detalle in detalles: - detalle['factura'] = obj - FacturasDetalle.create(**detalle) - for impuesto in impuestos: - imp = SATImpuestos.get(**impuesto['filtro']) - new = { - 'factura': obj, - 'impuesto': imp, - 'importe': impuesto['importe'], - } - try: - with database_proxy.atomic() as txn: - FacturasImpuestos.create(**new) - except IntegrityError as e: - pass - - except IntegrityError as e: - print (e) - msg = '\tFactura: id: {}'.format(row['serie'] + str(row['folio'])) - log.error(msg) - break - - log.info('\tFacturas importadas...') - return - - -def _importar_categorias(rows): - log.info('\tImportando Categorías...') - for row in rows: - with database_proxy.atomic() as txn: - try: - Categorias.create(**row) - except IntegrityError: - msg = '\tCategoria: ({}) {}'.format(row['padre'], row['categoria']) - log.error(msg) - - log.info('\tCategorías importadas...') - return - - -def _get_id_unidad(unidad): - try: - if 'pieza' in unidad.lower(): - unidad = 'pieza' - if 'metros' in unidad.lower(): - unidad = 'metro' - if 'tramo' in unidad.lower(): - unidad = 'paquete' - if 'juego' in unidad.lower(): - unidad = 'par' - if 'bolsa' in unidad.lower(): - unidad = 'globo' - if unidad.lower() == 'no aplica': - unidad = 'servicio' - - obj = SATUnidades.get(SATUnidades.name.contains(unidad)) - except SATUnidades.DoesNotExist: - msg = '\tNo se encontró la unidad: {}'.format(unidad) - # ~ log.error(msg) - return unidad - - return str(obj.id) - - -def _get_impuestos(impuestos): - lines = '|' - for impuesto in impuestos: - if impuesto['tasa'] == '-2/3': - tasa = str(round(2/3, 6)) - else: - if impuesto['tasa'] == 'EXENTO': - tasa = '0.00' - else: - tasa = str(round(float(impuesto['tasa']) / 100.0, 6)) - - info = ( - IMPUESTOS.get(impuesto['nombre']), - impuesto['nombre'], - impuesto['tipo'][0], - tasa, - ) - lines += '|'.join(info) + '|' - return lines - - -def _generar_archivo_productos(archivo): - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - - log.info('Importando datos...') - app = util.ImportFacturaLibre(archivo, rfc) - if not app.is_connect: - log.error('\t{}'.format(app._error)) - return - - rows = app.import_productos() - - p, _, _, _ = util.get_path_info(archivo) - path_txt = util._join(p, 'productos_{}.txt'.format(rfc)) - log.info('\tGenerando archivo: {}'.format(path_txt)) - - fields = ( - 'clave', - 'clave_sat', - 'unidad', - 'categoria', - 'descripcion', - 'valor_unitario', - 'existencia', - 'inventario', - 'codigo_barras', - 'cuenta_predial', - 'ultimo_precio', - 'minimo', - ) - - data = ['|'.join(fields)] - not_units = [] - for row in rows: - impuestos = row.pop('impuestos', ()) - line = [str(row[r]) for r in fields] - if line[10] == 'None': - line[10] = '0.0' - line[2] = _get_id_unidad(line[2]) - try: - int(line[2]) - except ValueError: - if not line[2] in not_units: - not_units.append(line[2]) - msg = 'No se encontró la unidad: {}'.format(line[2]) - log.error(msg) - continue - line = '|'.join(line) + _get_impuestos(impuestos) - data.append(line) - - with open(path_txt, 'w') as fh: - fh.write('\n'.join(data)) - - log.info('\tArchivo generado: {}'.format(path_txt)) - return - - -def importar_bdfl(): - try: - emisor = Emisor.select()[0] - except IndexError: - msg = 'Configura primero al emisor' - return {'ok': False, 'msg': msg} - - name = '{}.sqlite'.format(emisor.rfc.lower()) - path = util._join('/tmp', name) - - log.info('Importando datos...') - app = util.ImportFacturaLibre(path, emisor.rfc) - if not app.is_connect: - msg = app._error - log.error('\t{}'.format(msg)) - return {'ok': False, 'msg': msg} - - data = app.import_data() - - _importar_socios(data['Socios']) - _importar_facturas(data['Facturas']) - _importar_categorias(data['Categorias']) - - msg = 'Importación terminada...' - log.info(msg) - - return {'ok': True, 'msg': msg} - - -def _importar_factura_libre(archivo): - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - - log.info('Importando datos...') - app = util.ImportFacturaLibre(archivo, rfc) - if not app.is_connect: - log.error('\t{}'.format(app._error)) - return - - data = app.import_data() - - _importar_socios(data['Socios']) - _importar_facturas(data['Facturas']) - _importar_categorias(data['Categorias']) - - log.info('Importación terminada...') - return - - -def _importar_factura_libre_gambas(conexion): - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - - log.info('Importando datos...') - app = util.ImportFacturaLibreGambas(conexion, rfc) - if not app.is_connect: - log.error('\t{}'.format(app._error)) - return - - data = app.import_data() - - _importar_socios(data['Socios']) - _importar_facturas(data['Facturas']) - _importar_categorias(data['Categorias']) - _importar_productos_gambas(data['Productos']) - _import_tickets(data['Tickets']) - - log.info('Importación terminada...') - return - - -def _exist_ticket(row): - filters = ( - (Tickets.serie==row['serie']) & - (Tickets.folio==row['folio']) - ) - return Tickets.select().where(filters).exists() - - -def _import_tickets(rows): - log.info('\tImportando Tickets...') - for row in rows: - try: - details = row.pop('details') - taxes = row.pop('taxes') - with database_proxy.atomic() as txn: - if _exist_ticket(row): - msg = '\tTicket existente: {}{}'.format( - row['serie'], row['folio']) - log.info(msg) - continue - - if not row['factura'] is None and row['factura']: - row['factura'] = Facturas.get( - Facturas.serie==row['factura']['serie'], - Facturas.folio==row['factura']['folio']) - else: - row['factura'] = None - - obj = Tickets.create(**row) - for detail in details: - detail['ticket'] = obj - TicketsDetalle.create(**detail) - for tax in taxes: - imp = SATImpuestos.get(**tax['filter']) - new = { - 'ticket': obj, - 'impuesto': imp, - 'importe': tax['import'], - } - TicketsImpuestos.create(**new) - except IntegrityError as e: - # ~ print (e) - msg = '\tTicket: id: {}'.format(row['serie'] + str(row['folio'])) - log.error(msg) - - log.info('\tTickets importadas...') - return - - -def _importar_productos_gambas(rows): - log.info('Importando productos...') - - KEYS = { - 'Exento': '000', - 'ISR': '001', - 'IVA': '002', - } - - totals = len(rows) - for i, row in enumerate(rows): - msg = '\tGuardando producto {} de {}'.format(i+1, totals) - log.info(msg) - - source_taxes = row.pop('impuestos') - row['unidad'] = SATUnidades.get(SATUnidades.key==row['unidad']) - taxes = [] - for tax in source_taxes: - w = { - 'key': KEYS[tax[0]], - 'name': tax[0], - 'tasa': float(tax[1]), - 'tipo': tax[2][0], - } - taxes.append(SATImpuestos.get_o_crea(w)) - - with database_proxy.transaction(): - try: - obj = Productos.create(**row) - obj.impuestos = taxes - except IntegrityError as e: - msg = '\tProducto ya existe' - log.info(msg) - - log.info('Importación terminada...') - return - - -def _importar_productos(archivo): - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - log.info('Importando productos...') - - fields = ( - 'clave', - 'clave_sat', - 'unidad', - 'categoria', - 'descripcion', - 'valor_unitario', - 'existencia', - 'inventario', - 'codigo_barras', - 'cuenta_predial', - 'ultimo_precio', - 'minimo', - ) - - rows = util.read_file(archivo, 'r').split('\n') - for i, row in enumerate(rows): - if i == 0: - continue - data = row.split('|') - # ~ print (data) - new = {} - for i, f in enumerate(fields): - if not len(data[0]): - continue - - if i in (2, 3): - try: - new[f] = int(data[i]) - except ValueError: - continue - elif i in (5, 6, 10, 11): - new[f] = float(data[i]) - elif i == 7: - new[f] = bool(data[i]) - else: - new[f] = data[i] - - impuestos = data[i + 1:-1] - if not impuestos: - taxes = [SATImpuestos.select().where(SATImpuestos.id==6)] - else: - taxes = [] - try: - for i in range(0, len(impuestos), 4): - w = { - 'key': impuestos[i], - 'name': impuestos[i+1], - 'tipo': impuestos[i+2], - 'tasa': float(impuestos[i+3]), - } - taxes.append(SATImpuestos.get_o_crea(w)) - except IndexError: - print ('IE', data) - continue - - with database_proxy.transaction(): - try: - obj = Productos.create(**new) - obj.impuestos = taxes - except IntegrityError as e: - pass - - log.info('Importación terminada...') - return - - -def _import_from_folder(path): - files = util.get_files(path, 'json') - if not files: - msg = 'No se encontraron archivos para importar' - log.error(msg) - return - - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - log.info('Importando valores...') - for p in files: - msg = '\tImportando tabla: {}' - data = util.import_json(p) - log.info(msg.format(data['tabla'])) - table = globals()[data['tabla']] - for r in data['datos']: - try: - with database_proxy.atomic() as txn: - table.create(**r) - except IntegrityError: - pass - - log.info('Valores importados...') - return - - -def _test(): - rfc = input('Introduce el RFC: ').strip().upper() - if not rfc: - msg = 'El RFC es requerido' - log.error(msg) - return - - args = util.get_con(rfc) - if not args: - return - - conectar(args) - - return - - -CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) -help_create_tables = 'Crea las tablas en la base de datos' -help_migrate_db = 'Migra las tablas en la base de datos' -help_superuser = 'Crea un nuevo super usuario' -help_change_pass = 'Cambia la contraseña a un usuario' -help_rfc = 'Agrega un nuevo RFC' -help_br = 'Elimina un RFC' -help_lr = 'Listar RFCs' - -@click.command(context_settings=CONTEXT_SETTINGS) -@click.option('-bd', '--iniciar-bd',help=help_create_tables, - is_flag=True, default=False) -@click.option('-m', '--migrar-bd', help=help_migrate_db, - is_flag=True, default=False) -@click.option('-ns', '--nuevo-superusuario', help=help_superuser, - is_flag=True, default=False) -@click.option('-cc', '--cambiar-contraseña', help=help_change_pass, - is_flag=True, default=False) -@click.option('-ar', '--agregar-rfc', help=help_rfc, is_flag=True, default=False) -@click.option('-br', '--borrar-rfc', help=help_br, is_flag=True, default=False) -@click.option('-lr', '--listar-rfc', help=help_lr, is_flag=True, default=False) -@click.option('-i', '--importar-valores', is_flag=True, default=False) -@click.option('-f', '--archivo') -@click.option('-c', '--conexion') -@click.option('-fl', '--factura-libre', is_flag=True, default=False) -@click.option('-flg', '--factura-libre-gambas', is_flag=True, default=False) -@click.option('-t', '--test', is_flag=True, default=False) -@click.option('-gap', '--generar-archivo-productos', is_flag=True, default=False) -@click.option('-ip', '--importar-productos', is_flag=True, default=False) -@click.option('-bk', '--backup-dbs', is_flag=True, default=False) -@click.option('-n', '--no-bd', is_flag=True, default=False) -@click.option('-a', '--alta', is_flag=True, default=False) -@click.option('-r', '--rfc') -@click.option('-d', '--detalle', is_flag=True, default=False) -@click.option('-id', '--importar-directorio') -def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, - agregar_rfc, borrar_rfc, listar_rfc, importar_valores, archivo, conexion, - factura_libre, factura_libre_gambas, test, generar_archivo_productos, - importar_productos, backup_dbs, no_bd, alta, rfc, detalle, - importar_directorio): - - opt = locals() - - if opt['test']: - _test() - sys.exit(0) - - if opt['alta']: - if not opt['rfc']: - msg = 'Falta el RFC' - raise click.ClickException(msg) - empresa_agregar(opt['rfc'], no_bd) - sys.exit(0) - - if opt['iniciar_bd']: - _iniciar_bd() - sys.exit(0) - - if opt['migrar_bd']: - _migrate_tables() - sys.exit(0) - - if opt['nuevo_superusuario']: - _agregar_superusuario() - sys.exit(0) - - if opt['cambiar_contraseña']: - _cambiar_contraseña() - sys.exit(0) - - if opt['agregar_rfc']: - _agregar_rfc(no_bd) - sys.exit(0) - - if opt['borrar_rfc']: - _borrar_rfc() - sys.exit(0) - - if opt['listar_rfc']: - _listar_rfc(opt['detalle']) - sys.exit(0) - - if opt['importar_valores']: - if not opt['archivo']: - msg = 'Falta la ruta del archivo importar' - raise click.ClickException(msg) - if not util.is_file(opt['archivo']): - msg = 'No es un archivo' - raise click.ClickException(msg) - - _importar_valores(opt['archivo']) - sys.exit(0) - - if opt['factura_libre']: - if not opt['archivo']: - msg = 'Falta la ruta de la base de datos' - raise click.ClickException(msg) - if not util.is_file(opt['archivo']): - msg = 'No es un archivo' - raise click.ClickException(msg) - _, _, _, ext = util.get_path_info(opt['archivo']) - if ext != '.sqlite': - msg = 'No es una base de datos' - raise click.ClickException(msg) - - _importar_factura_libre(opt['archivo']) - sys.exit(0) - - if opt['factura_libre_gambas']: - if not opt['conexion']: - msg = 'Falta los datos de conexión' - raise click.ClickException(msg) - _importar_factura_libre_gambas(opt['conexion']) - sys.exit(0) - - if opt['generar_archivo_productos']: - if not opt['archivo']: - msg = 'Falta la ruta de la base de datos' - raise click.ClickException(msg) - if not util.is_file(opt['archivo']): - msg = 'No es un archivo' - raise click.ClickException(msg) - _, _, _, ext = util.get_path_info(opt['archivo']) - if ext != '.sqlite': - msg = 'No es una base de datos' - raise click.ClickException(msg) - - _generar_archivo_productos(opt['archivo']) - sys.exit(0) - - if opt['importar_productos']: - if not opt['archivo']: - msg = 'Falta la ruta del archivo' - raise click.ClickException(msg) - if not util.is_file(opt['archivo']): - msg = 'No es un archivo' - raise click.ClickException(msg) - _, _, _, ext = util.get_path_info(opt['archivo']) - if ext != '.txt': - msg = 'No es un archivo de texto' - raise click.ClickException(msg) - - _importar_productos(opt['archivo']) - sys.exit(0) - - if opt['importar_directorio']: - _import_from_folder(opt['importar_directorio']) - - if opt['backup_dbs']: - util.backup_dbs() - - return - - -if __name__ == '__main__': - main() -