Conversation
d56a75e to
f961ee9
Compare
9581476 to
fedbd8a
Compare
| continue | ||
| quantity_produced = sum(byproduct_move.move_line_ids.mapped("quantity")) | ||
|
|
||
| if not byproduct_move.product_id.sale_ok: |
There was a problem hiding this comment.
This has to be put at the beginning of the loop to avoid too much conditions evaluations.
There was a problem hiding this comment.
Moreover, I would have put this in a separate function like _get_byproduct_moves_to_add() or somethng like that.
0ce29bc to
7ed5507
Compare
|
Thanks for the review. |
- Add res_company model with byproduct_note_template field - Update res_config_settings to use compute/inverse methods for company-specific settings - Update mrp_production to use company-specific template with fallback to global config - Add proper fallback logic: company setting -> global config -> default value - Maintain backward compatibility with existing global configurations - Include _get_byproduct_moves_to_add() method for cleaner code structure - Apply proper float_compare() usage as per review - Update __init__.py to import new company model
- Change _logger.info() calls to _logger.debug() for internal processing messages - Maintain appropriate logging level for diagnostic information - Keep debug level for internal processing logs to reduce noise in production - Apply to all logging calls in the _add_byproducts_to_sale_order method
7ed5507 to
4d83fc5
Compare
alexey-pelykh
left a comment
There was a problem hiding this comment.
Review: REQUEST_CHANGES
Thank you for the contribution -- the business case is well-documented and the test coverage is solid. However, there are several correctness issues and OCA convention gaps that should be addressed before merge.
Critical Findings
| # | File | Severity | Finding |
|---|---|---|---|
| 1 | sale_order_line.py |
Bug | _action_launch_stock_rule override breaks mixed recordsets -- returns True for the entire batch when ANY line is a byproduct, silently skipping procurement for legitimate lines |
| 2 | sale_order_line.py |
Bug | create override may double-trigger procurement for non-byproduct lines added to confirmed SOs (Odoo already handles this) |
| 3 | mrp_production.py |
Dead code | Duplicate SO search at L42-45 (origin.startswith("S") branch does the exact same query as L38-40) |
| 4 | mrp_production.py |
Security | Note template uses Python str.format() with user-controlled template string from ir.config_parameter -- could expose object attributes via {0.__class__} style injection |
| 5 | sale_order_line.py |
OCA | Missing license header |
| 6 | res_config_settings.py |
Convention | Reviewer rousseldenis already flagged: prefer config_parameter= attribute or a proper company-level field with related= instead of manual compute/inverse |
Suggestions
| # | File | Finding |
|---|---|---|
| 7 | __manifest__.py |
Remove commented-out "demo" key |
| 8 | Tests | Excessive invalidate_all() + re-browse suggests misunderstanding of ORM cache -- consider simplifying |
| 9 | mrp_production.py |
bom_param argument in helper is accepted but never used -- remove or implement |
|
|
||
| # Call super only on lines that should be procured | ||
| if lines_not_to_procure: | ||
| return True |
There was a problem hiding this comment.
Bug (critical): This logic is inverted for mixed recordsets. If self contains 5 lines and 1 is a byproduct, this returns True immediately and skips procurement for the other 4 lines.
The correct pattern is to exclude byproduct lines and call super() on the remaining lines:
def _action_launch_stock_rule(self, previous_product_uom_qty=False):
lines_to_procure = self.filtered(lambda line: not line.is_mrp_byproduct_line)
if not lines_to_procure:
return True
return super(SaleOrderLine, lines_to_procure)._action_launch_stock_rule(
previous_product_uom_qty=previous_product_uom_qty
)| for line in lines: | ||
| if line.order_id.state == "sale" and not line.is_mrp_byproduct_line: | ||
| line._action_launch_stock_rule() | ||
| return lines |
There was a problem hiding this comment.
Bug: Overriding create to manually call _action_launch_stock_rule() for non-byproduct lines is risky. Odoo's standard sale.order.line already triggers procurement on line creation for confirmed SOs via its own mechanism. This override may cause double procurement (two MOs created for one SO line).
I believe the _action_launch_stock_rule override above (once fixed) is sufficient to prevent byproduct lines from triggering procurement. This entire create override should likely be removed.
If it IS needed, please add a test that demonstrates the scenario it solves -- currently no test covers the create-triggered procurement path specifically.
| if not sale_order and self.origin.startswith("S"): | ||
| sale_order = self.env["sale.order"].search( | ||
| [("name", "=", self.origin)], limit=1 | ||
| ) |
There was a problem hiding this comment.
Dead code: This if branch (self.origin.startswith("S")) executes the exact same search as lines 38-40 above:
sale_order = self.env["sale.order"].search(
[("name", "=", self.origin)], limit=1
)This duplicate search can never find anything the first one missed. Remove this branch entirely.
| if config_param: | ||
| note = config_param.format( | ||
| product_name=product_to_add.name, mo_name=self.name | ||
| ) |
There was a problem hiding this comment.
Security concern: Using str.format() with a user-controlled template string is a known Python injection vector. A malicious admin could craft a template like {product_name.__class__.__mro__} to leak internal Python object information.
Consider using safe_substitute from string.Template, or restrict the format to known placeholders via a simple .replace() approach:
note = config_param.replace("{product_name}", product_to_add.name).replace("{mo_name}", self.name)| @@ -0,0 +1,35 @@ | |||
| from odoo import fields, models | |||
There was a problem hiding this comment.
OCA convention: Missing license header. All Python files in OCA modules must have the copyright and license header:
# Copyright 2025 OBS Solutions BV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).| "license": "AGPL-3", | ||
| "depends": ["mrp", "sale_management", "sale_stock"], | ||
| "data": ["data/config_parameter_data.xml", "views/res_config_settings_views.xml"], | ||
| # "demo": ["demo/demo_data.xml"], |
There was a problem hiding this comment.
Cleanup: Remove the commented-out "demo" line. OCA guidelines discourage committed commented-out code.
|
|
||
| byproduct_note_template = fields.Char( | ||
| string="By-product Note Template", | ||
| help="Template for the note on sale order lines created for by-products. " |
There was a problem hiding this comment.
As rousseldenis already noted: since you now have a res.company field, the settings field should use related="company_id.byproduct_note_template" rather than manual _compute / _inverse methods. This is the standard OCA pattern for company-level settings and removes ~15 lines of code.
|
@bosd Could you improve your commit messages and squash some of them if needed. Thanks |
This module is designed for contract manufacturing scenarios where the
client provides raw materials or a base product for processing. Unlike
standard manufacturing where the output quantity of finished goods is
precisely known, this use case involves a process where the exact yield
of by-products is uncertain until after the manufacturing operation
is complete.
Scenario
A contract manufacturer processes materials supplied by a client. During
this process, in addition to any primary output, a variable quantity of
by-products is inevitably generated. The client is obligated to purchase
these by-products from the contract manufacturer.