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, 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.


viernes, 23 de octubre de 2020

Script subida automatica de respaldos

 Subir automáticamente archivos mediante Script Bash

El día de hoy les presento un script sencillo, practico y 100% funcional para poder transferir respaldos mediante un script el cual podemos añadir al servicio cron para gestionar su planificación.


Algunos servidores FTP pueden requerir el modo pasivo para la transferencia de archivos:

#!/bin/bash
cd /data/odoo/backups/
HOST="ftp.poncesoft.com"
USER="odoo"
PASS="pass123"
PORT="22"
## Respaldo Base ##
FILE="*.dump"
## Respaldo de Binarios ##
FILE2="*.tgz"
REMOTEPATH='/data/backups'
## NOTA ###
## Este script se debe ejecutar directamente sobre la raiz de los archivos a subir.

ftp -p -inv $HOST << EOF

user $USER $PASS

cd $REMOTEPATH

put $FILE

put $FILE2

bye

EOF


En alguno no sera necesario entonces el script no debe contener el parameto -p :

#!/bin/bash
cd /data/odoo/backups/
HOST="ftp.poncesoft.com"
USER="odoo"
PASS="pass123"
PORT="22"
## Respaldo Base ##
FILE="*.dump"
## Respaldo de Binarios ##
FILE2="*.tgz"
REMOTEPATH='/data/backups'
## NOTA ###
## Este script se debe ejecutar directamente sobre la raiz de los archivos a subir.

ftp -inv $HOST << EOF

user $USER $PASS

cd $REMOTEPATH

put $FILE

put $FILE2

bye

EOF

Gracias por su visita.

martes, 20 de octubre de 2020

Creacion de usuarios Linux

Crear usuario con privilegios de root



Existen varios métodos para crear un nuevo usuario con privilegios de root en Linux.


Método rápido

Se trata de crearlo de golpe añadiéndolo al grupo desde el propio comando useradd.


Añadir el usuario:

sudo useradd -u 0 -o -g 0 nombreusuario

Establecer la nueva contraseña:

sudo passwd nombreusuario


Método típico

Añadir el usuario:

sudo adduser nombreusuario

sudo /usr/sbin/visudo

En este fichero, añadimos después de la línea donde pone ‘root’, el nombre de nuestro usuario, con las líneas de ALL iguales.

# User privilege specification

root            ALL=(ALL:ALL) ALL 

nombreusuario	ALL=(ALL:ALL) ALL


Ante cualquier error podemos eliminar el usuario:

sudo userdel nombreusuario

O también cambiar la contraseña:

sudo passwd nombreusuario

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

martes, 6 de octubre de 2020

Solucion al Error : bus.Bus unavailable

 

Error : bus.Bus unavailable



Este error ocurre cuando activamos los Workers para resolverlo tenemos que 

hacer lo siguiente en el archivo: nano /etc/odoo/odoo.conf

proxy_mode = True
longpolling_port = 8072
xmlrpc_port = 8069
xmlrpc_interface = 127.0.0.1
netrpc_interface = 127.0.0.1

Luego en el archivo : nano /etc/nginx/sites-available/default

upstream odoochat{
 server 127.0.0.1:8072;
}

location /longpolling {
        proxy_pass http://odoochat;
        }

Si mostramos los archivos completos quedaría

nano /etc/odoo/odoo.conf

[options]
addons_path = /opt/odoo/server/addons
admin_passwd = odoo14
db_host = False
db_name = False
db_password = odoo14
;db_port = 5432
db_user = odoo
;dbfilter = .*
list_db = True
log_db = False
logfile = /var/log/odoo/odoo-server.log
logrotate = True
;log_level = warn

proxy_mode = True
longpolling_port = 8072
xmlrpc_port = 8069
xmlrpc_interface = 127.0.0.1
netrpc_interface = 127.0.0.1

workers = 17
limit_time_real = 1200
limit_time_cpu = 600

nano /etc/nginx/sites-available/default

upstream odoo{
    server localhost:8069;
}


upstream odoochat{
 server 127.0.0.1:8072;
}

server {
        listen 80 default;
        server_name erp.yourdomain.com 50.69.84.113;
        keepalive_timeout           600;
        client_header_timeout       600;
        client_max_body_size 200M;
        client_body_timeout         600;
        proxy_connect_timeout       600;
        proxy_send_timeout          600;
        proxy_read_timeout          600;
        send_timeout                600;

        root /usr/share/nginx/html;
        index index.html index.htm;

        proxy_buffers 16 64k;
        proxy_buffer_size 128k;

        location / {
            proxy_pass  http://odoo;
            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;
            # invento mio
            proxy_set_header   X-ODOO_DBFILTER db;
            proxy_pass_request_headers on;
        }
tiny_mce_marker_
        location /longpolling {
        proxy_pass http://odoochat;
        }
}

miércoles, 5 de agosto de 2020

Revision Bloqueos de Base de datos por medio de una Vista

Generación de una vista para Monitorear bloqueos de tablas en nuestra Base de Datos


El primer punto seria conectarnos a nuestra base de datos, exportando nuestros accesos o de la forma tradicional:


sudo su postgres

psql MI_BASE

Ejecutamos el sig. fragmento:


CREATE VIEW lock_monitor AS(
SELECT
  COALESCE(blockingl.relation::regclass::text,blockingl.locktype) as locked_item,
  now() - blockeda.query_start AS waiting_duration, blockeda.pid AS blocked_pid,
  blockeda.query as blocked_query, blockedl.mode as blocked_mode,
  blockinga.pid AS blocking_pid, blockinga.query as blocking_query,
  blockingl.mode as blocking_mode
FROM pg_catalog.pg_locks blockedl
JOIN pg_stat_activity blockeda ON blockedl.pid = blockeda.pid
JOIN pg_catalog.pg_locks blockingl ON(
  ( (blockingl.transactionid=blockedl.transactionid) OR
  (blockingl.relation=blockedl.relation AND blockingl.locktype=blockedl.locktype)
  ) AND blockedl.pid != blockingl.pid)
JOIN pg_stat_activity blockinga ON blockingl.pid = blockinga.pid
  AND blockinga.datid = blockeda.datid
WHERE NOT blockedl.granted
AND blockinga.datname = current_database()
);


