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

Mostrando las entradas con la etiqueta python odoo. Mostrar todas las entradas
Mostrando las entradas con la etiqueta python odoo. Mostrar todas las entradas

viernes, 11 de diciembre de 2020

Dominios DinƔmicos en Campos One2Many (Odoo 9, 10, 11, 12, 13, 14)

 Creación de un dominio dinĆ”mico para Odoo

    Una pregunta y necesidad que tuve hace poco tiempo fue agregar un dominio en una vista de Ć”rbol generada por un campo One2many, buscando información y ejemplos finalmente no encontrĆ© y lo trate de realizar por medio de JS, un total fracaso, el tiempo que tenia no ayudaba mucho y fue entonces que intente realizarlo por medio de un dominio dinĆ”mico y una emulación de botón que retornara mi objeto con el filtro dinĆ”mico.

Primero voy a ejemplificar mis 2 clases, realmente no eran como el siguiente ejemplo pero trato de que sea mas digerible para todos nosotros.


Mi Clase Linea:

class DescargaAlmacenProducto(models.Model):
    _name = 'descarga.almacen.producto'
    _description = 'Linea Descarga de Productos'
    _rec_name = 'product_id' 
    _order = 'id desc' 

    download_id = fields.Many2one('descarga.almacen', 'ID Ref')
	
	product_id = fields.Many2one('product.product', 'Nombre', required=True)

    familia_producto = fields.Char('Familia', size=128, related="product_id.familia_producto")

    product_qty = fields.Float('Cantidad', digits=(14,4))

    uom_id = fields.Many2one('product.uom', 'UdM')


Mi Clase Principal:

class DescargaAlmacen(models.Model):
    _name = 'descarga.almacen'
    _description = 'Asistente Descarga de Productos'
    _rec_name = 'sequence_name' 
    _order = 'id desc' 


    def _compute_selection_family(self):
        selection_options = [('no_one','Sin Filtro')]
        cr = self.env.cr
        context = self._context
        if 'family_complete_list_ctx' in context and context['family_complete_list_ctx']:
            family_complete_list_ctx = context['family_complete_list_ctx']
            if family_complete_list_ctx:
                for f2 in family_complete_list_ctx:
                    selection_options.append((f2,f2))

        return selection_options

    def _get_domain_func(self, ):
        context = self._context
        if 'dynamic_domain' in context and context['dynamic_domain']:
            if 'record_ids' in context and context['record_ids']:
                self_br = self.browse(context['record_ids'])[0]
                filter_familia_producto = self_br.filter_familia_producto
                if not filter_familia_producto or filter_familia_producto == 'no_one':
                    return []
                domain = [('familia_producto', '=', filter_familia_producto)]
                return domain
        return []

    filter_familia_producto = fields.Selection(selection=lambda self: self._compute_selection_family(), string="Familia", default="no_one")

    space_download_line_ids = fields.One2many('descarga.almacen.producto', 'download_id', 'Lineas de Descarga', ondelete="cascade", domain=_get_domain_func)

    sequence_name = fields.Char('Secuencia', size=128)

    #### Filtro Dinamico ######
    def refresh_filter(self):
        family_complete_list_ctx = []
        for rec in self:
            print ("## rec.space_download_line_id >>>>> ",rec.space_download_line_ids)              
            for line in rec.space_download_line_ids:
                line_familia = line.familia_producto
                if line_familia and line_familia not in family_complete_list_ctx:
                    family_complete_list_ctx.append(line_familia)
        return {
                'name': _('Descarga - Familia' % self.filter_familia_producto),
                'view_mode': 'form',
                'view_id': self.env.ref('mi_modulo.mi_vista').id,
                'res_model': 'descarga.almacen',
                'context': "{'readonly_by_pass': True, 'record_ids': %s, 'dynamic_domain': True, 'family_complete_list_ctx': %s}" % (self.ids, family_complete_list_ctx), # self.env.context
                'type': 'ir.actions.act_window',
                'res_id': self.id,
                'flags': {'initial_mode': 'edit'}
            }


