Skip to main content

Overview

The stock.request model represents an individual stock request in the system. It inherits from stock.request.abstract and manages the lifecycle of requests from draft through to completion, including procurement rule execution, allocation tracking, and integration with Odoo’s stock moves and pickings. Model Name: stock.request Inherits: stock.request.abstract, mail.thread, mail.activity.mixin Order: id desc

Fields

Core Fields

name
Char
Unique identifier for the stock request. Auto-generated from sequence stock.request if not provided.
state
Selection
required
Current status of the stock request.Options:
  • draft - Draft
  • open - In progress
  • done - Done
  • cancel - Cancelled
Properties: copy=False, default="draft", index=True, readonly=True, tracking=True
requested_by
Many2one
required
Reference to res.users. The user who requested the stock.Default: Current user (self.env.uid)Properties: tracking=True
expected_date
Datetime
required
Date when you expect to receive the goods.Properties: index=True, required=True
picking_policy
Selection
required
Defines how products should be received.Options:
  • direct - Receive each product when available
  • one - Receive all products at once
Default: "direct"

Product & Quantity Fields

product_id
Many2one
Reference to product.product. The product being requested.Domain: [('type', 'in', ['product', 'consu'])]Inherited from: stock.request.abstract
product_uom_id
Many2one
Reference to uom.uom. Product unit of measure.Inherited from: stock.request.abstract
product_uom_qty
Float
required
Quantity specified in the unit of measure indicated in the request.Digits: Product Unit of MeasureInherited from: stock.request.abstract
qty_in_progress
Float
Quantity in progress (not yet completed).Properties: readonly=True, compute="_compute_qty", store=TrueDigits: Product Unit of Measure
qty_done
Float
Quantity completed.Properties: readonly=True, compute="_compute_qty", store=TrueDigits: Product Unit of Measure
qty_cancelled
Float
Quantity cancelled.Properties: readonly=True, compute="_compute_qty", store=TrueDigits: Product Unit of Measure

Location & Warehouse Fields

warehouse_id
Many2one
required
Reference to stock.warehouse. The warehouse where stock is requested.Inherited from: stock.request.abstract
location_id
Many2one
required
Reference to stock.location. The specific location within the warehouse.Domain: [('usage', 'in', ['internal', 'transit'])] (when virtual locations not allowed)Inherited from: stock.request.abstract

Procurement & Routing Fields

route_id
Many2one
Reference to stock.route. The route to use for procurement.Domain: [('id', 'in', route_ids)]Inherited from: stock.request.abstract
procurement_group_id
Many2one
Reference to procurement.group. Moves created through this stock request will be put in this procurement group.Inherited from: stock.request.abstract

Relational Fields

order_id
Many2one
Reference to stock.request.order. The parent order if this request is part of an order.Properties: readonly=True
allocation_ids
One2many
Reference to stock.request.allocation records with inverse_name="stock_request_id".Links to all allocations associated with this stock request.
move_ids
One2many
Reference to stock.move records.Properties: compute="_compute_move_ids", readonly=TrueAll stock moves related to this request (including origin moves).
picking_ids
One2many
Reference to stock.picking records.Properties: compute="_compute_picking_ids", readonly=TrueAll pickings associated with this stock request.
picking_count
Integer
Number of delivery orders/pickings.Properties: compute="_compute_picking_ids", readonly=True
company_id
Many2one
required
Reference to res.company. The company this request belongs to.Inherited from: stock.request.abstract

Methods

Action Methods

action_confirm
method
Confirms the stock request and launches procurement rules.Returns: TrueBehavior:
  • Calls _action_confirm() internally
  • Changes state from draft to open
  • Triggers procurement rule execution via _action_launch_procurement_rule()
stock_request/models/stock_request.py:268
def action_confirm(self):
    self._action_confirm()
    return True
action_draft
method
Resets the stock request to draft state.Returns: True
stock_request/models/stock_request.py:272
def action_draft(self):
    self.write({"state": "draft"})
    return True
action_cancel
method
Cancels the stock request and all associated stock moves.Returns: TrueBehavior:
  • Cancels all related stock moves using sudo()
  • Sets state to cancel
stock_request/models/stock_request.py:276
def action_cancel(self):
    self.sudo().mapped("move_ids")._action_cancel()
    self.write({"state": "cancel"})
    return True
action_done
method
Marks the stock request as done.Returns: True
stock_request/models/stock_request.py:281
def action_done(self):
    self.write({"state": "done"})
    return True
action_view_transfer
method
Opens the transfer/picking view for this stock request.Returns: dict - Action dictionary for viewing pickingsBehavior:
  • Shows tree view if multiple pickings
  • Shows form view if single picking
stock_request/models/stock_request.py:442
def action_view_transfer(self):
    action = self.env["ir.actions.act_window"]._for_xml_id(
        "stock.action_picking_tree_all"
    )
    pickings = self.mapped("picking_ids")
    if len(pickings) > 1:
        action["domain"] = [("id", "in", pickings.ids)]
    elif pickings:
        action["views"] = [(self.env.ref("stock.view_picking_form").id, "form")]
        action["res_id"] = pickings.id
    return action

