Search code examples
odooodoo-14

How to adjust the stock of a product tracked by lots in Odoo via API


OK so I have been banging my head at this problem for way too long by now.

I want to sync stock levels of a product that is tracked with lots between the webshop and Odoo. For this reason I need to be able to make a stock adjustment of a lot via the API (in this case in python).

I have found this possible way of doing it:

odoo(
    'stock.move',
    'create',
    [{
        "name": "Webshop stock adjustment",
        "company_id": 1,
        "location_id": 8, # warehouse
        "location_dest_id": 14, # virtual location
        "product_id": batch["product_id"][0],
        "product_uom": 1,
        "lot_ids": [batch["id"]], # I am searching for the id by the lot name beforehand
        "product_uom_qty": 1,
        "quantity_done": 1,
        "state": "done"
    }]
)

This, however, results in two moves! One move which has the correct lot, and another one without a specified lot. The latter move is faulty of course, as the product is tracked with lots. This results in a fault lot entry, where I can't change the quantity by hand, as the field is invalid. Worse, it results in wrong stock levels. You can see the problematic bookings here

I have tried to just create a stock.move.line, like so:

odoo(
    'stock.move.line',
    'create',
    [{
        "company_id": 1,
        "display_name": "Webshop adjustment", # does not appear
        "location_id": location_id,
        "location_dest_id": location_dest_id,
        "product_id": batch["product_id"][0],
        "product_uom_id": 1,
        "lot_id": batch["id"],
        "product_uom_qty": quantity,
        "qty_done": quantity,
        "state": "done" # has no effect
    }]
)

However that results in a line with no effect: Line

I have also tried to find the stock adjustment wizard, but the only one I found in the code as opposed to the UI, doesn't have a field for lots..

I'd be happy for any input on how to solve this problem!


Solution

  • Meanwhile I managed to solve this problem reliably. I needed to implement a function for that, rather than mucking around with the external API.

    The function here is expecting vals with the format below. It reduces whatever batch needs to go first.

    [{
        'sku': sku,
        'qty': quantity
    },]
    
    @api.model
    def reduce_lots(self, vals):
        log(vals)
        for product_req in vals:
            product = self.env['product.product'].search(
                [['default_code','=', product_req['sku']]]
                )
            if len(product) == 0:
                continue
            
            lots = self.env['stock.quant'].search(
                ['&',('product_id', '=', product[0]['id']),('on_hand', '=', True)], 
                order='removal_date asc'
                )
    
            move = self.env['stock.move'].create({
                'name': product_req['order'],
                'location_id': 8, # Our Warehouse
                'location_dest_id': 14, # Virtual Location, Customer. If you need to increase stock, reverse the two numbers.
                'product_id': product.id,
                'product_uom': product.uom_id.id,
                'product_uom_qty': product_req['qty'],
            })
            move._action_confirm()
            move._action_assign()
            product_req['lots'] = []
            for line in move.move_line_ids:
                line.write({'qty_done': line['product_uom_qty']})
                product_req['lots'].append({
                    '_qty': line['product_uom_qty'],
                    '_lot_id': line.lot_id.name,
                    '_best_before': line.lot_id.removal_date
                })
            move._action_done()
    
        return vals