Skip to main content

Overview

The stock.request.order model acts as a container for grouping multiple stock requests together. It provides unified control over warehouse, location, expected date, and shipping policy for all contained requests. The order’s state is computed based on the states of its child requests. Model Name: stock.request.order Inherits: mail.thread, mail.activity.mixin Order: id desc

Fields

Core Fields

name
Char
required
Unique identifier for the stock request order. Auto-generated from sequence stock.request.order if not provided.Properties: copy=False, readonly=True, default="/"
state
Selection
Current status of the stock request order. Computed from child request states.Options:
  • draft - Draft (all requests are draft)
  • open - In progress (mixed states)
  • done - Done (all requests are done or cancelled)
  • cancel - Cancelled (all requests are cancelled)
Properties: copy=False, default="draft", index=True, readonly=True, tracking=True, compute="_compute_state", store=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. Applied to all child requests.Default: fields.Datetime.nowProperties: index=True, required=True
picking_policy
Selection
required
Defines how products should be received for all requests in the order.Options:
  • direct - Receive each product when available
  • one - Receive all products at once
Default: "direct"

Location & Warehouse Fields

warehouse_id
Many2one
required
Reference to stock.warehouse. The warehouse where stock is requested.Properties: check_company=True, ondelete="cascade", required=True
location_id
Many2one
required
Reference to stock.location. The specific location within the warehouse.Domain: [('usage', 'in', ['internal', 'transit'])] (when virtual locations not allowed)Properties: ondelete="cascade", required=True
allow_virtual_location
Boolean
Whether virtual locations are allowed.Related: company_id.stock_request_allow_virtual_locProperties: readonly=True

Procurement & Company Fields

procurement_group_id
Many2one
Reference to procurement.group. Moves created through stock requests in this order will be put in this procurement group.Help: If none is given, moves generated by procurement rules will be grouped into one big picking.
company_id
Many2one
required
Reference to res.company. The company this order belongs to.Default: self.env.company

Route Fields

route_id
Many2one
Reference to stock.route. The route related to a stock request order. When set, applied to all child requests.Properties: compute="_compute_route_id", inverse="_inverse_route_id", readonly=True, store=True
route_ids
Many2many
Reference to stock.route. Available routes based on warehouse and location.Properties: compute="_compute_route_ids", readonly=True, store=True

Relational Fields

stock_request_ids
One2many
Reference to stock.request records with inverse_name="order_id".All stock requests contained in this order.Properties: copy=True
stock_request_count
Integer
Number of stock requests in this order.Properties: compute="_compute_stock_request_count", readonly=True
move_ids
One2many
Reference to stock.move records from all child requests.Properties: compute="_compute_move_ids", readonly=True
picking_ids
One2many
Reference to stock.picking records from all child requests.Properties: compute="_compute_picking_ids", readonly=True
picking_count
Integer
Number of delivery orders/pickings.Properties: compute="_compute_picking_ids", readonly=True

Methods

Action Methods

action_confirm
method
Confirms all stock requests in the order.Returns: TrueRaises: UserError if there are no stock request items in the orderBehavior:
  • Validates that at least one request exists
  • Calls action_confirm() on all child stock_request_ids
stock_request/models/stock_request_order.py:300
def action_confirm(self):
    if not self.stock_request_ids:
        raise UserError(
            _("There should be at least one request item for confirming the order.")
        )
    self.mapped("stock_request_ids").action_confirm()
    return True
action_draft
method
Resets all stock requests in the order to draft state.Returns: True
stock_request/models/stock_request_order.py:308
def action_draft(self):
    self.mapped("stock_request_ids").action_draft()
    return True
action_cancel
method
Cancels all stock requests in the order.Returns: True
stock_request/models/stock_request_order.py:312
def action_cancel(self):
    self.mapped("stock_request_ids").action_cancel()
    return True
action_done
method
Marks the order as done (currently no-op, state computed from requests).Returns: True
stock_request/models/stock_request_order.py:316
def action_done(self):
    return True
action_view_transfer
method
Opens the transfer/picking view for all pickings in this order.Returns: dict - Action dictionary for viewing pickingsBehavior:
  • Shows tree view if multiple pickings
  • Shows form view if single picking
stock_request/models/stock_request_order.py:319
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
action_view_stock_requests
method
Opens the view for stock requests in this order.Returns: dict - Action dictionary for viewing stock requestsBehavior:
  • Shows tree view if multiple requests
  • Shows form view if single request
stock_request/models/stock_request_order.py:332
def action_view_stock_requests(self):
    action = self.env["ir.actions.act_window"]._for_xml_id(
        "stock_request.action_stock_request_form"
    )
    if len(self.stock_request_ids) > 1:
        action["domain"] = [("order_id", "in", self.ids)]
    elif self.stock_request_ids:
        action["views"] = [
            (self.env.ref("stock_request.view_stock_request_form").id, "form")
        ]
        action["res_id"] = self.stock_request_ids.id
    return action

Compute Methods