Procurement Methods

_prepare_procurement_values
method
Prepares values for procurement group execution.Parameters:
  • group_id (int/bool, optional) - Procurement group ID
Returns: dict - Values dictionary for procurementKeys in returned dict:
  • date_planned: Expected date for the procurement
  • warehouse_id: Target warehouse
  • stock_request_allocation_ids: ID of this stock request
  • group_id: Procurement group ID
  • route_ids: Route to use
  • stock_request_id: ID of this stock request
stock_request/models/stock_request.py:321
def _prepare_procurement_values(self, group_id=False):
    return {
        "date_planned": self.expected_date,
        "warehouse_id": self.warehouse_id,
        "stock_request_allocation_ids": self.id,
        "group_id": group_id or self.procurement_group_id.id or False,
        "route_ids": self.route_id,
        "stock_request_id": self.id,
    }
_action_launch_procurement_rule
method
Launches procurement rules to fulfill the stock request.Returns: TrueRaises: UserError if procurement failsBehavior:
  • Skips if state is not draft or product type is not storable
  • Checks if stock is available first (if company setting enabled)
  • Creates procurement group request
  • Executes procurement rules (_run_move, _run_buy, or _run_manufacture)
If company_id.stock_request_check_available_first is enabled and sufficient stock is available in the location, it will use available stock instead of creating procurement.
stock_request/models/stock_request.py:381
def _action_launch_procurement_rule(self):
    # Launch procurement group run method with required fields
    # See source for full implementation
_action_use_stock_available
method
Creates stock moves for available stock and marks them as done.Behavior:
  • Gathers available quants from the location
  • Creates stock moves for available quantities
  • Confirms and immediately completes the moves
  • Creates allocations linking moves to the request
stock_request/models/stock_request.py:360
def _action_use_stock_available(self):
    # Create a stock move with the necessary data and mark it as done

Preparation Methods

_prepare_stock_move
method
Prepares values for creating a stock move.Parameters:
  • qty (float) - Quantity for the move
Returns: dict - Values for stock move creationKeys in returned dict:
  • name: Product display name
  • company_id: Company ID
  • product_id: Product ID
  • product_uom_qty: Quantity
  • product_uom: Product UoM ID
  • location_id: Source location ID
  • location_dest_id: Destination location ID
  • state: Move state (“draft”)
  • reference: Stock request name
stock_request/models/stock_request.py:340
def _prepare_stock_move(self, qty):
    return {
        "name": self.product_id.display_name,
        "company_id": self.company_id.id,
        "product_id": self.product_id.id,
        "product_uom_qty": qty,
        "product_uom": self.product_id.uom_id.id,
        "location_id": self.location_id.id,
        "location_dest_id": self.location_id.id,
        "state": "draft",
        "reference": self.name,
    }
_prepare_stock_request_allocation
method
Prepares values for creating a stock request allocation.Parameters:
  • move (stock.move) - The stock move record
Returns: dict - Values for allocation creation
stock_request/models/stock_request.py:353
def _prepare_stock_request_allocation(self, move):
    return {
        "stock_request_id": self.id,
        "stock_move_id": move.id,
        "requested_product_uom_qty": move.product_uom_qty,
    }

Status Check Methods

check_done
method
Checks if the stock request should be marked as done.Returns: TrueBehavior:
  • Compares allocated quantity with requested quantity
  • Automatically calls action_done() if fully allocated
  • Calls write({"state": "cancel"}) if cancelled but not fulfilled
stock_request/models/stock_request.py:290
def check_done(self):
    precision = self.env["decimal.precision"].precision_get(
        "Product Unit of Measure"
    )
    for request in self:
        allocated_qty = sum(request.allocation_ids.mapped("allocated_product_qty"))
        qty_done = request.product_id.uom_id._compute_quantity(
            allocated_qty, request.product_uom_id
        )
        if (
            float_compare(
                qty_done, request.product_uom_qty, precision_digits=precision
            )
            >= 0
        ):
            request.action_done()
        elif request._check_cancel_allocation():
            request.write({"state": "cancel"})
    return True
check_cancel
method
Checks if the stock request should be cancelled.Behavior:
  • Calls _check_cancel_allocation() to determine if cancellation is appropriate
  • Automatically sets state to cancel if conditions are met
stock_request/models/stock_request.py:285
def check_cancel(self):
    for request in self:
        if request._check_cancel_allocation():
            request.write({"state": "cancel"})

Compute Methods

