Conversation
1b8fb0e to
8be9119
Compare
|
/ocabot migration sale_order_merge |
rousseldenis
left a comment
There was a problem hiding this comment.
LGTM even if I'm wondering if compute method for 'merge_with' could be improved
|
Hi @rousseldenis , maybe it can be improved, but well, try to remove only what doesn't work in the target version. |
|
Hi @rousseldenis @hildickethan , I'm going to make the changes you mentioned to me to adapt them to the guidelines. Thank you for your feedback. |
|
Hi @rousseldenis @hildickethan , I'm covering the tests, I'll do squash at the end. |
b5dd279 to
c7e047e
Compare
aaa1f42 to
3642728
Compare
* [ADD] Module to merge draft or confirmed sales orders * [ADD] Extensibility hooks [FIX] Add company to merge domain [FIX] Fix syntax error in case multiple pickings are merged * [FIX] Update the origin of the remaining pickings [UPD] README, courtesy of Jordi Ballester * [FIX] Syntax error
Currently translated at 50.0% (10 of 20 strings) Translation: sale-workflow-8.0/sale-workflow-8.0-sale_order_merge Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-8-0/sale-workflow-8-0-sale_order_merge/it/
Currently translated at 50.0% (10 of 20 strings) Translation: sale-workflow-8.0/sale-workflow-8.0-sale_order_merge Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-8-0/sale-workflow-8-0-sale_order_merge/it/
Currently translated at 50.0% (10 of 20 strings) Translation: sale-workflow-8.0/sale-workflow-8.0-sale_order_merge Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-8-0/sale-workflow-8-0-sale_order_merge/it/
3642728 to
541787e
Compare
@edescalona what happened to this? Was there some issue with migrating the fields? |
alexey-pelykh
left a comment
There was a problem hiding this comment.
Review: [17.0][MIG] sale_order_merge
Thanks for the migration effort -- this is a useful module. I reviewed the code for Odoo 17 API compatibility, OCA conventions, and correctness. CI is green on tests (OCB and Odoo) and pre-commit, which is good. A few items below.
Bug: wrong order name in merge chatter message
wizard_sale_order_merge.py, merge() method, line 162
The cancelled orders post a message referencing themselves rather than the target order:
order.message_post(body=_("Merged into %(order)s") % {"order": order.name})order.name here is the name of the order being cancelled. Should be self.order_id.name (the merge target).
Additionally at line 165, self.to_merge.mapped("name") includes the target itself. Consider using orders.mapped("name") (where orders = self.to_merge - self.order_id, already computed above).
Deprecated key: view_type in action dicts
view_type was removed from ir.actions.act_window in Odoo 13. Two occurrences:
sale_order.py:192--action_button_merge()return dictwizard_sale_order_merge.py:130--open_sale()return dict
Please remove the "view_type": "form" lines from both.
Unresolved review feedback: field naming conventions
@hildickethan raised this earlier and it has not been addressed:
merge_with(Many2many onsale.order) -- OCA convention is to suffix relational fields: considermerge_with_idsormergeable_order_idsto_merge(Many2many on wizard) -- considerto_merge_idsmergeable(Many2many on wizard) -- considermergeable_ids
The order_id M2O already follows the convention correctly.
_compute_merge_with performs a search per record
The compute method runs self.search([("merge_with", "=", sale.id)]) inside a for sale in self loop. For list views with many orders this could be expensive. Since the field is non-stored, consider whether merge_ok alone is sufficient to gate the UI button and the full merge_with set can be resolved lazily only when the merge button is clicked.
_get_orders_selected uses _read_group as a per-record iterator
_read_group with groupby=["id"] produces one group per record, which is equivalent to iterating self. A simpler direct iteration (for order in self: ...) would improve readability and avoid relying on the id pseudo-field's return type from _read_group.
merge_moves -- invoice line reassignment bypasses recomputation
Line 59 writes move_id directly on invoice_line_ids to move them between account.move records. This bypasses the accounting engine's recomputation of tax lines, sequences, and balanced-entry checks. It only applies to draft invoices which limits risk, but verifying the merged invoice renders correctly after this operation (and potentially calling _onchange_invoice_line_ids() or _compute_amount() on the full move) would be safer.
Minor / style
- License header year: The copyright headers carry only 2016 (from the 8.0 original). Consider adding the current year and the migrating author per OCA conventions.
_merge_order_by_statesreadsir.config_parameterviasudo()on every call, and it is called multiple times per flow. Consider caching per-request.- Missing
@api.dependson_compute_merge_with: non-stored computed fields without@api.dependsare recomputed on every access. Adding an explicit empty depends or a docstring comment would help future maintainers understand the intent.
Overall the module is functional and well-tested. The merge message bug and the deprecated view_type keys are the most important items to fix before merge. The naming convention feedback from the earlier review also remains open.
| self.merge_order_lines() | ||
| for order in orders: | ||
| order.with_context(disable_cancel_warning=True).action_cancel() | ||
| order.message_post(body=_("Merged into %(order)s") % {"order": order.name}) |
There was a problem hiding this comment.
Bug: order.name is the name of the order being cancelled, not the merge target. This should be self.order_id.name:
| order.message_post(body=_("Merged into %(order)s") % {"order": order.name}) | |
| order.message_post(body=_("Merged into %(order)s") % {"order": self.order_id.name}) |
| order.message_post(body=_("Merged into %(order)s") % {"order": order.name}) | ||
| self.order_id.message_post( | ||
| body=_("Order(s) %(orders)s merged into this one") | ||
| % {"orders": ",".join(self.to_merge.mapped("name"))} |
There was a problem hiding this comment.
self.to_merge includes the target order (self.order_id), so the message lists the target among "merged into this one". Consider using orders (already defined above as self.to_merge - self.order_id):
| % {"orders": ",".join(self.to_merge.mapped("name"))} | |
| % {"orders": ",".join(orders.mapped("name"))} |
| self.ensure_one() | ||
| return { | ||
| "name": _("Merged sale order"), | ||
| "view_type": "form", |
There was a problem hiding this comment.
view_type was removed from ir.actions.act_window in Odoo 13. Please remove this line.
| ) | ||
| return { | ||
| "name": _("Merge sale orders"), | ||
| "view_type": "form", |
There was a problem hiding this comment.
view_type was removed from ir.actions.act_window in Odoo 13. Please remove this line.
alexey-pelykh
left a comment
There was a problem hiding this comment.
Thank you for migrating this module to 17.0. I have reviewed the code and found several issues that should be addressed.
Summary:
- One bug in the merge message (wrong order name referenced)
- Stale
view_typekeys from old Odoo versions that should be removed for a clean 17.0 migration - Translation files reference old 8.0 model paths that no longer exist
- Potential data integrity concern in picking merge logic
- Performance concern in
_compute_merge_with
See inline comments for details.
| any(order.state in ("sent", "draft") for order in orders) | ||
| and self.order_id.state == "sale" | ||
| ): | ||
| self._confirm_orders_by_order_target(orders) |
There was a problem hiding this comment.
Bug: order.name here refers to the source order being iterated, not the target order. This message is posted on the source order and should say it was merged into the target, so this should be self.order_id.name:
order.message_post(body=_("Merged into %(order)s") % {"order": self.order_id.name})| merge_ids = self.search(domain) | ||
| if len(self) > 1: | ||
| (self - merge_ids)._validate_selected(order) | ||
|
|
There was a problem hiding this comment.
view_type was removed in Odoo 17.0 (it was deprecated since ~14.0). This key is silently ignored but should be removed for a clean migration. Same applies to the open_sale method in the wizard.
| ("company_id", "=", self.company_id.id), | ||
| ("currency_id", "=", self.currency_id.id), | ||
| ("state", "in", self._merge_order_by_states()), | ||
| ] |
There was a problem hiding this comment.
The op and arg parameters are ignored. This means the search method only works correctly for the specific ("merge_with", "=", sale.id) usage, but any other operator (e.g. !=, in) would return wrong results silently. Consider at minimum validating that op == "=" and raising NotImplementedError for unsupported operators, or better yet, handling common operators.
| self.env["ir.config_parameter"] | ||
| .sudo() | ||
| .get_param("sale_order_merge.merge_order_confirm", False) | ||
| ): |
There was a problem hiding this comment.
This compute method performs a self.search(...) call for each record in the recordset, which is an N+1 pattern. For a list view with many orders this could be slow. Consider whether this can be optimized, e.g. by batching the search or restructuring the computation. At minimum, adding a note about the performance implications would be helpful.
| if self._picking_can_merge(picking): | ||
| key = self._get_picking_map_key(picking) | ||
| if key not in pick_map: | ||
| pick_map[key] = self.env["stock.picking"] |
There was a problem hiding this comment.
When merging pickings, move_line_ids (stock.move.line) are reassigned to the target picking, but the corresponding move_ids (stock.move) are not moved. This can leave stock.move records pointing to the source picking, which will then be missing its move lines. The stock.move records should also be reassigned to the target picking before unlinking the source pickings (which doesn't seem to happen either -- source pickings with no moves/move_lines are left dangling).
| name="name" | ||
| >res.config.settings.view.form.inherit.sale_order_merge</field> | ||
| <field name="model">res.config.settings</field> | ||
| <field name="inherit_id" ref="base.res_config_settings_view_form" /> |
There was a problem hiding this comment.
This inherits base.res_config_settings_view_form which places the setting in the General Settings page. Since this is a sale-related setting, it should inherit sale.res_config_settings_view_form and use an appropriate xpath to place it in the Sales settings section. The xpath //setting[@id='no_edit_order'] would need to be adjusted accordingly.
| target = pickings[0] | ||
| if len(pickings) > 1: | ||
| pickings -= target | ||
| pickings.mapped("move_line_ids").write({"picking_id": target.id}) |
There was a problem hiding this comment.
Same as the other action dict: remove the view_type key, which was removed in Odoo 17.0.
@BinhexTeam
This PR was based on [8.0] sale_order_merge, so several changes were made.
The merge criteria are defined as follows:
ping @StefanRijnhart @gurneyalex