Como importar stocks en Odoo usando xmlrpc

29 de junio de 2023 por
Gustavo Orrillo
| Sin comentarios aún
 

En sus inicios Odoo tenía objetos para administrar los ajustes de inventarios. Ahora no, si prestan atención notarán que los ajustes de inventario son en realidad actualizaciones al modelo stock.quant. 

Nosotros para importar stocks en Odoo realizamos movimientos de inventario (modelo stock.move, que es al fin y al cabo lo que sucede cada vez que se hace un ajuste de inventario). En un ajuste de inventario; por cada línea que se debe procesar se 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". 

Para ello vamos a procesar un archivo formato Excel con el formato que detallamos 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]



Por cada línea procesada secrea un diccionario (vals) el cual crea una 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


Primer issue conque nos vamos a encontrar, el movimiento de stock debe ser confirmado. Pero no se puede invocar el método _action_done con xmlrpc debido a que el mismo retorna un valor None. Para eso creamos un módulo llamado stock_done, el cual permite llamar al método _action_donde desde xmlrpc.

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

Aquí hacemos que el método retorne un valor... que puede ser cualquiera. Lo importante es que cuando invocamos 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:


Antes de terminar, algunas aclaraciones. Este script agrega stock al inventario, no lo ajusta. Si desean ajustar stocks primero se debe consultar el inventario, y luego mandar a scrap las unidades necesarias (en el caso que se deba descartar stock) o hacer el ajuste de inventario correspondiente. Por otra parte, aquí no se agrega ni números de serie ni lotes.

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


Gustavo Orrillo 29 de junio de 2023
Compartir
Categorías
Archivar
Identificarse dejar un comentario