_compute_move_ids
method
Computes all stock moves related to this request, including origin moves.Depends on: allocation_ids, allocation_ids.stock_move_id
stock_request/models/stock_request.py:133
@api.depends("allocation_ids", "allocation_ids.stock_move_id")
def _compute_move_ids(self):
    for request in self:
        move_ids = request.allocation_ids.mapped("stock_move_id")
        all_moves = self.env["stock.move"]
        for move in move_ids:
            all_moves |= self._get_all_origin_moves(move)
        request.move_ids = all_moves
_compute_picking_ids
method
Computes all pickings related to this request.Depends on: allocation_ids, allocation_ids.stock_move_id, allocation_ids.stock_move_id.picking_id
stock_request/models/stock_request.py:142
@api.depends(
    "allocation_ids",
    "allocation_ids.stock_move_id",
    "allocation_ids.stock_move_id.picking_id",
)
def _compute_picking_ids(self):
    for request in self:
        request.picking_count = 0
        request.picking_ids = self.env["stock.picking"]
        request.picking_ids = request.move_ids.filtered(
            lambda m: m.state != "cancel"
        ).mapped("picking_id")
        request.picking_count = len(request.picking_ids)
_compute_qty
method
Computes quantity fields: qty_done, qty_in_progress, and qty_cancelled.Depends on:
  • allocation_ids
  • allocation_ids.stock_move_id.state
  • allocation_ids.stock_move_id.move_line_ids
  • allocation_ids.stock_move_id.move_line_ids.quantity
Logic:
  • qty_done: Absolute difference between incoming and outgoing allocated quantities
  • qty_in_progress: Sum of open product quantities from allocations
  • qty_cancelled: Remaining quantity that won’t be fulfilled
stock_request/models/stock_request.py:156
@api.depends(
    "allocation_ids",
    "allocation_ids.stock_move_id.state",
    "allocation_ids.stock_move_id.move_line_ids",
    "allocation_ids.stock_move_id.move_line_ids.quantity",
)
def _compute_qty(self):
    # See source for implementation details

Constraints

SQL Constraints

name_uniq: Stock Request name must be unique per company.

Python Constraints

_check_qty
Validates product quantity based on state.Constraint: state, product_qtyRules:
  • In draft state: quantity must be strictly positive (> 0)
  • In other states: quantity cannot be negative (>= 0)
Raises: ValidationError
check_order_requested_by
Ensures requested_by matches the parent order’s requested_by.Constraint: order_id, requested_byRaises: ValidationError
check_order_warehouse_id
Ensures warehouse_id matches the parent order’s warehouse_id.Constraint: order_id, warehouse_idRaises: ValidationError
check_order_location
Ensures location_id matches the parent order’s location_id.Constraint: order_id, location_idRaises: ValidationError
check_order_procurement_group
Ensures procurement_group_id matches the parent order’s procurement_group_id.Constraint: order_id, procurement_group_idRaises: ValidationError
check_order_company
Ensures company_id matches the parent order’s company_id.Constraint: order_id, company_idRaises: ValidationError
check_order_expected_date
Ensures expected_date matches the parent order’s expected_date.Constraint: order_id, expected_dateRaises: ValidationError
check_order_picking_policy
Ensures picking_policy matches the parent order’s picking_policy.Constraint: order_id, picking_policyRaises: ValidationError

Lifecycle

Create

create
method
Creates new stock request records with auto-generated names and expected dates.Decorator: @api.model_create_multiBehavior:
  • Auto-generates name from sequence stock.request if not provided or equals ”/”
  • Sets expected_date from order if order_id is provided, otherwise uses _get_expected_date()
stock_request/models/stock_request.py:454
@api.model_create_multi
def create(self, vals_list):
    vals_list_upd = []
    for vals in vals_list:
        upd_vals = vals.copy()
        if upd_vals.get("name", "/") == "/":
            upd_vals["name"] = self.env["ir.sequence"].next_by_code("stock.request")
        if "order_id" in upd_vals:
            order_id = self.env["stock.request.order"].browse(upd_vals["order_id"])
            upd_vals["expected_date"] = order_id.expected_date
        else:
            upd_vals["expected_date"] = self._get_expected_date()
        vals_list_upd.append(upd_vals)
    return super().create(vals_list_upd)
Deletes stock request records.Raises: UserError if any record is not in draft state
stock_request/models/stock_request.py:469
def unlink(self):
    if self.filtered(lambda r: r.state != "draft"):
        raise UserError(_("Only requests on draft state can be unlinked"))
    return super().unlink()

Usage Example

# Create a stock request
request = env['stock.request'].create({
    'product_id': product.id,
    'product_uom_qty': 10.0,
    'warehouse_id': warehouse.id,
    'location_id': location.id,
    'requested_by': env.user.id,
})

# Confirm the request (triggers procurement)
request.action_confirm()

# Check status
print(f"State: {request.state}")
print(f"In Progress: {request.qty_in_progress}")
print(f"Done: {request.qty_done}")
print(f"Cancelled: {request.qty_cancelled}")

# View associated pickings
if request.picking_count > 0:
    action = request.action_view_transfer()

See Also

Build docs developers (and LLMs) love