Programacion Interactiva

Descubre una Nueva y Poderosa Herramienta.

FrameWork de Nueva Generacion

Acceso a tu Informacion desde cualquier Dispositivo con Navegador.

Enfoque en el Backend

Frontend de forma facil con Odoo y XML.

Creacion de Verticales

Creacion de nuevas Verticales Conquistando nuevos Mercados.

Tu marca aqui mismo

Llega a miles de personas.

Publicidad

viernes, 5 de junio de 2020

Separar parte numƩrica de una secuencia establecida

Separar la parte numƩrica y cadena en un valor de secuencia



     El titulo suena un poco extraƱo lo se, pero la idea principal es tratar de separar la parte numerica dentro de una secuencia o una cadena de caracteres, esto lo tuve que realizar para una modificación dentro de Odoo para obtener el folio y la secuencia, para lograrlo solo utilize una expresión regular que espero les pueda servir:




            #### Division Caracteres y Enteros ####

            import re

            folio_invoice = 'FAC04445'
            expresion_re = re.compile("([a-zA-Z]+)([0-9]+)")
            m_re = expresion_re.match(folio_invoice)
            group_1 = m_re.group(1)
            group_2 = m_re.group(2)



Alternativa:


temp_string = "Hi my age is 32 years and 250 days12"
print(temp_string)

numbers = [int(temp)for temp in temp_string.split() if temp.isdigit()]

print(numbers)

Resultado:

Hi my age is 32 years and 250 days12
[32, 250]

Mi función Final:

 
def get_split_integer_part( cadena):
    cadena = cadena.replace('-',' ').replace('/',' ').replace('_',' ').replace('*',' ').replace('+',' ')
    numbers = [int(temp)for temp in cadena.split() if temp.isdigit()]
    if not numbers:
        expresion_re = re.compile("([a-zA-Z]+)([0-9]+)")
        m_re = expresion_re.match(cadena)
        try:
            group_1 = m_re.group(1)
            group_2 = m_re.group(2)
            numbers = [group_2]
        except:
            expresion_re = re.compile("([0-9]+)([a-zA-Z]+)")
            m_re = expresion_re.match(cadena)
            try:
                group_1 = m_re.group(1)
                group_2 = m_re.group(2)
                numbers = [group_1]
            except:
                numbers = []
    if not numbers:
        return [0]
    return numbers

jueves, 4 de junio de 2020

Validar un rango de fechas en Odoo por medio de atributos en la vistas

Restricción en periodo de fechas




     Dentro de Odoo muchas veces requerimos tener un asistente que nos obtenga información dentro de un periodo establecido por el usuario o para determinar la validez de un documento dentro del mismo periodo. Al realizar este tipo de modificaciones o asistentes muchas veces descartamos o llegamos a ignorar la validación del periodo que el usuario introduce que en ocasiones puede ser un Bug que no permita la funcionalidad correcta al buscar información, una solución simple seria aƱadir un constraint para que por medio de python validarĆ­amos la información pero dentro de las mismas vistas podemos determinar esta validación sin programar nada adicional.

La sintaxis es la siguiente:

<field name="start_date" widget="daterange" options='{"related_end_date": "end_date"}'/>


El parĆ”metro o campo start_date hace referencia a nuestra fecha inicial y para la fecha final tenemos end_date


Para realizar la validacion basta con aƱadir las opciones que podemos visualizar en la sintaxis:

options='{"related_end_date": "end_date"}'

Recordemos que start_date y end_date son los nombres con los cuales generamos los campos en Odoo, pero no es obligatorio que se llamen exactamente como en el ejemplo.

Desabilitar Fines de Semana en campos Fecha Odoo

No permitir la selección de fines de semana en campos de tipo fecha



Existe un atributo dentro de Odoo que nos permite restringir la selección de fines de semana en campos de tipo fecha, lo cual nos deja lo que conocemos como días hÔbiles L-V pero que puede ser configurado de acuerdo a lo requerido.

El ejemplo seria:

options='{"datepicker": {"daysOfWeekDisabled": [0, 6]}}'

Dentro del campo de fecha de expiación en las cotizaciones, al seleccionar un día L - V no tendríamos problemas:


Pero al seleccionar por ejemplo el dĆ­a sabado:

Como vemos el icono de bloqueo no dejarĆ­a seleccionar el dĆ­a que no esta permitido.


Como observamos en la imagen el parametro tiene por nombre daysOfWeekDisabled


miƩrcoles, 3 de junio de 2020

Consulta SQL en Odoo implementando nuestra Zona horaria

