68 lines
3.0 KiB
Python
Executable File
68 lines
3.0 KiB
Python
Executable File
# Copyright 2015-2017 Akretion (http://www.akretion.com)
|
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
|
|
from odoo import _, api, models
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tools import config, float_compare
|
|
|
|
|
|
class StockQuant(models.Model):
|
|
_inherit = "stock.quant"
|
|
|
|
@api.constrains("product_id", "quantity")
|
|
def check_negative_qty(self):
|
|
# To provide an option to skip the check when necessary.
|
|
# e.g. mrp_subcontracting_skip_no_negative - passes the context
|
|
# for subcontracting receipts.
|
|
# Also allows negative stock for subcontracting locations as they
|
|
# are expected to have negative stock in certain Odoo 18 workflows.
|
|
if self.env.context.get("skip_negative_qty_check"):
|
|
return
|
|
p = self.env["decimal.precision"].precision_get("Product Unit of Measure")
|
|
check_negative_qty = (
|
|
config["test_enable"] and self.env.context.get("test_stock_no_negative")
|
|
) or not config["test_enable"]
|
|
if not check_negative_qty:
|
|
return
|
|
|
|
for quant in self:
|
|
disallowed_by_product = (
|
|
not quant.product_id.allow_negative_stock
|
|
and not quant.product_id.categ_id.allow_negative_stock
|
|
)
|
|
disallowed_by_location = not quant.location_id.allow_negative_stock
|
|
|
|
# Allow negative stock for subcontracting locations
|
|
# Check if mrp_subcontracting is installed and location has is_subcontracting_location field
|
|
is_subcontracting_location = False
|
|
if hasattr(quant.location_id, 'is_subcontracting_location'):
|
|
is_subcontracting_location = quant.location_id.is_subcontracting_location
|
|
|
|
if (
|
|
float_compare(quant.quantity, 0, precision_digits=p) == -1
|
|
and quant.product_id.is_storable
|
|
and quant.location_id.usage in ["internal", "transit"]
|
|
and disallowed_by_product
|
|
and disallowed_by_location
|
|
and not is_subcontracting_location
|
|
):
|
|
msg_add = ""
|
|
if quant.lot_id:
|
|
msg_add = _(" lot %(name)s", name=quant.lot_id.display_name)
|
|
raise ValidationError(
|
|
_(
|
|
"You cannot validate this stock operation because the "
|
|
"stock level of the product '{name}'{name_lot} would "
|
|
"become negative "
|
|
"({q_quantity}) on the stock location '{complete_name}' "
|
|
"and negative stock is "
|
|
"not allowed for this product and/or location."
|
|
).format(
|
|
name=quant.product_id.display_name,
|
|
name_lot=msg_add,
|
|
q_quantity=quant.quantity,
|
|
complete_name=quant.location_id.complete_name,
|
|
)
|
|
)
|