El campo que utilizare para filtrar los datos es un tipo Selection:

  •  filter_familia_producto

Un dato importante es que este campo de tipo "seleccion" es calculado para solo tener los filtros de mis registros, no quisiera mostrar todos por que no traeria información.

Mi campo One2many que contiene las lineas principales de mi registro tiene por nombre:

  •  space_download-line_ids

Podemos observar que dentro de estas lineas se encuentra mi dominio dinƔmico:

domain=_get_domain_func

Estos datos los estoy retornando por medio del contexto en mi función refresh_filter

'context': "{'readonly_by_pass': True, 'record_ids': %s, 'dynamic_domain': True, 'family_complete_list_ctx': %s}" % (self.ids, family_complete_list_ctx), # self.env.context

Para no tener errores con mi funcion domain, dentro del contexto vuelvo a retornar las opciones calculadas en todos mis registros y los ids de estos.

Para finalizar, agregue en mi vista el campo y el boton que retornara mi objeto con los nuevos filtros:

<group >
    <group>
        <field name="filter_familia_producto" string="Filtro Familia"  />
    </group>
    <group class="oe_subtotal_footer oe_right" >
        <button string="Filtrar" icon="fa-search" name="refresh_filter" style="display: inline-block;
padding: 5px 15px;
font-size: 12px;
cursor: pointer;
text-align: center;
text-decoration: none;
outline: none;
color: #fff;
background-color: #b787aa;
border: none;
border-radius: 5px;
box-shadow: 0 5px #999;"/>
    </group>
</group>


Agregue unos estilos para dar mejor presentación y el resultado se muestra asi:



Espero sea de su ayuda.


jueves, 15 de octubre de 2020

Trucos Odoo 14

 Trucos Odoo 14



1.1 ¿QuĆ© es Odoo?

Odoo ERP es un software libre que puede ser instalado en la nube y se puede unir con una app para que desde tu celular puedas interactuar con el, tiene varios módulos muy interesantes para la administración de las empresas. Se conocĆ­a anteriormente como OpenERP, que es un sistema de ERP integrado de código abierto actualmente producido por la empresa belga Odoo S.A. El fabricante declara su producto como una alternativa de código abierto a SAP ERP y Microsoft Dynamics.

Odoo usa una arquitectura cliente servidor donde los clientes se conectan usando un navegador via RPC. Generalmente, la lógica y la extensión de negocio se realizan en el lado del servidor.

Entre sus principales módulos podemos señalar:

  • CRM Customer relationship management
  • Módulo de Ventas
  • Módulo de Contabilidad
  • Módulo de Compras

1.2 GitHub para Odoo

Lo BƔsico que tenemos que conocer de GitHub y Linux para Odoo


git add .
git commit -m "Commit message"
git push

# Saber en que rama estamos
git branch -l

# Creamos la rama
git checkout -b 10.0

# Nos movemos a la rama
git checkout 10.0

# hacer push 10.0
git add .
git commit -m "Actualizando"
git push origin 10.0


# Fusionar ramas 
git checkout master
git merge 10.0

1.3 Erppeek para Odoo

Lo BƔsico que tenemos que Erppeek

          
# Conectarse
erppeek -u admin -d db10-chile-sii --server=http://127.0.0.1:8069

# Actualizar Módulo
client.upgrade('l10n_cl_base')
client.upgrade('base_travel_agency')
          

1.4 Linux Odoo

Linux comandos Odoo


# Cambiar pass root
passwd

# Borrar todo el contenido de un directorio en Linux
sudo rm -R carpeta

# Oculta y muetra archivos
CTRL + H 

# Ver estado de salud de nuestro servidor
top

# log
cd /var/log/odoo
rm odoo-server.log
du -hs

# Dir con tamaƱo
ls -lh
df -h

1.5 Linux Profile

Profile


nano .profile
source .profile