Consulta SQL en Odoo corrigiendo el error de la zona horaria en el resultado de la consulta de un periodo



     La siguiente Consulta SQL resuelve el error de la Zona Horario Odoo, como sabran la base de datos se encuentra en UTC o zona horaria central por lo cual visualmente dentro del sistema se realiza la conversión a la zona horaria del usuario y se muestra correctamente, pero en base de datos podemos tener una diferencia en campos fecha/hora, por lo cual al realizar una consulta esta puede abarcar un periodo que no es correcto, para ello lo unico que requerimos es dentro de la conulta convertir la columna de fecha/hora a nuestra zona horaria, para este ejemplo utilizare la zona horaria de Mexico UTC-6.

Las librerias que utilizaremos son:


from datetime import timedelta, datetime



La clave esta en convertir la columna fecha/hora a UTC-6

   

        start_period = self.date_start + " 00:00:01"
        start_period_strp = datetime.strptime(start_period, '%Y-%m-%d %H:%M:%S')
        start_period_tz = fields.Datetime.context_timestamp(self, 
        timestamp=start_period_strp)
        end_period = self.date_stop + " 23:59:59"
        end_period_strp = datetime.strptime(end_period, '%Y-%m-%d %H:%M:%S')
        end_period_tz = fields.Datetime.context_timestamp(self, 
        timestamp=end_period_strp)
        get_list_contacts_partner = self.get_parners_list(self.supplier_id)

        product_list = []


        cr.execute("""

            insert into supplier_puchase_detail_record(  supplier_id,

                                                        product_id,

                                                        uom_id,

                                                        qty_total,

                                                        qty_available

                                                        )

            select  %s,

                    stock_move.product_id,

                    stock_move.product_uom,

                    sum(stock_move.product_uom_qty),

                    0.0

            from stock_move

                 join stock_picking

                   on stock_move.picking_id = stock_picking.id

                 join purchase_order_line

                   on purchase_order_line.id = stock_move.purchase_line_id

            where stock_picking.partner_id in %s

            and (stock_picking.date_done at time zone 'UTC-6') between %s and %s

            group by stock_move.product_id, stock_move.product_uom

           

            """, (self.supplier_id.id,
                  tuple(get_list_contacts_partner), 
                  start_period_tz, end_period_tz))

Nginx SSL - ERR_SSL_PROTOCOL_ERROR - No se puede conectar por SSL (Solución)

Solución al error con Nginx ERR_SSL_PROTOCOL_ERROR




139857146823744:error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../ssl/record/rec_layer_s3.c:1528:SSL alert number 80


El error esta en la configuración de nuestro sitio nginx, para ser exactos en el apartado de configuración ssl debemos modificar y de ser necesario cambiar o reemplazar los sig. parÔmetros:


    ssl_prefer_server_ciphers on;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;


TEST de CONEXION SSL



openssl s_client -connect jlbelleza.es:443 -servername midominio.mx


El parƔmetro midominio.mx es el sitio al cual se estƔn conectando e implementando el certificado SSL.

Implementando STP con Python

Como utilizar SFTP desde Python


El fragmento para poder copiar un archivo a un servidor sftp es el siguiente:
import paramiko
import os

paramiko.util.log_to_file('logfile.log')

host = "101.102.103.104"
port = 22
transport = paramiko.Transport((host, port))
password = "pass"
username = "user"
transport.connect(username = username, password = password)


sftp = paramiko.SFTPClient.from_transport(transport)

filepath = '~/remote/file'
localpath = '~/local/file'
sftp.get(filepath, localpath)

sftp.close()
transport.close()

Espero que les sea de utilizad.

Solucion al error con la libreria sassc: error: no such option: --stdin ()

Solución al error con la libreria sassc
error: no such option: --stdin




Solución al Error:
sassc: error: no such option: --stdin

Line 13 in 3c11cf9
libsass==0.12.3

Como verÔn la solución es la versión de la librería.

jueves, 21 de mayo de 2020

Solución al error con Nginx ssl_error_bad_cert_domain

Solución al Error ssl_error_bad_cert_domain


Si tenemos un error al configurar nuestro dominio nginx con letsencrypt se debe a que el certificado ssl requiere expandir a ambas url, para ello ejecutamos el comando:

 letsencrypt certonly --standalone -d midominio.mx -d www.midominio.mx


La configuración de nginx quedaria (midominio.mx) --> /etc/nginx/sites-enabled:

upstream oddo {
    server 127.0.0.1:8069;
}

server {
    listen      443 default;
    server_name midominio.mx www.midominio.mx;

    access_log  /var/log/nginx/oddo.access.log;
    error_log   /var/log/nginx/oddo.error.log;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/midominio.mx/cert.pem;
    ssl_certificate_key /etc/letsencrypt/live/midominio.mx/privkey.pem;
    keepalive_timeout   60;

    #ssl_ciphers             HIGH:!ADH:!MD5;
    #ssl_protocols           SSLv3 TLSv1;
    ssl_prefer_server_ciphers on;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    proxy_buffers 16 64k;
    proxy_buffer_size 128k;

    location / {
        proxy_pass  http://oddo;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;

        proxy_set_header    Host            $host;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto https;
        proxy_read_timeout 300000;

    }

    location ~* /web/static/ {
        proxy_cache_valid 200 60m;
        proxy_buffering on;
        expires 864000;
        proxy_pass http://oddo;
    }
}