Ahora para visualizar los bloqueos en tiempo real solo tendremos que ejecutar la vista:


SELECT * from lock_monitor;

Si en algún momento decidimos eliminar el proceso que esta bloqueando nuestra tabla, ya sea por que se ciclo en Odoo o nos esta provocando un retraso en la operación el comando seria el siguiente:



SELECT pg_terminate_backend();

Dentro de los parentesis tendremos que meter el PID (ID del proceso) que queremos matar o eliminar este nos los arroja la vita del monitor.


martes, 4 de agosto de 2020

Demonio - Servicio para Odoo13 Ubuntu 17.x Ubuntu 18.x Ubuntu 20.x

Servicio Systemctl en Ubuntu para nuestra instalación de Odoo
Fácil y Rápido


Creamos el servicio en la ruta: /usr/lib/systemd/system

Usamos el comando nano /usr/lib/systemd/system/odoo-server.service y pegamos la instrucción en mi caso mi Odoo esta en el directorio /odoo13


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Unit]

Description=Servicio Arranque Odoo13

After=network.target

[Service]

Type=simple

Restart=always

RestartSec=1

User=odoo

ExecStart=/usr/bin/python3 /odoo13/odoo-bin -c /odoo13/odoo-server.conf



[Install]

WantedBy=multi-user.target

Reiniciamos los para ctualziar los nuevos servicios disponibles:

systemctl daemon-reload

Iniciamos como prueba nuestro servicio:

systemctl start odoo-server

Habilitamos con el servicio para iniciar con el Sistema Operativo:

systemctl enable odoo-server


NOTAS:
El directorio de Python es obligatorio, se tiene que escribir la ruta completa del binario de python ya sea que este en la ruta por defecto o que lo hayamos compilado manualmente.

martes, 14 de julio de 2020

Obtener registros Hijos de un registro Padre en Odoo

Como utilizar child of para buscar registros hijos en Odoo



Algo muy frecuente y que estoy seguro que te as preguntado es como poder obtener las categorías hijas de un registro, por ejemplo en los modelos:

  • Ubicaciones
  • Categorias
  • Partners

Existen columnas dentro de los lineamientos de Odoo que nos permiten tener un control en un esquema de arbol:
  • Parent Left
  • Parent Right
  • Parent ID

Con estos campos podemos saber de donde la estructura de la que se compone nuestro registro y por medio de ellas buscar sus hijos:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
## Iniciamos nuestra lista final ###
all_record_ids = []
#### La variable que usare como ejemplo es record, que es una instancia de un registro de X tabla que usa el esquema de hijos ###
#### Iniciamos nuestro dominio, en este ejemplo usare una variable para no tener todo en una sola linea ####
child_dom = [('parent_left', '>', record.parent_left), ('parent_left', '<', record.parent_right)] 
#### Buscamos los registros que cumplen mi condicion ####
child_records = self.search(child_dom)
#### Agregamos a mi lista final, el registro principal (record) y agregamos los hijos ####
all_record_ids.append(record.id)
all_record_ids=all_record_ids+child_records.ids


Yo por ejemplo, necesitaba saber las categorías hijas de un registro en donde mi variable era category:

1
2
3
product_category = self.env['product.category']
child_dom = [('parent_left', '>', category.parent_left), ('parent_left', '<', category.parent_right)] 
child_categs = product_category.search(child_dom)

jueves, 9 de julio de 2020

Descomprimir lista de archivos ZIP Bash

#1: Descomprimir utilizando un comodin (instrucción corta)

La sintaxis es la siguiente:
unzip '*.zip'
Un ejemplo facil:
$ cd /disk2/images/
$ unzip '*.zip'
$ ls -l
Nota*.zip se encuentra dentro de comillas simples.

#2: Descomprimir utilizando un bucle (instrucción larga)

La Sintaxis es la siguiente:
 for z in *.zip; do unzip $z; done
Gracias.

miércoles, 8 de julio de 2020

Numero a Letra Correspondiente (Excel) - Función Python

Función para transformar un indice a su letra correspondiente
(Similar a las columnas en Excel)



El siguiente método me resulto muy útil al momento de realizar un reporte en Excel con columnas dinámicas, por medio de un indice que podia incrementar el no. de columnas a insertar, con ello logre tener un reporte muy dinamico, mi problema era conocer que letra le correspondía al indice, ya que desconocía el no. de columnas finales.

Mi problema lo solucione con el siguiente metodo:


1
2
3
4
5
6
7
8
def indice_to_column_string(n):
    string = ""
    while n > 0:
        n, remainder = divmod(n - 1, 26)
        string = chr(65 + remainder) + string
    return string

print(indice_to_column_string(28))


La variable que recibe mi metodo es el indice del cual quisiera saber la columna correspondiente.


IPV6 Linux check - Visualizar nuestra IP V6 Linux

Show IPv6 - Mostra IPv6 Linux

 

Existen varias formas, por ejemplo el siguiente comando:


1
ip addr show dev eth0 | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d'

Otro comando que nos arroja la IPv6:

1
/sbin/ifconfig | grep inet6

El caso de tener error al utilizar ifconfig, debemos instalar las aplicaciones de net-tools:


1
sudo apt-get install net-tools