mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
258 lines
9.2 KiB
Python
258 lines
9.2 KiB
Python
# Copyright 2017-2020 ForgeFlow, S.L.
|
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class StockRequest(models.AbstractModel):
|
|
_name = "stock.request.abstract"
|
|
_description = "Stock Request Template"
|
|
_inherit = ["mail.thread", "mail.activity.mixin"]
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
res = super(StockRequest, self).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
|
|
|
|
@api.depends(
|
|
"product_id",
|
|
"product_uom_id",
|
|
"product_uom_qty",
|
|
"product_id.product_tmpl_id.uom_id",
|
|
)
|
|
def _compute_product_qty(self):
|
|
for rec in self:
|
|
rec.product_qty = rec.product_uom_id._compute_quantity(
|
|
rec.product_uom_qty, rec.product_id.product_tmpl_id.uom_id
|
|
)
|
|
|
|
name = fields.Char("Name", copy=False, required=True, readonly=True, default="/")
|
|
warehouse_id = fields.Many2one(
|
|
"stock.warehouse", "Warehouse", ondelete="cascade", required=True
|
|
)
|
|
location_id = fields.Many2one(
|
|
"stock.location",
|
|
"Location",
|
|
domain=[("usage", "in", ["internal", "transit"])],
|
|
ondelete="cascade",
|
|
required=True,
|
|
)
|
|
product_id = fields.Many2one(
|
|
"product.product",
|
|
"Product",
|
|
domain=[("type", "in", ["product", "consu"])],
|
|
ondelete="cascade",
|
|
required=True,
|
|
)
|
|
allow_virtual_location = fields.Boolean(
|
|
related="company_id.stock_request_allow_virtual_loc", readonly=True
|
|
)
|
|
product_uom_id = fields.Many2one(
|
|
"uom.uom",
|
|
"Product Unit of Measure",
|
|
required=True,
|
|
default=lambda self: self._context.get("product_uom_id", False),
|
|
)
|
|
product_uom_qty = fields.Float(
|
|
"Quantity",
|
|
digits="Product Unit of Measure",
|
|
required=True,
|
|
help="Quantity, specified in the unit of measure indicated in the request.",
|
|
)
|
|
product_qty = fields.Float(
|
|
"Real Quantity",
|
|
compute="_compute_product_qty",
|
|
store=True,
|
|
copy=False,
|
|
digits="Product Unit of Measure",
|
|
help="Quantity in the default UoM of the product",
|
|
)
|
|
procurement_group_id = fields.Many2one(
|
|
"procurement.group",
|
|
"Procurement Group",
|
|
help="Moves created through this stock request will be put in this "
|
|
"procurement group. If none is given, the moves generated by "
|
|
"procurement rules will be grouped into one big picking.",
|
|
)
|
|
company_id = fields.Many2one(
|
|
"res.company", "Company", required=True, default=lambda self: self.env.company
|
|
)
|
|
route_id = fields.Many2one(
|
|
"stock.location.route",
|
|
string="Route",
|
|
domain="[('id', 'in', route_ids)]",
|
|
ondelete="restrict",
|
|
)
|
|
|
|
route_ids = fields.Many2many(
|
|
"stock.location.route",
|
|
string="Routes",
|
|
compute="_compute_route_ids",
|
|
readonly=True,
|
|
)
|
|
|
|
_sql_constraints = [
|
|
("name_uniq", "unique(name, company_id)", "Name must be unique")
|
|
]
|
|
|
|
@api.depends("product_id", "warehouse_id", "location_id")
|
|
def _compute_route_ids(self):
|
|
route_obj = self.env["stock.location.route"]
|
|
for wh in self.mapped("warehouse_id"):
|
|
wh_routes = route_obj.search([("warehouse_ids", "=", wh.id)])
|
|
for record in self.filtered(lambda r: r.warehouse_id == wh):
|
|
routes = route_obj
|
|
if record.product_id:
|
|
routes += record.product_id.mapped(
|
|
"route_ids"
|
|
) | record.product_id.mapped("categ_id").mapped("total_route_ids")
|
|
if record.warehouse_id:
|
|
routes |= wh_routes
|
|
parents = record.get_parents().ids
|
|
record.route_ids = routes.filtered(
|
|
lambda r: any(p.location_id.id in parents for p in r.rule_ids)
|
|
)
|
|
|
|
def get_parents(self):
|
|
location = self.location_id
|
|
result = location
|
|
while location.location_id:
|
|
location = location.location_id
|
|
result |= location
|
|
return result
|
|
|
|
@api.constrains(
|
|
"company_id", "product_id", "warehouse_id", "location_id", "route_id"
|
|
)
|
|
def _check_company_constrains(self):
|
|
""" Check if the related models have the same company """
|
|
for rec in self:
|
|
if (
|
|
rec.product_id.company_id
|
|
and rec.product_id.company_id != rec.company_id
|
|
):
|
|
raise ValidationError(
|
|
_(
|
|
"You have entered a product that is assigned "
|
|
"to another company."
|
|
)
|
|
)
|
|
if (
|
|
rec.location_id.company_id
|
|
and rec.location_id.company_id != rec.company_id
|
|
):
|
|
raise ValidationError(
|
|
_(
|
|
"You have entered a location that is "
|
|
"assigned to another company."
|
|
)
|
|
)
|
|
if rec.warehouse_id.company_id != rec.company_id:
|
|
raise ValidationError(
|
|
_(
|
|
"You have entered a warehouse that is "
|
|
"assigned to another company."
|
|
)
|
|
)
|
|
if (
|
|
rec.route_id
|
|
and rec.route_id.company_id
|
|
and rec.route_id.company_id != rec.company_id
|
|
):
|
|
raise ValidationError(
|
|
_(
|
|
"You have entered a route that is "
|
|
"assigned to another company."
|
|
)
|
|
)
|
|
|
|
@api.constrains("product_id")
|
|
def _check_product_uom(self):
|
|
""" Check if the UoM has the same category as the
|
|
product standard UoM """
|
|
if any(
|
|
request.product_id.uom_id.category_id != request.product_uom_id.category_id
|
|
for request in self
|
|
):
|
|
raise ValidationError(
|
|
_(
|
|
"You have to select a product unit of measure in the "
|
|
"same category than the default unit "
|
|
"of measure of the product"
|
|
)
|
|
)
|
|
|
|
@api.constrains("product_qty")
|
|
def _check_qty(self):
|
|
for rec in self:
|
|
if rec.product_qty <= 0:
|
|
raise ValidationError(
|
|
_("Stock Request product quantity has to be strictly positive.")
|
|
)
|
|
|
|
@api.onchange("warehouse_id")
|
|
def onchange_warehouse_id(self):
|
|
""" Finds location id for changed warehouse. """
|
|
res = {"domain": {}}
|
|
if self._name == "stock.request" and self.order_id:
|
|
# When the stock request is created from an order the wh and
|
|
# location are taken from the order and we rely on it to change
|
|
# all request associated. Thus, no need to apply
|
|
# the onchange, as it could lead to inconsistencies.
|
|
return res
|
|
if self.warehouse_id:
|
|
loc_wh = self.location_id.get_warehouse()
|
|
if self.warehouse_id != loc_wh:
|
|
self.location_id = self.warehouse_id.lot_stock_id.id
|
|
if self.warehouse_id.company_id != self.company_id:
|
|
self.company_id = self.warehouse_id.company_id
|
|
return res
|
|
|
|
@api.onchange("location_id")
|
|
def onchange_location_id(self):
|
|
if self.location_id:
|
|
loc_wh = self.location_id.get_warehouse()
|
|
if loc_wh and self.warehouse_id != loc_wh:
|
|
self.warehouse_id = loc_wh
|
|
self.with_context(no_change_childs=True).onchange_warehouse_id()
|
|
|
|
@api.onchange("allow_virtual_location")
|
|
def onchange_allow_virtual_location(self):
|
|
if self.allow_virtual_location:
|
|
return {"domain": {"location_id": []}}
|
|
|
|
@api.onchange("company_id")
|
|
def onchange_company_id(self):
|
|
""" Sets a default warehouse when the company is changed and limits
|
|
the user selection of warehouses. """
|
|
if self.company_id and (
|
|
not self.warehouse_id or self.warehouse_id.company_id != self.company_id
|
|
):
|
|
self.warehouse_id = self.env["stock.warehouse"].search(
|
|
[("company_id", "=", self.company_id.id)], limit=1
|
|
)
|
|
self.onchange_warehouse_id()
|
|
|
|
return {"domain": {"warehouse_id": [("company_id", "=", self.company_id.id)]}}
|
|
|
|
@api.onchange("product_id")
|
|
def onchange_product_id(self):
|
|
res = {"domain": {}}
|
|
if self.product_id:
|
|
self.product_uom_id = self.product_id.uom_id.id
|
|
res["domain"]["product_uom_id"] = [
|
|
("category_id", "=", self.product_id.uom_id.category_id.id)
|
|
]
|
|
return res
|
|
res["domain"]["product_uom_id"] = []
|
|
return res
|