server {
    listen      80;
    server_name midominio.mx www.midominio.mx;

    add_header Strict-Transport-Security max-age=2592000;
    rewrite ^/.*$ https://$host$request_uri? permanent;
}

domingo, 29 de marzo de 2020

Presicion Decimal en Odoo 13

Creación de Campos con Decimales Dinamicos




Como habíamos mencionado en post anteriores podemos definir una presicion dinÔmica que nos permita gestionar de forma modificable el no. de decimales para la definición de los campos en una clase.

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
   <data>
  <!--  Mi presicion decimal-->

  <record model="decimal.precision" id="precision_discounts">
    <field name="name">Discount</field>
    <field name="digits">6</field>
  </record>  

   </data>
</odoo>

Al instalar nuestro modulo o actualizarlo, nos creara el registro correspondiente al XML:


El valor digits es el numero de decimales que tendra el campo que lo utilice como su presición decimal.

El paso final es asignar esta presición en la creación del campo en una clase Odoo.

# -*- coding: utf-8 -*-
from openerp.exceptions import UserError
from openerp import api, fields, models, _, tools

class Accountinvoice(models.Model):
    _inherit = 'account.invoice'
    _name = 'account.invoice'
    
    invoice_discount_amount = fields.Float(digits='Discount', 
                                string='Descuento Total en Monto')


En el ejemplo anterior herede facturas y cree mi campo llamado Descuento Total con 6 Decimales en su parte flotante, el campo se visualiza de la siguiente manera en el Sistema:

Si modificamos la presición decimal a 4.


Veriamos el campo actualizado y los registros anteriores redondeados al nuevo numero de decimales.

Una presición puede ser reutilzada muchas veces no es necesario crear una por cada campo a menos que el proceso lo requiera.

Espero que les ayude esta información y no se olviden dejar sus comentarios.




martes, 25 de febrero de 2020

Campo Binario B64 a Archivo Temporal Python

Binario B64 a Archivo Fisico Temporal


Ultimamente me han pedido desarrollar reportes en Excel, para esto me apoyo mucho de la libreria xlsxwriter o del modulo report_xlsx, pero muchas veces es necesario utilizar el logo dentro del reporte, en Qweb seria muy sencillo ya que la utilidad hace la conversión del binario, pero en mi caso no fue asi, existen 2 maneras de hacerlo metiendo la ruta fisica del archivo dentro de nuestro moulo o la segunda la cual vengo a enseñarles, guardar el campo a un archivo temporal, en el siguiente ejemplo cree 2 funciones para guardar el logo dato que requiero para dar formato a mi excel.


import tempfile
import base64
import binascii
import os

class ClaseOdoo(models.Model):
    _name = 'mi.clase'
    _inherit ='mi.clase'

    def b64str_to_tempfile(self, b64_str=None, file_suffix=None, file_prefix=None):
        """
        @param b64_str : Text in Base_64 format for add in the file
        @param file_suffix : Sufix of the file
        @param file_prefix : Name of file in TempFile
        """

        (fileno, fname) = tempfile.mkstemp(file_suffix, file_prefix)
        f = open(fname, 'wb')
        f.write(base64.decodestring(b64_str or ''))
        f.close()
        os.close(fileno)
        return fname



    def logo_b64_str_to_physical_file(self, b64_str, file_extension='png', prefix='company_logo'):

        certificate_lib = self.env['facturae.certificate.library']
        b64_temporal_route = self.b64str_to_tempfile(base64.encodestring(''), 
                                                          file_suffix='.%s' % file_extension, 
                                                          file_prefix='odoo__%s__' % prefix)
        _logger.info("\n### b64_temporal_route %s " % b64_temporal_route)
        ### Guardando el Logo  ###
        f = open(b64_temporal_route, 'wb')
        f.write(base64.b64decode(b64_str))
        f.close()
        file_result = open(b64_temporal_route, 'rb').read()
        return file_result, b64_temporal_route




Como paso final solo necesitamos el fragmento de codigo que llame a estas funciones:


        company_logo = self.env.user.company_id.logo
        print ("###### company_logo >>>>>>> ", company_logo)
        if company_logo:
            file_result_b64, logo_path_b64 = self.logo_b64_str_to_physical_file(company_logo, 'png', 'company_logo')
            print ("#### logo_path_b64 >>>>>>>> ",logo_path_b64)     
            image_module_path = logo_path_b64
        else:
            module_path = module.get_module_path('mi_modulo')
            #print "########## MODULE PATH >>> ",module_path
            image_module_path = module_path+'/static/img/logo.jpg'