_compute_state
method
Computes the order state based on child request states.Depends on: stock_request_ids.stateLogic:
  • draft: No requests OR all requests are draft
  • cancel: All requests are cancel
  • done: All requests are done or cancel
  • open: Mixed states (in progress)
stock_request/models/stock_request_order.py:211
@api.depends("stock_request_ids.state")
def _compute_state(self):
    for item in self:
        states = item.stock_request_ids.mapped("state")
        if not item.stock_request_ids or all(x == "draft" for x in states):
            item.state = "draft"
        elif all(x == "cancel" for x in states):
            item.state = "cancel"
        elif all(x in ("done", "cancel") for x in states):
            item.state = "done"
        else:
            item.state = "open"
_compute_route_ids
method
Computes available routes based on warehouse, location, and child requests.Depends on: warehouse_id, location_id, stock_request_idsBehavior:
  • Finds routes associated with the warehouse
  • Filters routes based on location hierarchy
  • If requests exist, computes common routes across all requests
stock_request/models/stock_request_order.py:150
@api.depends("warehouse_id", "location_id", "stock_request_ids")
def _compute_route_ids(self):
    # Complex logic to determine available routes
    # See source for full implementation
_compute_route_id
method
Computes the single route ID if all child requests use the same route.Depends on: stock_request_idsLogic:
  • If all requests have the same route, sets that route
  • If routes differ across requests, sets to False
stock_request/models/stock_request_order.py:191
@api.depends("stock_request_ids")
def _compute_route_id(self):
    for order in self:
        if order.stock_request_ids:
            first_route = order.stock_request_ids[0].route_id or False
            if any(r.route_id != first_route for r in order.stock_request_ids):
                first_route = False
            order.route_id = first_route
_compute_picking_ids
method
Computes all pickings from child requests.Depends on: stock_request_ids.allocation_ids
stock_request/models/stock_request_order.py:224
@api.depends("stock_request_ids.allocation_ids")
def _compute_picking_ids(self):
    for record in self:
        record.picking_ids = record.stock_request_ids.mapped("picking_ids")
        record.picking_count = len(record.picking_ids)
_compute_move_ids
method
Computes all stock moves from child requests.Depends on: stock_request_ids
stock_request/models/stock_request_order.py:230
@api.depends("stock_request_ids")
def _compute_move_ids(self):
    for record in self:
        record.move_ids = record.stock_request_ids.mapped("move_ids")
_compute_stock_request_count
method
Computes the number of stock requests in the order.Depends on: stock_request_ids
stock_request/models/stock_request_order.py:235
@api.depends("stock_request_ids")
def _compute_stock_request_count(self):
    for record in self:
        record.stock_request_count = len(record.stock_request_ids)

Inverse Methods

_inverse_route_id
method
Applies the order’s route to all child requests.Behavior:
  • When route_id is set on the order, writes it to all stock_request_ids
stock_request/models/stock_request_order.py:200
def _inverse_route_id(self):
    for order in self:
        if order.route_id:
            order.stock_request_ids.write({"route_id": order.route_id.id})

Onchange Methods

onchange_route_id
method
Updates all child requests when route changes.Decorator: @api.onchange("route_id")
stock_request/models/stock_request_order.py:205
@api.onchange("route_id")
def _onchange_route_id(self):
    if self.route_id:
        for request in self.stock_request_ids:
            request.route_id = self.route_id
onchange_requested_by
method
Propagates requested_by change to child requests.Decorator: @api.onchange("requested_by")
onchange_expected_date
method
Propagates expected_date change to child requests.Decorator: @api.onchange("expected_date")
onchange_picking_policy
method
Propagates picking_policy change to child requests.Decorator: @api.onchange("picking_policy")
onchange_location_id
method
Updates warehouse when location changes and propagates to child requests.Decorator: @api.onchange("location_id")Behavior:
  • If location has a different warehouse, updates warehouse_id
  • Calls change_childs() to update child requests
onchange_warehouse_id
method
Updates location and company when warehouse changes.Decorator: @api.onchange("warehouse_id")Behavior:
  • Updates location_id to warehouse’s lot_stock_id
  • Updates company_id if warehouse company differs
  • Calls change_childs() to update child requests
onchange_procurement_group_id
method
Propagates procurement_group_id change to child requests.Decorator: @api.onchange("procurement_group_id")
onchange_company_id
method
Updates warehouse when company changes.Decorator: @api.onchange("company_id")Behavior:
  • Searches for a warehouse in the new company
  • Calls change_childs() to update child requests
change_childs
method
Propagates order-level changes to all child stock requests.Updated fields:
  • warehouse_id
  • location_id
  • company_id
  • picking_policy
  • expected_date
  • requested_by
  • procurement_group_id
Skipped when context contains no_change_childs=True
stock_request/models/stock_request_order.py:289
def change_childs(self):
    if not self._context.get("no_change_childs", False):
        for line in self.stock_request_ids:
            line.warehouse_id = self.warehouse_id
            line.location_id = self.location_id
            line.company_id = self.company_id
            line.picking_policy = self.picking_policy
            line.expected_date = self.expected_date
            line.requested_by = self.requested_by
            line.procurement_group_id = self.procurement_group_id