odoo_actualizar(){
  echo Deteniendo Odoo
  /etc/init.d/odoo stop
  for i in /opt/odoo/server/extra-addons/*
  do
    echo Actualizando $i
    git -C $i pull
  done
  if [[ ! -z "$1" ]]; then
    echo "Actualizando server odoo"
    sudo -u odoo python /opt/odoo/server/odoo-bin -c /etc/odoo/odoo.conf -d $1 -u all --stop-after-init
  fi
  /etc/init.d/odoo start
  echo Finalizado
}


1.6 Postgres

Profile


# Cambiar pass admin
sudo -u postgres psql -d db10-chile-sii
UPDATE res_users SET password='x1234567890', password_crypt='HASH' WHERE login='admin';

1.7 Acualizar Odoo

Profile


/etc/init.d/odoo stop
su - odoo -s /bin/bash
python /opt/odoo/server/odoo-bin -c /etc/odoo/odoo.conf -d erp -u all --stop-after-init

Fuente:


Marlon Falcon HernƔndez / http://falconsolutions.cl/odoo-precios

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

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.

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;
}

miƩrcoles, 18 de abril de 2018

Lectura Archivos Python 3 y Odoo 11

Lectura de archivos y conversión a base64




La actual migración de Python 2.7 a Python 3 para Odoo 11, fue un gran cambio en el manejo de algunos tipos de datos, por ejemplo en la lectura de un archivo que puede ser una imagen, un csv o un txt nos llega a generar errores ya que nos puede devolver una instancia IO o generarnos algunos errores, para ello me di a la tarea de indagar en el manejo interno de Odoo y el mismo me dio la solución, una pequeña linea capaz de leer cualquier archivo y convertirlo a Base64 para grabarlo en un Campo Binario de nuestra Tabla.

Primero Debemos Importar la libreria base64:

# -*- coding: utf-8 -*-

import base64



Ahora debemos obtener la ruta de la Imagen o archivo que queremos grabar en nuestra tabla, por ejemplo /tmp/qr_cfdi.png

La lectura de mi archivo y conversión quedaria de la siguiente manera:

f_b64 = base64.b64encode(open('/tmp/qr_cfdi.png','rb').read())


El paso final seria grabarlo en la tabla correspondiente, en mi caso la imagen QR necesito guardarla en la tabla de Facturas (account.invoice).

invoice_br = self.env['account.invoice].browse(ID_FACT)

invoice_br.cfdi_cbb = f_b64

# Utilizando el Metodo Write:

invoice_br.write({'cfdi_cbb':f_b64})



El ejemplo anterior utiliza el metodo browse para poder obtener una instancia de Facturas del registro en el cual quiero grabar la imagen obtenida de otro proceso, el campo cfdi_cbb es un binario de la tabla donde quiero guardar la imagen y el resultado podria observarse como la siguiente imagen:




viernes, 23 de junio de 2017

Descarga de Reporte con Nombres DinƔmicos en Odoo 9 y Odoo 10

Generación de Reportes con Nombres DinÔmicos en Odoo



Dentro de las grandes virtudes de Odoo podemos ver una actualización muy util al apartado de Reportes, la cual es la generación del nombre del archivo descargado (Ej. Pedido 01.pdf) de forma dinÔmica tomando información de un campo del formulario donde se imprime el Reporte.

El primero paso es activar el modo desarrollador.

Con este paso, podremos visualizar en Configuración --> Informes --> Formulario --> Campo Printed Report Name

Este campo nos permitirÔ ingresar una serie de evaluaciones del registro del cual se imprime la información para generar el nombre de descarga del Archivo PDF, por ejemplo en el reporte de Facturas podria generar el siguiente nombre:


El resultado sera de 2 maneras, si no existe numero de factura:


Existiendo un numero de folio (factura confirmada):


El Nombre del reporte con concatenaciones puede ser dinƔmico o fijo, reemplazando el valor original del reporte.


Espero les ayude tanto como a mi.