first commit

This commit is contained in:
Suherdy Yacob 2026-02-06 18:05:38 +07:00
commit df7545fb99
7 changed files with 146 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
# Python
__pycache__/
*.py[cod]
*$py.class
# Odoo
*.po
*.pot
# IDE
.vscode/
.idea/
# System
.DS_Store
Thumbs.db

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# MO Lock Consumed
This Odoo module customizes the behavior of Manufacturing Orders (MO) regarding ingredient consumption and validation.
## Features
1. **Lock Consumed Quantity**:
- Prevents the automatic update of a component's "Consumed" quantity when the MO's "Quantity to Produce" is changed, **IF**:
- The component already has a manually entered "Consumed" quantity (greater than 0).
- OR the component has already been marked as picked.
- If a component has 0 consumed quantity and is not picked, it will continue to scale automatically based on the BOM ratio (standard behavior).
2. **Safety Check for Negative Stock**:
- Hides the **"Produce"** and **"Produce All"** buttons if proceeding with the production would cause the potential stock of any component to drop below zero (negative stock).
- **Exception**: If a component's "Consumed" quantity is explicitly set to **0** (e.g., for custom productions where a BOM component is not used), it is skipped in this check, allowing production to proceed.
## Usage
- **Standard Flow**: Create an MO, confirm it. The behavior remains standard unless you manually intervene.
- **Custom Consumption**: If you manually set a component's consumed quantity (e.g., to 5 units), changing the global "Quantity to Produce" will **not** override your manual entry of 5 units.
- **Stock Validation**: If you try to produce a quantity that requires more components than you have in stock, the Produce buttons will disappear, preventing you from accidentally forcing negative stock. To fix this, either replenish stock or adjust the consumed quantity to 0 (if omitting the component).

1
__init__.py Normal file
View File

@ -0,0 +1 @@
from . import models

19
__manifest__.py Normal file
View File

@ -0,0 +1,19 @@
{
'name': 'MO Lock Consumed',
'version': '1.0',
'summary': 'Lock consumed quantity on MO when manually edited or picked, and hide produce buttons on negative stock.',
'description': """
Prevents automatic update of the 'Consumed' quantity on Manufacturing Orders when:
1. The component already has a consumed quantity.
2. The component transfer component is already picked.
Also hides 'Produce' and 'Produce All' buttons if the operation would result in negative potential stock for components.
""",
'category': 'Manufacturing',
'author': 'Antigravity',
'depends': ['mrp'],
'data': [],
'installable': True,
'application': False,
'license': 'LGPL-3',
}

2
models/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from . import stock_move
from . import mrp_production

73
models/mrp_production.py Normal file
View File

@ -0,0 +1,73 @@
from odoo import models, api
class MrpProduction(models.Model):
_inherit = 'mrp.production'
@api.depends('state', 'product_qty', 'qty_producing', 'move_raw_ids.quantity', 'move_raw_ids.product_uom_qty')
def _compute_show_produce(self):
"""
Override to hide produce buttons if proceeding would cause negative stock.
"""
# First compute standard visibility
super()._compute_show_produce()
for production in self:
# If standard logic says hidden, no need to check further
if not production.show_produce and not production.show_produce_all:
continue
# Determine which action we are validating
# If show_produce_all is True, we are producing the full remaining amount
# If show_produce (partial) is True, we are producing 'qty_producing'
# This logic needs to mirror what happens when the button is clicked.
# However, for visibility, we want to know if *current* configured production is possible.
# If "Produce All" is available, it means we plan to produce everything remaining.
# If "Produce" (partial) is available, we plan to produce `qty_producing`.
# Check if any component would go negative
potential_negative = False
# We need to check stock availability in the source location
location = production.location_src_id
for move in production.move_raw_ids:
if move.state in ('done', 'cancel'):
continue
# User request:
# "if the components qty consumed is 0 then still show produce and produce all button"
# "only hide the visibility if components qty consumed will make the stock negative"
# If nothing is currently set to be consumed (0), we assume it's safe to proceed
# (or the user intends to consume 0/different amount later).
# We only block if the *Explicitly Consumed* amount exceeds available stock.
if move.quantity == 0:
continue
# Check availability based on the ACTUAL 'Consumed' quantity
qty_to_consume = move.quantity
# Check availability
product_in_location = move.product_id.with_context(location=location.id)
available_qty = product_in_location.qty_available
if available_qty - qty_to_consume < 0:
potential_negative = True
break
if potential_negative:
production.show_produce = False
production.show_produce_all = False
# User request: "hide the produce if all components consumed is 0 (does not make sense right to produce without components)"
# This should only apply if we are in "Partial Produce" mode (qty_producing > 0), where consumption should have been calculated/entered.
# If qty_producing is 0 (Fresh MO), we usually show "Produce All", which will auto-calculate consumption, so we shouldn't hide it.
if production.qty_producing > 0:
# Check if ALL moves have 0 quantity
# We filter out cancelled moves, and maybe we should focus on raw materials.
relevant_moves = production.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel'))
if relevant_moves and all(m.quantity == 0 for m in relevant_moves):
production.show_produce = False
production.show_produce_all = False

14
models/stock_move.py Normal file
View File

@ -0,0 +1,14 @@
from odoo import models
class StockMove(models.Model):
_inherit = 'stock.move'
def _should_bypass_set_qty_producing(self):
"""
Prevent auto-update of consumed quantity if:
- The move already has a quantity set (partial consumption).
- The move is already picked.
"""
if self.quantity > 0 or self.picked:
return True
return super()._should_bypass_set_qty_producing()