Utility Methods

get_parents
method
Returns all parent locations up to the root for the order’s location.Returns: stock.location recordset
stock_request/models/stock_request_order.py:183
def get_parents(self):
    location = self.location_id
    result = location
    while location.location_id:
        location = location.location_id
        result |= location
    return result
default_get
method
Provides default values for warehouse and location based on company.Decorator: @api.modelBehavior:
  • Searches for a warehouse in the specified company
  • Sets location_id to warehouse’s lot_stock_id
stock_request/models/stock_request_order.py:14
@api.model
def default_get(self, fields):
    res = super().default_get(fields)
    warehouse = None
    if "warehouse_id" not in res and res.get("company_id"):
        warehouse = self.env["stock.warehouse"].search(
            [("company_id", "=", res["company_id"])], limit=1
        )
    if warehouse:
        res["warehouse_id"] = warehouse.id
        res["location_id"] = warehouse.lot_stock_id.id
    return res

Constraints

SQL Constraints

name_uniq: Stock Request name must be unique per company.

Python Constraints

_check_warehouse_company
Validates that warehouse company matches order company.Constraint: warehouse_id, company_idRaises: ValidationError
stock_request/models/stock_request_order.py:362
@api.constrains("warehouse_id", "company_id")
def _check_warehouse_company(self):
    if any(
        request.warehouse_id.company_id != request.company_id for request in self
    ):
        raise ValidationError(
            _(
                "The company of the stock request must match with "
                "that of the warehouse."
            )
        )
_check_location_company
Validates that location company (if set) matches order company.Constraint: location_id, company_idRaises: ValidationError
stock_request/models/stock_request_order.py:374
@api.constrains("location_id", "company_id")
def _check_location_company(self):
    if any(
        request.location_id.company_id
        and request.location_id.company_id != request.company_id
        for request in self
    ):
        raise ValidationError(
            _(
                "The company of the stock request must match with "
                "that of the location."
            )
        )

Lifecycle

Create

create
method
Creates new stock request order records with auto-generated names.Decorator: @api.model_create_multiBehavior:
  • Auto-generates name from sequence stock.request.order if not provided or equals ”/“
stock_request/models/stock_request_order.py:345
@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.order"
            )
        vals_list_upd.append(upd_vals)
    return super().create(vals_list_upd)
Deletes stock request order records.Raises: UserError if any record is not in draft state
stock_request/models/stock_request_order.py:357
def unlink(self):
    if self.filtered(lambda r: r.state != "draft"):
        raise UserError(_("Only orders on draft state can be unlinked"))
    return super().unlink()

Special Methods

Create From Product Multiselect

_create_from_product_multiselect
method
Creates a stock request order from a multi-selection of products.Decorator: @api.modelParameters:
  • products - Recordset of product.product or product.template
Returns: Action dictionary to open the created orderRaises: ValidationError if called outside product contextBehavior:
  • Creates one stock request per product with quantity 1.0
  • Uses default expected date
  • Opens the created order in form view
stock_request/models/stock_request_order.py:388
@api.model
def _create_from_product_multiselect(self, products):
    if not products:
        return False
    if products._name not in ("product.product", "product.template"):
        raise ValidationError(
            _("This action only works in the context of products")
        )
    if products._name == "product.template":
        products = self.env["product.product"].search(
            [("product_tmpl_id", "in", products.ids)]
        )
    expected = self.default_get(["expected_date"])["expected_date"]
    order = self.env["stock.request.order"].create(
        dict(
            expected_date=expected,
            stock_request_ids=[
                (
                    0,
                    0,
                    dict(
                        product_id=product.id,
                        product_uom_id=product.uom_id.id,
                        product_uom_qty=1.0,
                        expected_date=expected,
                    ),
                )
                for product in products
            ],
        )
    )
    # Returns action to view created order

Usage Example

# Create a stock request order with multiple products
order = env['stock.request.order'].create({
    'warehouse_id': warehouse.id,
    'location_id': location.id,
    'requested_by': env.user.id,
    'expected_date': fields.Datetime.now(),
    'picking_policy': 'direct',
    'stock_request_ids': [
        (0, 0, {
            'product_id': product1.id,
            'product_uom_qty': 10.0,
            'product_uom_id': product1.uom_id.id,
        }),
        (0, 0, {
            'product_id': product2.id,
            'product_uom_qty': 5.0,
            'product_uom_id': product2.uom_id.id,
        }),
    ],
})

# Confirm all requests in the order
order.action_confirm()

# Check order state (computed from child requests)
print(f"Order state: {order.state}")
print(f"Number of requests: {order.stock_request_count}")
print(f"Number of pickings: {order.picking_count}")

# Set a common route for all requests
order.route_id = route

# View all stock requests
action = order.action_view_stock_requests()

See Also

Build docs developers (and LLMs) love