[ADD] stock_inventory_adjustment_security: limit who can adjust stock
This commit is contained in:
parent
3f5c5cc038
commit
a080586ef4
8 changed files with 523 additions and 0 deletions
91
stock_inventory_adjustment_security/README.md
Normal file
91
stock_inventory_adjustment_security/README.md
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
# Stock Inventory Adjustment Security
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This module adds granular security controls for inventory adjustments in Odoo. It allows you to separate the ability to **count inventory** from the ability to **apply adjustments**.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **New Security Group**: "Inventory: Manual Adjustments"
|
||||||
|
- **Separation of Duties**:
|
||||||
|
- Regular inventory users can view stock and enter counted quantities
|
||||||
|
- Only privileged users can apply the adjustments to modify actual stock levels
|
||||||
|
- **No Impact on Automatic Operations**: Stock moves from pickings, manufacturing orders, and other automatic operations work normally
|
||||||
|
|
||||||
|
## Use Case
|
||||||
|
|
||||||
|
Perfect for organizations that want to:
|
||||||
|
- Allow warehouse staff to participate in inventory counts
|
||||||
|
- Restrict who can actually modify stock levels
|
||||||
|
- Maintain audit trails by limiting adjustment privileges
|
||||||
|
- Implement proper inventory control procedures
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
|
||||||
|
The module overrides the `write()` method on `stock.quant` to check:
|
||||||
|
1. If the operation is in "inventory mode" (manual adjustment context)
|
||||||
|
2. If the `quantity` field is being modified
|
||||||
|
3. If the user has the required security group
|
||||||
|
|
||||||
|
The check uses Odoo's built-in `_is_inventory_mode()` method which returns `True` only when:
|
||||||
|
- The `inventory_mode` context flag is set (manual adjustments)
|
||||||
|
- The user has the `stock.group_stock_user` group
|
||||||
|
|
||||||
|
### User Experience
|
||||||
|
|
||||||
|
**Regular Inventory User:**
|
||||||
|
- Can navigate to Inventory > Inventory Adjustments
|
||||||
|
- Can view current stock quantities
|
||||||
|
- Can enter counted quantities in the `inventory_quantity` field
|
||||||
|
- **Cannot** click "Apply" to commit the changes
|
||||||
|
- Receives clear error message when attempting to apply
|
||||||
|
|
||||||
|
**Privileged User (with Manual Adjustments group):**
|
||||||
|
- Can do everything a regular user can do
|
||||||
|
- **Can** click "Apply" to commit inventory adjustments
|
||||||
|
- Can modify actual stock quantities
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Install the module
|
||||||
|
2. Go to Settings > Users & Companies > Users
|
||||||
|
3. Edit a user who should be able to apply inventory adjustments
|
||||||
|
4. Add them to the "Inventory: Manual Adjustments" group
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The module includes comprehensive test coverage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
odoo-bin -d your_database -i stock_inventory_adjustment_security --test-enable --stop-after-init
|
||||||
|
|
||||||
|
# Run specific test class
|
||||||
|
odoo-bin -d your_database --test-tags stock_inventory_adjustment_security
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Cases
|
||||||
|
|
||||||
|
- ✅ Regular users can set inventory_quantity (count)
|
||||||
|
- ✅ Regular users cannot apply adjustments
|
||||||
|
- ✅ Privileged users can apply adjustments
|
||||||
|
- ✅ Automatic operations (pickings, etc.) are not affected
|
||||||
|
- ✅ Inventory mode detection works correctly
|
||||||
|
- ✅ Multiple quant adjustments work correctly
|
||||||
|
- ✅ Non-inventory mode writes are not restricted
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
- Odoo 18.0
|
||||||
|
- Depends on: `stock`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
LGPL-3
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Bemade Inc.
|
||||||
|
https://bemade.org
|
||||||
1
stock_inventory_adjustment_security/__init__.py
Normal file
1
stock_inventory_adjustment_security/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
30
stock_inventory_adjustment_security/__manifest__.py
Normal file
30
stock_inventory_adjustment_security/__manifest__.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "Stock Inventory Adjustment Security",
|
||||||
|
"author": "Bemade Inc.",
|
||||||
|
"version": "18.0.1.0.0",
|
||||||
|
"category": "Inventory/Inventory",
|
||||||
|
"summary": "Restrict manual inventory adjustments to privileged users",
|
||||||
|
"description": """
|
||||||
|
Stock Inventory Adjustment Security
|
||||||
|
====================================
|
||||||
|
|
||||||
|
This module adds an additional security layer for inventory adjustments:
|
||||||
|
|
||||||
|
* Creates a new security group "Inventory: Manual Adjustments"
|
||||||
|
* Regular inventory users can view and count inventory (set inventory_quantity)
|
||||||
|
* Only users in the privileged group can apply adjustments (modify actual quantity)
|
||||||
|
* Automatic stock operations (pickings, manufacturing, etc.) are not affected
|
||||||
|
|
||||||
|
This allows inventory staff to participate in counts while restricting who can
|
||||||
|
actually apply the adjustments and modify stock levels.
|
||||||
|
""",
|
||||||
|
"website": "https://www.bemade.org",
|
||||||
|
"depends": ["stock"],
|
||||||
|
"data": [
|
||||||
|
"security/security.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
"auto_install": False,
|
||||||
|
"license": "LGPL-3",
|
||||||
|
"application": False,
|
||||||
|
}
|
||||||
1
stock_inventory_adjustment_security/models/__init__.py
Normal file
1
stock_inventory_adjustment_security/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import stock_quant
|
||||||
61
stock_inventory_adjustment_security/models/stock_quant.py
Normal file
61
stock_inventory_adjustment_security/models/stock_quant.py
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import models, _, api
|
||||||
|
from odoo.exceptions import AccessError
|
||||||
|
|
||||||
|
|
||||||
|
class StockQuant(models.Model):
|
||||||
|
_inherit = "stock.quant"
|
||||||
|
|
||||||
|
def _check_inventory_adjustment_permission(self):
|
||||||
|
"""
|
||||||
|
Check if the current user has permission to make inventory adjustments.
|
||||||
|
|
||||||
|
Raises AccessError if the user lacks the required group.
|
||||||
|
"""
|
||||||
|
if not self.env.user.has_group(
|
||||||
|
"stock_inventory_adjustment_security.group_inventory_manual_adjustments"
|
||||||
|
):
|
||||||
|
raise AccessError(
|
||||||
|
_(
|
||||||
|
"You don't have permission to apply inventory adjustments. "
|
||||||
|
"You can count inventory, but only authorized users can apply the changes. "
|
||||||
|
"Please contact your inventory manager."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_apply_inventory(self):
|
||||||
|
"""
|
||||||
|
Override action_apply_inventory to restrict who can apply adjustments.
|
||||||
|
|
||||||
|
This is the main entry point for applying manual inventory adjustments.
|
||||||
|
Regular users can count, but only privileged users can apply.
|
||||||
|
"""
|
||||||
|
self._check_inventory_adjustment_permission()
|
||||||
|
return super().action_apply_inventory()
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
"""
|
||||||
|
Override create to restrict quant creation in inventory mode.
|
||||||
|
|
||||||
|
Regular users should not be able to create new quants in inventory mode
|
||||||
|
as this is another way to manipulate inventory levels.
|
||||||
|
"""
|
||||||
|
if self._is_inventory_mode():
|
||||||
|
self._check_inventory_adjustment_permission()
|
||||||
|
|
||||||
|
return super().create(vals_list)
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
"""
|
||||||
|
Override write to restrict quantity changes in inventory mode.
|
||||||
|
|
||||||
|
This catches direct quantity modifications during inventory adjustments.
|
||||||
|
Automatic operations (pickings, manufacturing, etc.) don't set
|
||||||
|
inventory_mode context, so they are unaffected.
|
||||||
|
"""
|
||||||
|
if self._is_inventory_mode() and "quantity" in vals:
|
||||||
|
self._check_inventory_adjustment_permission()
|
||||||
|
|
||||||
|
return super().write(vals)
|
||||||
12
stock_inventory_adjustment_security/security/security.xml
Normal file
12
stock_inventory_adjustment_security/security/security.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="group_inventory_manual_adjustments" model="res.groups">
|
||||||
|
<field name="name">Manual Adjustments</field>
|
||||||
|
<field name="category_id" ref="base.module_category_inventory_inventory"/>
|
||||||
|
<field name="implied_ids" eval="[(4, ref('stock.group_stock_user'))]"/>
|
||||||
|
<field name="comment">
|
||||||
|
Users in this group can apply manual inventory adjustments.
|
||||||
|
Regular inventory users can count inventory but cannot apply the adjustments.
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
1
stock_inventory_adjustment_security/tests/__init__.py
Normal file
1
stock_inventory_adjustment_security/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import test_inventory_adjustment_security
|
||||||
|
|
@ -0,0 +1,326 @@
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
from odoo.exceptions import AccessError
|
||||||
|
|
||||||
|
|
||||||
|
class TestInventoryAdjustmentSecurity(TransactionCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
# Create test product
|
||||||
|
cls.product = cls.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Product",
|
||||||
|
"type": "consu",
|
||||||
|
"is_storable": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get stock location
|
||||||
|
cls.stock_location = cls.env.ref("stock.warehouse0").lot_stock_id
|
||||||
|
|
||||||
|
# Create initial quant with some quantity
|
||||||
|
cls.quant = (
|
||||||
|
cls.env["stock.quant"]
|
||||||
|
.sudo()
|
||||||
|
.create(
|
||||||
|
{
|
||||||
|
"product_id": cls.product.id,
|
||||||
|
"location_id": cls.stock_location.id,
|
||||||
|
"quantity": 100.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create test users
|
||||||
|
# Regular inventory user (can count but not apply)
|
||||||
|
cls.inventory_user = cls.env["res.users"].create(
|
||||||
|
{
|
||||||
|
"name": "Inventory Counter",
|
||||||
|
"login": "inventory_counter",
|
||||||
|
"email": "counter@test.com",
|
||||||
|
"groups_id": [(6, 0, [cls.env.ref("stock.group_stock_user").id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Privileged user (can count and apply)
|
||||||
|
cls.privileged_user = cls.env["res.users"].create(
|
||||||
|
{
|
||||||
|
"name": "Inventory Manager",
|
||||||
|
"login": "inventory_manager",
|
||||||
|
"email": "manager@test.com",
|
||||||
|
"groups_id": [
|
||||||
|
(
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
[
|
||||||
|
cls.env.ref("stock.group_stock_user").id,
|
||||||
|
cls.env.ref(
|
||||||
|
"stock_inventory_adjustment_security.group_inventory_manual_adjustments"
|
||||||
|
).id,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_regular_user_can_set_inventory_quantity(self):
|
||||||
|
"""Test that regular users can set inventory_quantity (count)"""
|
||||||
|
quant = self.quant.with_user(self.inventory_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should be able to set inventory_quantity
|
||||||
|
quant.write({"inventory_quantity": 90.0})
|
||||||
|
self.assertEqual(quant.inventory_quantity, 90.0)
|
||||||
|
self.assertEqual(quant.inventory_diff_quantity, -10.0)
|
||||||
|
|
||||||
|
def test_regular_user_cannot_apply_adjustment(self):
|
||||||
|
"""Test that regular users cannot apply adjustments (modify quantity)"""
|
||||||
|
quant = self.quant.with_user(self.inventory_user).with_context(inventory_mode=True)
|
||||||
|
|
||||||
|
# Set inventory quantity first
|
||||||
|
quant.write({"inventory_quantity": 90.0})
|
||||||
|
|
||||||
|
# Trying to apply (which writes to quantity field) should fail
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
quant.write({"quantity": 90.0})
|
||||||
|
|
||||||
|
def test_regular_user_cannot_create_quants_in_inventory_mode(self):
|
||||||
|
"""Test that regular users cannot create new quants in inventory mode"""
|
||||||
|
# Create a new product
|
||||||
|
new_product = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "New Product",
|
||||||
|
"type": "consu",
|
||||||
|
"is_storable": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Trying to create a quant in inventory mode should fail
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.env["stock.quant"].with_user(self.inventory_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
).create(
|
||||||
|
{
|
||||||
|
"product_id": new_product.id,
|
||||||
|
"location_id": self.stock_location.id,
|
||||||
|
"quantity": 50.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_regular_user_cannot_call_apply_inventory(self):
|
||||||
|
"""Test that regular users cannot call action_apply_inventory"""
|
||||||
|
quant = self.quant.with_user(self.inventory_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set inventory quantity
|
||||||
|
quant.write({"inventory_quantity": 90.0})
|
||||||
|
|
||||||
|
# Trying to apply inventory should fail
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
quant.action_apply_inventory()
|
||||||
|
|
||||||
|
def test_privileged_user_can_apply_adjustment(self):
|
||||||
|
"""Test that privileged users can apply adjustments"""
|
||||||
|
quant = self.quant.with_user(self.privileged_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set inventory quantity
|
||||||
|
quant.write({"inventory_quantity": 90.0})
|
||||||
|
|
||||||
|
# Should be able to apply the adjustment
|
||||||
|
quant.action_apply_inventory()
|
||||||
|
|
||||||
|
# Quantity should be updated
|
||||||
|
self.assertEqual(quant.quantity, 90.0)
|
||||||
|
self.assertEqual(quant.inventory_quantity_set, False)
|
||||||
|
|
||||||
|
def test_privileged_user_can_create_quants_in_inventory_mode(self):
|
||||||
|
"""Test that privileged users can create new quants in inventory mode"""
|
||||||
|
# Create a new product
|
||||||
|
new_product = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Privileged Product",
|
||||||
|
"type": "consu",
|
||||||
|
"is_storable": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Privileged user should be able to create a quant in inventory mode
|
||||||
|
quant = self.env["stock.quant"].with_user(self.privileged_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
).create(
|
||||||
|
{
|
||||||
|
"product_id": new_product.id,
|
||||||
|
"location_id": self.stock_location.id,
|
||||||
|
"quantity": 50.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(quant.quantity, 50.0)
|
||||||
|
self.assertEqual(quant.product_id, new_product)
|
||||||
|
|
||||||
|
def test_automatic_operations_not_affected(self):
|
||||||
|
"""Test that automatic stock operations work normally"""
|
||||||
|
# Create a stock move (simulating a picking or manufacturing operation)
|
||||||
|
# These operations don't set inventory_mode context
|
||||||
|
move = self.env["stock.move"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Move",
|
||||||
|
"product_id": self.product.id,
|
||||||
|
"product_uom_qty": 10.0,
|
||||||
|
"product_uom": self.product.uom_id.id,
|
||||||
|
"location_id": self.env.ref("stock.stock_location_suppliers").id,
|
||||||
|
"location_dest_id": self.stock_location.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
move._action_confirm()
|
||||||
|
move._action_assign()
|
||||||
|
# Set quantity on the move itself
|
||||||
|
move.quantity = 10.0
|
||||||
|
move.picked = True
|
||||||
|
move._action_done()
|
||||||
|
|
||||||
|
# Quant quantity should be updated automatically
|
||||||
|
# Find the quant for this product in stock location
|
||||||
|
quant = self.env["stock.quant"].search(
|
||||||
|
[
|
||||||
|
("product_id", "=", self.product.id),
|
||||||
|
("location_id", "=", self.stock_location.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(quant.quantity, 110.0)
|
||||||
|
|
||||||
|
def test_regular_user_can_view_quantities(self):
|
||||||
|
"""Test that regular users can view all quantity fields"""
|
||||||
|
quant = self.quant.with_user(self.inventory_user)
|
||||||
|
|
||||||
|
# Should be able to read all fields
|
||||||
|
self.assertEqual(quant.quantity, 100.0)
|
||||||
|
self.assertEqual(quant.inventory_quantity, 0.0)
|
||||||
|
|
||||||
|
# Should be able to read in inventory mode too
|
||||||
|
quant_inv_mode = quant.with_context(inventory_mode=True)
|
||||||
|
self.assertEqual(quant_inv_mode.quantity, 100.0)
|
||||||
|
|
||||||
|
def test_non_inventory_mode_writes_allowed(self):
|
||||||
|
"""Test that quantity writes outside inventory mode are not restricted"""
|
||||||
|
# Even regular users can write quantity when not in inventory_mode
|
||||||
|
# (though they typically wouldn't have access to do this via UI)
|
||||||
|
quant = self.quant.sudo()
|
||||||
|
|
||||||
|
# Without inventory_mode context, write should work
|
||||||
|
quant.write({"quantity": 95.0})
|
||||||
|
|
||||||
|
self.assertEqual(quant.quantity, 95.0)
|
||||||
|
|
||||||
|
def test_inventory_mode_detection(self):
|
||||||
|
"""Test that _is_inventory_mode() works correctly"""
|
||||||
|
quant = self.quant.with_user(self.inventory_user)
|
||||||
|
|
||||||
|
# Without inventory_mode context
|
||||||
|
self.assertFalse(quant._is_inventory_mode())
|
||||||
|
|
||||||
|
# With inventory_mode context
|
||||||
|
quant_inv = quant.with_context(inventory_mode=True)
|
||||||
|
self.assertTrue(quant_inv._is_inventory_mode())
|
||||||
|
|
||||||
|
# User without stock_user group shouldn't trigger inventory mode
|
||||||
|
basic_user = self.env["res.users"].create(
|
||||||
|
{
|
||||||
|
"name": "Basic User",
|
||||||
|
"login": "basic_user",
|
||||||
|
"email": "basic@test.com",
|
||||||
|
"groups_id": [(6, 0, [self.env.ref("base.group_user").id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
quant_basic = self.quant.with_user(basic_user).with_context(inventory_mode=True)
|
||||||
|
self.assertFalse(quant_basic._is_inventory_mode())
|
||||||
|
|
||||||
|
def test_multiple_quants_adjustment(self):
|
||||||
|
"""Test applying adjustments to multiple quants at once"""
|
||||||
|
# Create another product to avoid quant merging
|
||||||
|
product2 = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Product 2",
|
||||||
|
"type": "consu",
|
||||||
|
"is_storable": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create another quant with different product
|
||||||
|
quant2 = (
|
||||||
|
self.env["stock.quant"]
|
||||||
|
.sudo()
|
||||||
|
.create(
|
||||||
|
{
|
||||||
|
"product_id": product2.id,
|
||||||
|
"location_id": self.stock_location.id,
|
||||||
|
"quantity": 50.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set inventory quantities on both quants
|
||||||
|
self.quant.with_user(self.privileged_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
).write({"inventory_quantity": 80.0})
|
||||||
|
quant2.with_user(self.privileged_user).with_context(inventory_mode=True).write(
|
||||||
|
{"inventory_quantity": 40.0}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apply all at once
|
||||||
|
quants = (
|
||||||
|
(self.quant | quant2)
|
||||||
|
.with_user(self.privileged_user)
|
||||||
|
.with_context(inventory_mode=True)
|
||||||
|
)
|
||||||
|
quants.action_apply_inventory()
|
||||||
|
|
||||||
|
self.assertEqual(self.quant.quantity, 80.0)
|
||||||
|
self.assertEqual(quant2.quantity, 40.0)
|
||||||
|
|
||||||
|
def test_regular_user_multiple_quants_blocked(self):
|
||||||
|
"""Test that regular users cannot apply multiple quants"""
|
||||||
|
# Create another product to avoid quant merging
|
||||||
|
product2 = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Product 3",
|
||||||
|
"type": "consu",
|
||||||
|
"is_storable": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
quant2 = (
|
||||||
|
self.env["stock.quant"]
|
||||||
|
.sudo()
|
||||||
|
.create(
|
||||||
|
{
|
||||||
|
"product_id": product2.id,
|
||||||
|
"location_id": self.stock_location.id,
|
||||||
|
"quantity": 50.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set inventory quantities
|
||||||
|
self.quant.with_user(self.inventory_user).with_context(
|
||||||
|
inventory_mode=True
|
||||||
|
).write({"inventory_quantity": 80.0})
|
||||||
|
quant2.with_user(self.inventory_user).with_context(inventory_mode=True).write(
|
||||||
|
{"inventory_quantity": 40.0}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Trying to apply should fail
|
||||||
|
quants = (
|
||||||
|
(self.quant | quant2)
|
||||||
|
.with_user(self.inventory_user)
|
||||||
|
.with_context(inventory_mode=True)
|
||||||
|
)
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
quants.action_apply_inventory()
|
||||||
Loading…
Reference in a new issue