Importando stocks con xmlrpc

Gustavo Orrillo
- 23/05/2022 - 3 min. de lectura

Para importar stocks en Odoo vamos a crear movimientos de inventario (modelo stock.move). Dicha funcionalidad lo que hace es por cada línea del ajuste de inventario, crea una línea de movimiento de stock cuya ubicación destino es la ubicación que queremos actualizar y la ubicación origen es la ubicación "Virtual Locations/Inventory Adjustment". No utilizamos ajustes de inventario debido a que dicho objeto no existe; antes existía pero ahora no existe más (lo que se hace ahora es actualizar directamente el objeto stock.quant lo que es bastante peligroso en mi opinión).

Procedamos a crear un script que va a actualizar los inventarios en base a un archivo de Excel, el cual se muestra a continuación:


Y el script de python para leer dicho archivo de excel es el que se lista a continuación:

#!/usr/bin/python3
from xmlrpc import client
import openpyxl
from datetime import datetime
url = 'http://localhost:8069'
common = client.ServerProxy('{}/xmlrpc/2/common'.format(url))
res = common.version()
dbname = 'demo_ecommerce'
user = 'admin'
pwd = 'admin'
uid = common.authenticate(dbname, user, pwd, {})
# prints Odoo version and UID to make sure we are connected
print(res)
print(uid)
models = client.ServerProxy('{}/xmlrpc/2/object'.format(url))
# Define la variable para leer el workbook
workbook = openpyxl.load_workbook("stocks.xlsx")
# Define variable para la planilla activa
worksheet = workbook.active
# Itera las filas para leer los contenidos de cada celda
rows = worksheet.rows
for x,row in enumerate(rows):
    # Saltea la primer fila porque tiene el nombre de las columnas
    if x == 0:
        continue
    # Lee cada una de las celdas en la fila
    vals = {}
    for i,cell in enumerate(row):
        print(i,cell.value)
        res = None
        if i == 0:
            col = 'location_dest_id'
            # Buscamos por complete_name ya que dicho campo provee toda la estructura de la ubicacion
            res = models.execute_kw(dbname,uid,pwd,'stock.location','search',[[['complete_name','=',cell.value]]])
            location_dest_id = res
        if i == 1:
            col = 'product_id'
            res = models.execute_kw(dbname,uid,pwd,'product.product','search',[[['default_code','=',cell.value]]])
            product_id = res
        if i == 2:
            col = 'inventory_quantity' 
        vals[col] = res and res[0] or cell.value
    if not location_id or not product_id:
        continue
# Lee unidad de medida product_data = models.execute_kw(dbname,uid,pwd,'product.product','read',[product_id]) vals['product_uom'] = product_data[0].get('uom_id')[0] vals['name'] = 'Actualizacion inventario %s' % product_data[0].get('name') vals['company_id'] = 1 vals['state'] = 'draft' vals['is_inventory'] = True # busca la ubicacion virtual de ajustes de inventario location_id = models.execute_kw(dbname,uid,pwd,'stock.location','search',[[['complete_name','=','Virtual Locations/Inventory adjustment'],         ['company_id','=',1]]]) if not location_id: continue vals['location_id'] = location_id[0]
Cada línea que se procesa crea un diccionario (vals) el cual creará la línea de ajuste de inventario en estado draft. Tengan en cuenta que se setea la compañía de la ubicación y el flag "is_inventory". Paso siguiente es crear el movimiento de stock.

move_id = models.execute_kw(dbname,uid,pwd,'stock.move','create',[vals]
print(move_id)
Esto va a crear los siguientes movimientos de stock



Ahora procederemos a crear sus líneas de movimientos de stock

# Agrega al diccionario el move_id y crea la línea de mov de stock
vals['move_id'] = move_id
# asigna la unidad de medida a product_uom_id y borra product_uom
vals['product_uom_id'] = vals.get('product_uom')
del vals['product_uom']
# borra name
del vals['name']
move_line_id = models.execute_kw(dbname,uid,pwd,'stock.move.line','create',[vals])
print(move_line_id)

Esto crea los movimientos contables con sus correspondientes líneas


Aquí nos encontramos con un problema, se debe confirmar el movimiento de stock. Pero para ello hay que invocar el método _action_done el cual, al ser un método "privado" no puede ser invocado mediante xmlrpc. Es por ello que creamos un módulo llamado stock_done, el cual permite llamar desde el exterior la acción de validación

def action_done(self):
    for rec in self:
        self._action_done()
return True

Retornamos un valor True porque para invocar métodos mediante xmlrpc, dichos métodos deben retornar un valor. Luego invocamos el método action_done mediante xmlrpc de la siguiente manera:

return_id = models.execute(dbname,uid,pwd,'stock.move','action_done',[move_id])
print(return_id)

Y esto actualiza los stocks, como se puede ver en las siguientes líneas:


Un par de aclaraciones. Este script agrega stock al inventario, no lo ajusta. Para hacer ajustes se debe tener en cuenta cual es el inventario actual para determinar la cantidad a mover. Por otra parte, no se agregaron ni números de lote ni paquetes.

Por último, el código y el archivo de ejemplo se puede encontrar en nuestro github.

Acerca de:

Gustavo Orrillo

Passionate about programming, he has implemented Odoo for different types of businesses since 2010. In Moldeo Interactive he is a founding Partner and Programmer; In addition to writing on the Blog about different topics related to the developments he makes.